1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-11 05:30:37 -05:00

Merge branch 'main' into km/db-signing-keys

This commit is contained in:
Bernd Schoolmann 2025-06-03 12:31:34 +02:00 committed by GitHub
commit 7869b2f8e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
94 changed files with 397 additions and 1384 deletions

View File

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Version>2025.5.2</Version>
<Version>2025.6.0</Version>
<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
@ -69,4 +69,4 @@
</AssemblyAttribute>
</ItemGroup>
</Target>
</Project>
</Project>

View File

@ -287,11 +287,10 @@ public class ProviderService : IProviderService
foreach (var user in users)
{
if (!keyedFilteredUsers.ContainsKey(user.Id))
if (!keyedFilteredUsers.TryGetValue(user.Id, out var providerUser))
{
continue;
}
var providerUser = keyedFilteredUsers[user.Id];
try
{
if (providerUser.Status != ProviderUserStatusType.Accepted || providerUser.ProviderId != providerId)

View File

@ -16,8 +16,8 @@ public class Program
{
var context = e.Properties["SourceContext"].ToString();
if (e.Properties.ContainsKey("RequestPath") &&
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
if (e.Properties.TryGetValue("RequestPath", out var requestPath) &&
!string.IsNullOrWhiteSpace(requestPath?.ToString()) &&
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
{
return false;

View File

@ -370,8 +370,8 @@ public class AccountController : Controller
// for the user identifier.
static bool nameIdIsNotTransient(Claim c) => c.Type == ClaimTypes.NameIdentifier
&& (c.Properties == null
|| !c.Properties.ContainsKey(SamlPropertyKeys.ClaimFormat)
|| c.Properties[SamlPropertyKeys.ClaimFormat] != SamlNameIdFormats.Transient);
|| !c.Properties.TryGetValue(SamlPropertyKeys.ClaimFormat, out var claimFormat)
|| claimFormat != SamlNameIdFormats.Transient);
// Try to determine the unique id of the external user (issued by the provider)
// the most common claim type for that are the sub claim and the NameIdentifier

View File

@ -17,8 +17,8 @@ public class Program
logging.AddSerilog(hostingContext, (e, globalSettings) =>
{
var context = e.Properties["SourceContext"].ToString();
if (e.Properties.ContainsKey("RequestPath") &&
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
if (e.Properties.TryGetValue("RequestPath", out var requestPath) &&
!string.IsNullOrWhiteSpace(requestPath?.ToString()) &&
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
{
return false;

View File

@ -46,9 +46,9 @@ public static class OpenIdConnectOptionsExtensions
// Handle State if we've gotten that back
var decodedState = options.StateDataFormat.Unprotect(state);
if (decodedState != null && decodedState.Items.ContainsKey("scheme"))
if (decodedState != null && decodedState.Items.TryGetValue("scheme", out var stateScheme))
{
return decodedState.Items["scheme"] == scheme;
return stateScheme == scheme;
}
}
catch

View File

@ -12,7 +12,6 @@ using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Extensions;
using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Providers.Services;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Models.OrganizationConnectionConfigs;
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
@ -20,9 +19,6 @@ using Bit.Core.Repositories;
using Bit.Core.SecretsManager.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Bit.Core.Vault.Repositories;
using Microsoft.AspNetCore.Authorization;
@ -45,12 +41,9 @@ public class OrganizationsController : Controller
private readonly IPaymentService _paymentService;
private readonly IApplicationCacheService _applicationCacheService;
private readonly GlobalSettings _globalSettings;
private readonly IReferenceEventService _referenceEventService;
private readonly IUserService _userService;
private readonly IProviderRepository _providerRepository;
private readonly ILogger<OrganizationsController> _logger;
private readonly IAccessControlService _accessControlService;
private readonly ICurrentContext _currentContext;
private readonly ISecretRepository _secretRepository;
private readonly IProjectRepository _projectRepository;
private readonly IServiceAccountRepository _serviceAccountRepository;
@ -73,12 +66,9 @@ public class OrganizationsController : Controller
IPaymentService paymentService,
IApplicationCacheService applicationCacheService,
GlobalSettings globalSettings,
IReferenceEventService referenceEventService,
IUserService userService,
IProviderRepository providerRepository,
ILogger<OrganizationsController> logger,
IAccessControlService accessControlService,
ICurrentContext currentContext,
ISecretRepository secretRepository,
IProjectRepository projectRepository,
IServiceAccountRepository serviceAccountRepository,
@ -100,12 +90,9 @@ public class OrganizationsController : Controller
_paymentService = paymentService;
_applicationCacheService = applicationCacheService;
_globalSettings = globalSettings;
_referenceEventService = referenceEventService;
_userService = userService;
_providerRepository = providerRepository;
_logger = logger;
_accessControlService = accessControlService;
_currentContext = currentContext;
_secretRepository = secretRepository;
_projectRepository = projectRepository;
_serviceAccountRepository = serviceAccountRepository;
@ -272,11 +259,6 @@ public class OrganizationsController : Controller
await _organizationRepository.ReplaceAsync(organization);
await _applicationCacheService.UpsertOrganizationAbilityAsync(organization);
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.OrganizationEditedByAdmin, organization, _currentContext)
{
EventRaisedByUser = _userService.GetUserName(User),
SalesAssistedTrialStarted = model.SalesAssistedTrialStarted,
});
return RedirectToAction("Edit", new { id });
}

View File

@ -39,7 +39,7 @@ public class ReadOnlyEnvIdentityUserStore : ReadOnlyIdentityUserStore
}
}
var userStamp = usersDict.ContainsKey(normalizedEmail) ? usersDict[normalizedEmail] : null;
var userStamp = usersDict.GetValueOrDefault(normalizedEmail);
if (userStamp == null)
{
return Task.FromResult<IdentityUser>(null);

View File

@ -20,8 +20,8 @@ public class Program
logging.AddSerilog(hostingContext, (e, globalSettings) =>
{
var context = e.Properties["SourceContext"].ToString();
if (e.Properties.ContainsKey("RequestPath") &&
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
if (e.Properties.TryGetValue("RequestPath", out var requestPath) &&
!string.IsNullOrWhiteSpace(requestPath?.ToString()) &&
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
{
return false;

View File

@ -29,12 +29,12 @@ public class AccessControlService : IAccessControlService
}
var userRole = GetUserRoleFromClaim();
if (string.IsNullOrEmpty(userRole) || !RolePermissionMapping.RolePermissions.ContainsKey(userRole))
if (string.IsNullOrEmpty(userRole) || !RolePermissionMapping.RolePermissions.TryGetValue(userRole, out var rolePermissions))
{
return false;
}
return RolePermissionMapping.RolePermissions[userRole].Contains(permission);
return rolePermissions.Contains(permission);
}
public string GetUserRole(string userEmail)

View File

@ -25,7 +25,7 @@ public class UpdateTwoFactorAuthenticatorRequestModel : SecretVerificationReques
{
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
}
else if (providers.ContainsKey(TwoFactorProviderType.Authenticator))
else
{
providers.Remove(TwoFactorProviderType.Authenticator);
}
@ -62,7 +62,7 @@ public class UpdateTwoFactorDuoRequestModel : SecretVerificationRequestModel, IV
{
providers = [];
}
else if (providers.ContainsKey(TwoFactorProviderType.Duo))
else
{
providers.Remove(TwoFactorProviderType.Duo);
}
@ -88,7 +88,7 @@ public class UpdateTwoFactorDuoRequestModel : SecretVerificationRequestModel, IV
{
providers = [];
}
else if (providers.ContainsKey(TwoFactorProviderType.OrganizationDuo))
else
{
providers.Remove(TwoFactorProviderType.OrganizationDuo);
}
@ -145,7 +145,7 @@ public class UpdateTwoFactorYubicoOtpRequestModel : SecretVerificationRequestMod
{
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
}
else if (providers.ContainsKey(TwoFactorProviderType.YubiKey))
else
{
providers.Remove(TwoFactorProviderType.YubiKey);
}
@ -228,7 +228,7 @@ public class TwoFactorEmailRequestModel : SecretVerificationRequestModel
{
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
}
else if (providers.ContainsKey(TwoFactorProviderType.Email))
else
{
providers.Remove(TwoFactorProviderType.Email);
}

View File

@ -13,9 +13,9 @@ public class TwoFactorAuthenticatorResponseModel : ResponseModel
ArgumentNullException.ThrowIfNull(user);
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Authenticator);
if (provider?.MetaData?.ContainsKey("Key") ?? false)
if (provider?.MetaData?.TryGetValue("Key", out var keyValue) ?? false)
{
Key = (string)provider.MetaData["Key"];
Key = (string)keyValue;
Enabled = provider.Enabled;
}
else

View File

@ -15,9 +15,9 @@ public class TwoFactorEmailResponseModel : ResponseModel
}
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
if (provider?.MetaData?.ContainsKey("Email") ?? false)
if (provider?.MetaData?.TryGetValue("Email", out var email) ?? false)
{
Email = (string)provider.MetaData["Email"];
Email = (string)email;
Enabled = provider.Enabled;
}
else

View File

@ -19,29 +19,29 @@ public class TwoFactorYubiKeyResponseModel : ResponseModel
{
Enabled = provider.Enabled;
if (provider.MetaData.ContainsKey("Key1"))
if (provider.MetaData.TryGetValue("Key1", out var key1))
{
Key1 = (string)provider.MetaData["Key1"];
Key1 = (string)key1;
}
if (provider.MetaData.ContainsKey("Key2"))
if (provider.MetaData.TryGetValue("Key2", out var key2))
{
Key2 = (string)provider.MetaData["Key2"];
Key2 = (string)key2;
}
if (provider.MetaData.ContainsKey("Key3"))
if (provider.MetaData.TryGetValue("Key3", out var key3))
{
Key3 = (string)provider.MetaData["Key3"];
Key3 = (string)key3;
}
if (provider.MetaData.ContainsKey("Key4"))
if (provider.MetaData.TryGetValue("Key4", out var key4))
{
Key4 = (string)provider.MetaData["Key4"];
Key4 = (string)key4;
}
if (provider.MetaData.ContainsKey("Key5"))
if (provider.MetaData.TryGetValue("Key5", out var key5))
{
Key5 = (string)provider.MetaData["Key5"];
Key5 = (string)key5;
}
if (provider.MetaData.ContainsKey("Nfc"))
if (provider.MetaData.TryGetValue("Nfc", out var nfc))
{
Nfc = (bool)provider.MetaData["Nfc"];
Nfc = (bool)nfc;
}
}
else

View File

@ -6,14 +6,10 @@ using Bit.Api.Utilities;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
using Bit.Core.Billing.Models;
using Bit.Core.Billing.Services;
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Models.Business;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -161,8 +157,6 @@ public class AccountsController(
[HttpPost("cancel")]
public async Task PostCancelAsync(
[FromBody] SubscriptionCancellationRequestModel request,
[FromServices] ICurrentContext currentContext,
[FromServices] IReferenceEventService referenceEventService,
[FromServices] ISubscriberService subscriberService)
{
var user = await userService.GetUserByPrincipalAsync(User);
@ -175,12 +169,6 @@ public class AccountsController(
await subscriberService.CancelSubscription(user,
new OffboardingSurveyResponse { UserId = user.Id, Reason = request.Reason, Feedback = request.Feedback },
user.IsExpired());
await referenceEventService.RaiseEventAsync(new ReferenceEvent(
ReferenceEventType.CancelSubscription,
user,
currentContext)
{ EndOfPeriod = user.IsExpired() });
}
[HttpPost("reinstate-premium")]

View File

@ -20,9 +20,6 @@ using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -44,7 +41,6 @@ public class OrganizationsController(
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
IUpgradeOrganizationPlanCommand upgradeOrganizationPlanCommand,
IAddSecretsManagerSubscriptionCommand addSecretsManagerSubscriptionCommand,
IReferenceEventService referenceEventService,
ISubscriberService subscriberService,
IOrganizationInstallationRepository organizationInstallationRepository,
IPricingClient pricingClient)
@ -246,14 +242,6 @@ public class OrganizationsController(
Feedback = request.Feedback
},
organization.IsExpired());
await referenceEventService.RaiseEventAsync(new ReferenceEvent(
ReferenceEventType.CancelSubscription,
organization,
currentContext)
{
EndOfPeriod = organization.IsExpired()
});
}
[HttpPost("{id:guid}/reinstate")]

View File

@ -5,7 +5,6 @@ using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Identity;
using Bit.Core.Repositories;
using Bit.Core.SecretsManager.AuthorizationRequirements;
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
using Bit.Core.SecretsManager.Entities;
@ -16,9 +15,6 @@ using Bit.Core.SecretsManager.Queries.Interfaces;
using Bit.Core.SecretsManager.Queries.Secrets.Interfaces;
using Bit.Core.SecretsManager.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -30,7 +26,6 @@ public class SecretsController : Controller
private readonly ICurrentContext _currentContext;
private readonly IProjectRepository _projectRepository;
private readonly ISecretRepository _secretRepository;
private readonly IOrganizationRepository _organizationRepository;
private readonly ICreateSecretCommand _createSecretCommand;
private readonly IUpdateSecretCommand _updateSecretCommand;
private readonly IDeleteSecretCommand _deleteSecretCommand;
@ -39,14 +34,12 @@ public class SecretsController : Controller
private readonly ISecretAccessPoliciesUpdatesQuery _secretAccessPoliciesUpdatesQuery;
private readonly IUserService _userService;
private readonly IEventService _eventService;
private readonly IReferenceEventService _referenceEventService;
private readonly IAuthorizationService _authorizationService;
public SecretsController(
ICurrentContext currentContext,
IProjectRepository projectRepository,
ISecretRepository secretRepository,
IOrganizationRepository organizationRepository,
ICreateSecretCommand createSecretCommand,
IUpdateSecretCommand updateSecretCommand,
IDeleteSecretCommand deleteSecretCommand,
@ -55,13 +48,11 @@ public class SecretsController : Controller
ISecretAccessPoliciesUpdatesQuery secretAccessPoliciesUpdatesQuery,
IUserService userService,
IEventService eventService,
IReferenceEventService referenceEventService,
IAuthorizationService authorizationService)
{
_currentContext = currentContext;
_projectRepository = projectRepository;
_secretRepository = secretRepository;
_organizationRepository = organizationRepository;
_createSecretCommand = createSecretCommand;
_updateSecretCommand = updateSecretCommand;
_deleteSecretCommand = deleteSecretCommand;
@ -70,7 +61,6 @@ public class SecretsController : Controller
_secretAccessPoliciesUpdatesQuery = secretAccessPoliciesUpdatesQuery;
_userService = userService;
_eventService = eventService;
_referenceEventService = referenceEventService;
_authorizationService = authorizationService;
}
@ -148,9 +138,6 @@ public class SecretsController : Controller
if (_currentContext.IdentityClientType == IdentityClientType.ServiceAccount)
{
await _eventService.LogServiceAccountSecretEventAsync(userId, secret, EventType.Secret_Retrieved);
var org = await _organizationRepository.GetByIdAsync(secret.OrganizationId);
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.SmServiceAccountAccessedSecret, org, _currentContext));
}
return new SecretResponseModel(secret, access.Read, access.Write);
@ -266,7 +253,7 @@ public class SecretsController : Controller
throw new NotFoundException();
}
await LogSecretsRetrievalAsync(secrets.First().OrganizationId, secrets);
await LogSecretsRetrievalAsync(secrets);
var responses = secrets.Select(s => new BaseSecretResponseModel(s));
return new ListResponseModel<BaseSecretResponseModel>(responses);
@ -303,21 +290,18 @@ public class SecretsController : Controller
if (syncResult.HasChanges)
{
await LogSecretsRetrievalAsync(organizationId, syncResult.Secrets);
await LogSecretsRetrievalAsync(syncResult.Secrets);
}
return new SecretsSyncResponseModel(syncResult.HasChanges, syncResult.Secrets);
}
private async Task LogSecretsRetrievalAsync(Guid organizationId, IEnumerable<Secret> secrets)
private async Task LogSecretsRetrievalAsync(IEnumerable<Secret> secrets)
{
if (_currentContext.IdentityClientType == IdentityClientType.ServiceAccount)
{
var userId = _userService.GetProperUserId(User)!.Value;
var org = await _organizationRepository.GetByIdAsync(organizationId);
await _eventService.LogServiceAccountSecretsEventAsync(userId, secrets, EventType.Secret_Retrieved);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.SmServiceAccountAccessedSecret, org, _currentContext));
}
}
}

View File

@ -5,7 +5,6 @@ using Bit.Api.Tools.Models.Request;
using Bit.Api.Tools.Models.Response;
using Bit.Api.Utilities;
using Bit.Core;
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.Settings;
@ -33,7 +32,6 @@ public class SendsController : Controller
private readonly INonAnonymousSendCommand _nonAnonymousSendCommand;
private readonly ILogger<SendsController> _logger;
private readonly GlobalSettings _globalSettings;
private readonly ICurrentContext _currentContext;
public SendsController(
ISendRepository sendRepository,
@ -43,8 +41,7 @@ public class SendsController : Controller
INonAnonymousSendCommand nonAnonymousSendCommand,
ISendFileStorageService sendFileStorageService,
ILogger<SendsController> logger,
GlobalSettings globalSettings,
ICurrentContext currentContext)
GlobalSettings globalSettings)
{
_sendRepository = sendRepository;
_userService = userService;
@ -54,7 +51,6 @@ public class SendsController : Controller
_sendFileStorageService = sendFileStorageService;
_logger = logger;
_globalSettings = globalSettings;
_currentContext = currentContext;
}
#region Anonymous endpoints

View File

@ -62,9 +62,9 @@ public static class ApiHelpers
}
}
if (eventTypeHandlers.ContainsKey(eventGridEvent.EventType))
if (eventTypeHandlers.TryGetValue(eventGridEvent.EventType, out var eventTypeHandler))
{
await eventTypeHandlers[eventGridEvent.EventType](eventGridEvent);
await eventTypeHandler(eventGridEvent);
}
}

View File

@ -1064,7 +1064,7 @@ public class CiphersController : Controller
[HttpPut("share")]
[HttpPost("share")]
public async Task PutShareMany([FromBody] CipherBulkShareRequestModel model)
public async Task<CipherMiniResponseModel[]> PutShareMany([FromBody] CipherBulkShareRequestModel model)
{
var organizationId = new Guid(model.Ciphers.First().OrganizationId);
if (!await _currentContext.OrganizationUser(organizationId))
@ -1073,38 +1073,40 @@ public class CiphersController : Controller
}
var userId = _userService.GetProperUserId(User).Value;
var ciphers = await _cipherRepository.GetManyByUserIdAsync(userId, withOrganizations: false);
var ciphersDict = ciphers.ToDictionary(c => c.Id);
// Validate the model was encrypted for the posting user
foreach (var cipher in model.Ciphers)
{
if (cipher.EncryptedFor != null)
if (cipher.EncryptedFor.HasValue && cipher.EncryptedFor.Value != userId)
{
if (cipher.EncryptedFor != userId)
{
throw new BadRequestException("Cipher was not encrypted for the current user. Please try again.");
}
throw new BadRequestException("Cipher was not encrypted for the current user. Please try again.");
}
}
var shareCiphers = new List<(Cipher, DateTime?)>();
foreach (var cipher in model.Ciphers)
{
if (!ciphersDict.ContainsKey(cipher.Id.Value))
if (!ciphersDict.TryGetValue(cipher.Id.Value, out var existingCipher))
{
throw new BadRequestException("Trying to move ciphers that you do not own.");
throw new BadRequestException("Trying to share ciphers that you do not own.");
}
var existingCipher = ciphersDict[cipher.Id.Value];
ValidateClientVersionForFido2CredentialSupport(existingCipher);
shareCiphers.Add((cipher.ToCipher(existingCipher), cipher.LastKnownRevisionDate));
shareCiphers.Add(((Cipher)existingCipher, cipher.LastKnownRevisionDate));
}
await _cipherService.ShareManyAsync(shareCiphers, organizationId,
model.CollectionIds.Select(c => new Guid(c)), userId);
var updated = await _cipherService.ShareManyAsync(
shareCiphers,
organizationId,
model.CollectionIds.Select(Guid.Parse),
userId
);
return updated.Select(c => new CipherMiniResponseModel(c, _globalSettings, false)).ToArray();
}
[HttpPost("purge")]
@ -1186,14 +1188,14 @@ public class CiphersController : Controller
var cipher = await GetByIdAsync(id, userId);
var attachments = cipher?.GetAttachments();
if (attachments == null || !attachments.ContainsKey(attachmentId) || attachments[attachmentId].Validated)
if (attachments == null || !attachments.TryGetValue(attachmentId, out var attachment) || attachment.Validated)
{
throw new NotFoundException();
}
return new AttachmentUploadDataResponseModel
{
Url = await _attachmentStorageService.GetAttachmentUploadUrlAsync(cipher, attachments[attachmentId]),
Url = await _attachmentStorageService.GetAttachmentUploadUrlAsync(cipher, attachment),
FileUploadType = _attachmentStorageService.FileUploadType,
};
}
@ -1212,11 +1214,10 @@ public class CiphersController : Controller
var userId = _userService.GetProperUserId(User).Value;
var cipher = await GetByIdAsync(id, userId);
var attachments = cipher?.GetAttachments();
if (attachments == null || !attachments.ContainsKey(attachmentId))
if (attachments == null || !attachments.TryGetValue(attachmentId, out var attachmentData))
{
throw new NotFoundException();
}
var attachmentData = attachments[attachmentId];
await Request.GetFileAsync(async (stream) =>
{
@ -1366,7 +1367,7 @@ public class CiphersController : Controller
var cipher = await _cipherRepository.GetByIdAsync(new Guid(cipherId));
var attachments = cipher?.GetAttachments() ?? new Dictionary<string, CipherAttachment.MetaData>();
if (cipher == null || !attachments.ContainsKey(attachmentId) || attachments[attachmentId].Validated)
if (cipher == null || !attachments.TryGetValue(attachmentId, out var attachment) || attachment.Validated)
{
if (_attachmentStorageService is AzureSendFileStorageService azureFileStorageService)
{
@ -1376,7 +1377,7 @@ public class CiphersController : Controller
return;
}
await _cipherService.ValidateCipherAttachmentFile(cipher, attachments[attachmentId]);
await _cipherService.ValidateCipherAttachmentFile(cipher, attachment);
}
catch (Exception e)
{

View File

@ -113,18 +113,25 @@ public class CipherRequestModel
if (hasAttachments2)
{
foreach (var attachment in attachments.Where(a => Attachments2.ContainsKey(a.Key)))
foreach (var attachment in attachments)
{
var attachment2 = Attachments2[attachment.Key];
if (!Attachments2.TryGetValue(attachment.Key, out var attachment2))
{
continue;
}
attachment.Value.FileName = attachment2.FileName;
attachment.Value.Key = attachment2.Key;
}
}
else if (hasAttachments)
{
foreach (var attachment in attachments.Where(a => Attachments.ContainsKey(a.Key)))
foreach (var attachment in attachments)
{
attachment.Value.FileName = Attachments[attachment.Key];
if (!Attachments.TryGetValue(attachment.Key, out var attachmentForKey))
{
continue;
}
attachment.Value.FileName = attachmentForKey;
attachment.Value.Key = null;
}
}

View File

@ -129,13 +129,13 @@ public class CipherDetailsResponseModel : CipherResponseModel
IDictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphers, string obj = "cipherDetails")
: base(cipher, user, organizationAbilities, globalSettings, obj)
{
if (collectionCiphers?.ContainsKey(cipher.Id) ?? false)
if (collectionCiphers?.TryGetValue(cipher.Id, out var collectionCipher) ?? false)
{
CollectionIds = collectionCiphers[cipher.Id].Select(c => c.CollectionId);
CollectionIds = collectionCipher.Select(c => c.CollectionId);
}
else
{
CollectionIds = new Guid[] { };
CollectionIds = [];
}
}
@ -147,7 +147,7 @@ public class CipherDetailsResponseModel : CipherResponseModel
IEnumerable<CollectionCipher> collectionCiphers, string obj = "cipherDetails")
: base(cipher, user, organizationAbilities, globalSettings, obj)
{
CollectionIds = collectionCiphers?.Select(c => c.CollectionId) ?? new List<Guid>();
CollectionIds = collectionCiphers?.Select(c => c.CollectionId) ?? [];
}
public CipherDetailsResponseModel(
@ -158,7 +158,7 @@ public class CipherDetailsResponseModel : CipherResponseModel
string obj = "cipherDetails")
: base(cipher, user, organizationAbilities, globalSettings, obj)
{
CollectionIds = cipher.CollectionIds ?? new List<Guid>();
CollectionIds = cipher.CollectionIds ?? [];
}
public IEnumerable<Guid> CollectionIds { get; set; }
@ -170,13 +170,13 @@ public class CipherMiniDetailsResponseModel : CipherMiniResponseModel
IDictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphers, bool orgUseTotp, string obj = "cipherMiniDetails")
: base(cipher, globalSettings, orgUseTotp, obj)
{
if (collectionCiphers?.ContainsKey(cipher.Id) ?? false)
if (collectionCiphers?.TryGetValue(cipher.Id, out var collectionCipher) ?? false)
{
CollectionIds = collectionCiphers[cipher.Id].Select(c => c.CollectionId);
CollectionIds = collectionCipher.Select(c => c.CollectionId);
}
else
{
CollectionIds = new Guid[] { };
CollectionIds = [];
}
}
@ -184,7 +184,7 @@ public class CipherMiniDetailsResponseModel : CipherMiniResponseModel
GlobalSettings globalSettings, bool orgUseTotp, string obj = "cipherMiniDetails")
: base(cipher, globalSettings, orgUseTotp, obj)
{
CollectionIds = cipher.CollectionIds ?? new List<Guid>();
CollectionIds = cipher.CollectionIds ?? [];
}
public CipherMiniDetailsResponseModel(CipherOrganizationDetailsWithCollections cipher,

View File

@ -28,8 +28,8 @@ public class AppleController : Controller
return new BadRequestResult();
}
var key = HttpContext.Request.Query.ContainsKey("key") ?
HttpContext.Request.Query["key"].ToString() : null;
var key = HttpContext.Request.Query.TryGetValue("key", out var keyValue) ?
keyValue.ToString() : null;
if (!CoreHelpers.FixedTimeEquals(key, _billingSettings.AppleWebhookKey))
{
return new BadRequestResult();

View File

@ -51,8 +51,8 @@ public class PayPalController : Controller
[HttpPost("ipn")]
public async Task<IActionResult> PostIpn()
{
var key = HttpContext.Request.Query.ContainsKey("key")
? HttpContext.Request.Query["key"].ToString()
var key = HttpContext.Request.Query.TryGetValue("key", out var keyValue)
? keyValue.ToString()
: null;
if (string.IsNullOrEmpty(key))

View File

@ -20,8 +20,8 @@ public class Program
return e.Level >= globalSettings.MinLogLevel.BillingSettings.Jobs;
}
if (e.Properties.ContainsKey("RequestPath") &&
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
if (e.Properties.TryGetValue("RequestPath", out var requestPath) &&
!string.IsNullOrWhiteSpace(requestPath?.ToString()) &&
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
{
return false;

View File

@ -1,8 +1,4 @@
using Bit.Core.Context;
using Bit.Core.Repositories;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Repositories;
using Event = Stripe.Event;
namespace Bit.Billing.Services.Implementations;
@ -10,23 +6,17 @@ namespace Bit.Billing.Services.Implementations;
public class CustomerUpdatedHandler : ICustomerUpdatedHandler
{
private readonly IOrganizationRepository _organizationRepository;
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
private readonly IStripeEventService _stripeEventService;
private readonly IStripeEventUtilityService _stripeEventUtilityService;
private readonly ILogger<CustomerUpdatedHandler> _logger;
public CustomerUpdatedHandler(
IOrganizationRepository organizationRepository,
IReferenceEventService referenceEventService,
ICurrentContext currentContext,
IStripeEventService stripeEventService,
IStripeEventUtilityService stripeEventUtilityService,
ILogger<CustomerUpdatedHandler> logger)
{
_organizationRepository = organizationRepository ?? throw new ArgumentNullException(nameof(organizationRepository));
_referenceEventService = referenceEventService;
_currentContext = currentContext;
_stripeEventService = stripeEventService;
_stripeEventUtilityService = stripeEventUtilityService;
_logger = logger;
@ -95,20 +85,5 @@ public class CustomerUpdatedHandler : ICustomerUpdatedHandler
organization.BillingEmail = customer.Email;
await _organizationRepository.ReplaceAsync(organization);
if (_referenceEventService == null)
{
_logger.LogError("ReferenceEventService was not initialized in CustomerUpdatedHandler");
throw new InvalidOperationException($"{nameof(_referenceEventService)} is not initialized");
}
if (_currentContext == null)
{
_logger.LogError("CurrentContext was not initialized in CustomerUpdatedHandler");
throw new InvalidOperationException($"{nameof(_currentContext)} is not initialized");
}
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.OrganizationEditedInStripe, organization, _currentContext));
}
}

View File

@ -3,13 +3,9 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Pricing;
using Bit.Core.Context;
using Bit.Core.Platform.Push;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Event = Stripe.Event;
namespace Bit.Billing.Services.Implementations;
@ -22,9 +18,6 @@ public class PaymentSucceededHandler : IPaymentSucceededHandler
private readonly IStripeFacade _stripeFacade;
private readonly IProviderRepository _providerRepository;
private readonly IOrganizationRepository _organizationRepository;
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
private readonly IUserRepository _userRepository;
private readonly IStripeEventUtilityService _stripeEventUtilityService;
private readonly IPushNotificationService _pushNotificationService;
private readonly IOrganizationEnableCommand _organizationEnableCommand;
@ -36,9 +29,6 @@ public class PaymentSucceededHandler : IPaymentSucceededHandler
IStripeFacade stripeFacade,
IProviderRepository providerRepository,
IOrganizationRepository organizationRepository,
IReferenceEventService referenceEventService,
ICurrentContext currentContext,
IUserRepository userRepository,
IStripeEventUtilityService stripeEventUtilityService,
IUserService userService,
IPushNotificationService pushNotificationService,
@ -50,9 +40,6 @@ public class PaymentSucceededHandler : IPaymentSucceededHandler
_stripeFacade = stripeFacade;
_providerRepository = providerRepository;
_organizationRepository = organizationRepository;
_referenceEventService = referenceEventService;
_currentContext = currentContext;
_userRepository = userRepository;
_stripeEventUtilityService = stripeEventUtilityService;
_userService = userService;
_pushNotificationService = pushNotificationService;
@ -116,27 +103,7 @@ public class PaymentSucceededHandler : IPaymentSucceededHandler
_logger.LogError("invoice.payment_succeeded webhook ({EventID}) for Provider ({ProviderID}) indicates missing subscription line items",
parsedEvent.Id,
provider.Id);
return;
}
await _referenceEventService.RaiseEventAsync(new ReferenceEvent
{
Type = ReferenceEventType.Rebilled,
Source = ReferenceEventSource.Provider,
Id = provider.Id,
PlanType = PlanType.TeamsMonthly,
Seats = (int)teamsMonthlyLineItem.Quantity
});
await _referenceEventService.RaiseEventAsync(new ReferenceEvent
{
Type = ReferenceEventType.Rebilled,
Source = ReferenceEventSource.Provider,
Id = provider.Id,
PlanType = PlanType.EnterpriseMonthly,
Seats = (int)enterpriseMonthlyLineItem.Quantity
});
}
else if (organizationId.HasValue)
{
@ -156,15 +123,6 @@ public class PaymentSucceededHandler : IPaymentSucceededHandler
await _organizationEnableCommand.EnableAsync(organizationId.Value, subscription.CurrentPeriodEnd);
await _pushNotificationService.PushSyncOrganizationStatusAsync(organization);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.Rebilled, organization, _currentContext)
{
PlanName = organization?.Plan,
PlanType = organization?.PlanType,
Seats = organization?.Seats,
Storage = organization?.MaxStorageGb,
});
}
else if (userId.HasValue)
{
@ -174,14 +132,6 @@ public class PaymentSucceededHandler : IPaymentSucceededHandler
}
await _userService.EnablePremiumAsync(userId.Value, subscription.CurrentPeriodEnd);
var user = await _userRepository.GetByIdAsync(userId.Value);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.Rebilled, user, _currentContext)
{
PlanName = IStripeEventUtilityService.PremiumPlanId,
Storage = user?.MaxStorageGb,
});
}
}
}

View File

@ -8,14 +8,13 @@ using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Business;
using Bit.Core.Services;
using Bit.Core.Tools.Entities;
using Bit.Core.Utilities;
#nullable enable
namespace Bit.Core.AdminConsole.Entities;
public class Organization : ITableObject<Guid>, IStorableSubscriber, IRevisable, IReferenceable
public class Organization : ITableObject<Guid>, IStorableSubscriber, IRevisable
{
private Dictionary<TwoFactorProviderType, TwoFactorProvider>? _twoFactorProviders;
@ -258,12 +257,12 @@ public class Organization : ITableObject<Guid>, IStorableSubscriber, IRevisable,
public bool TwoFactorProviderIsEnabled(TwoFactorProviderType provider)
{
var providers = GetTwoFactorProviders();
if (providers == null || !providers.ContainsKey(provider))
if (providers == null || !providers.TryGetValue(provider, out var twoFactorProvider))
{
return false;
}
return providers[provider].Enabled && Use2fa;
return twoFactorProvider.Enabled && Use2fa;
}
public bool TwoFactorIsEnabled()
@ -280,12 +279,7 @@ public class Organization : ITableObject<Guid>, IStorableSubscriber, IRevisable,
public TwoFactorProvider? GetTwoFactorProvider(TwoFactorProviderType provider)
{
var providers = GetTwoFactorProviders();
if (providers == null || !providers.ContainsKey(provider))
{
return null;
}
return providers[provider];
return providers?.GetValueOrDefault(provider);
}
public void UpdateFromLicense(OrganizationLicense license, IFeatureService featureService)

View File

@ -1,15 +1,11 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
namespace Bit.Core.AdminConsole.OrganizationFeatures.Groups;
@ -18,21 +14,16 @@ public class CreateGroupCommand : ICreateGroupCommand
private readonly IEventService _eventService;
private readonly IGroupRepository _groupRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
public CreateGroupCommand(
IEventService eventService,
IGroupRepository groupRepository,
IOrganizationUserRepository organizationUserRepository,
IReferenceEventService referenceEventService,
ICurrentContext currentContext)
IOrganizationUserRepository organizationUserRepository
)
{
_eventService = eventService;
_groupRepository = groupRepository;
_organizationUserRepository = organizationUserRepository;
_referenceEventService = referenceEventService;
_currentContext = currentContext;
}
public async Task CreateGroupAsync(Group group, Organization organization,
@ -77,8 +68,6 @@ public class CreateGroupCommand : ICreateGroupCommand
{
await _groupRepository.CreateAsync(group, collections);
}
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.GroupCreated, organization, _currentContext));
}
private async Task GroupRepositoryUpdateUsersAsync(Group group, IEnumerable<Guid> userIds,

View File

@ -7,9 +7,6 @@ using Bit.Core.Exceptions;
using Bit.Core.Platform.Push;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
#nullable enable
@ -24,7 +21,6 @@ public class DeleteClaimedOrganizationUserAccountCommand : IDeleteClaimedOrganiz
private readonly IUserRepository _userRepository;
private readonly ICurrentContext _currentContext;
private readonly IHasConfirmedOwnersExceptQuery _hasConfirmedOwnersExceptQuery;
private readonly IReferenceEventService _referenceEventService;
private readonly IPushNotificationService _pushService;
private readonly IOrganizationRepository _organizationRepository;
private readonly IProviderUserRepository _providerUserRepository;
@ -36,7 +32,6 @@ public class DeleteClaimedOrganizationUserAccountCommand : IDeleteClaimedOrganiz
IUserRepository userRepository,
ICurrentContext currentContext,
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery,
IReferenceEventService referenceEventService,
IPushNotificationService pushService,
IOrganizationRepository organizationRepository,
IProviderUserRepository providerUserRepository)
@ -48,7 +43,6 @@ public class DeleteClaimedOrganizationUserAccountCommand : IDeleteClaimedOrganiz
_userRepository = userRepository;
_currentContext = currentContext;
_hasConfirmedOwnersExceptQuery = hasConfirmedOwnersExceptQuery;
_referenceEventService = referenceEventService;
_pushService = pushService;
_organizationRepository = organizationRepository;
_providerUserRepository = providerUserRepository;
@ -195,8 +189,6 @@ public class DeleteClaimedOrganizationUserAccountCommand : IDeleteClaimedOrganiz
await _userRepository.DeleteManyAsync(users);
foreach (var user in users)
{
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.DeleteAccount, user, _currentContext));
await _pushService.PushLogOutAsync(user.Id);
}

View File

@ -9,15 +9,11 @@ using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Utilities.Commands;
using Bit.Core.AdminConsole.Utilities.Errors;
using Bit.Core.AdminConsole.Utilities.Validation;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Models.Business;
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Microsoft.Extensions.Logging;
using OrganizationUserInvite = Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.OrganizationUserInvite;
@ -28,8 +24,6 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
IInviteUsersValidator inviteUsersValidator,
IPaymentService paymentService,
IOrganizationRepository organizationRepository,
IReferenceEventService referenceEventService,
ICurrentContext currentContext,
IApplicationCacheService applicationCacheService,
IMailService mailService,
ILogger<InviteOrganizationUsersCommand> logger,
@ -121,8 +115,6 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
await SendAdditionalEmailsAsync(validatedRequest, organization);
await SendInvitesAsync(organizationUserToInviteEntities, organization);
await PublishReferenceEventAsync(validatedRequest, organization);
}
catch (Exception ex)
{
@ -190,14 +182,6 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
}
}
private async Task PublishReferenceEventAsync(Valid<InviteOrganizationUsersValidationRequest> validatedResult,
Organization organization) =>
await referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.InvitedUsers, organization, currentContext)
{
Users = validatedResult.Value.Invites.Length
});
private async Task SendInvitesAsync(IEnumerable<CreateOrganizationUser> users, Organization organization) =>
await sendOrganizationInvitesCommand.SendInvitesAsync(
new SendInvitesRequest(
@ -284,15 +268,6 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
await organizationRepository.ReplaceAsync(organization); // could optimize this with only a property update
await applicationCacheService.UpsertOrganizationAbilityAsync(organization);
await referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.AdjustSeats, organization, currentContext)
{
PlanName = validatedResult.Value.InviteOrganization.Plan.Name,
PlanType = validatedResult.Value.InviteOrganization.Plan.Type,
Seats = validatedResult.Value.PasswordManagerSubscriptionUpdate.UpdatedSeatTotal,
PreviousSeats = validatedResult.Value.PasswordManagerSubscriptionUpdate.Seats
});
}
}
}

View File

@ -5,7 +5,6 @@ using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Models.Sales;
using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Services;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
@ -15,9 +14,6 @@ using Bit.Core.Models.StaticStore;
using Bit.Core.Platform.Push;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
namespace Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
@ -36,8 +32,6 @@ public class CloudOrganizationSignUpCommand(
IOrganizationBillingService organizationBillingService,
IPaymentService paymentService,
IPolicyService policyService,
IReferenceEventService referenceEventService,
ICurrentContext currentContext,
IOrganizationRepository organizationRepository,
IOrganizationApiKeyRepository organizationApiKeyRepository,
IApplicationCacheService applicationCacheService,
@ -132,17 +126,6 @@ public class CloudOrganizationSignUpCommand(
var ownerId = signup.IsFromProvider ? default : signup.Owner.Id;
var returnValue = await SignUpAsync(organization, ownerId, signup.OwnerKey, signup.CollectionName, true);
await referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.Signup, organization, currentContext)
{
PlanName = plan.Name,
PlanType = plan.Type,
Seats = returnValue.Item1.Seats,
SignupInitiationPath = signup.InitiationPath,
Storage = returnValue.Item1.MaxStorageGb,
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
});
return new SignUpOrganizationResponse(returnValue.organization, returnValue.organizationUser);
}

View File

@ -2,38 +2,28 @@
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Repositories;
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
namespace Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
public class OrganizationDeleteCommand : IOrganizationDeleteCommand
{
private readonly IApplicationCacheService _applicationCacheService;
private readonly ICurrentContext _currentContext;
private readonly IOrganizationRepository _organizationRepository;
private readonly IPaymentService _paymentService;
private readonly IReferenceEventService _referenceEventService;
private readonly ISsoConfigRepository _ssoConfigRepository;
public OrganizationDeleteCommand(
IApplicationCacheService applicationCacheService,
ICurrentContext currentContext,
IOrganizationRepository organizationRepository,
IPaymentService paymentService,
IReferenceEventService referenceEventService,
ISsoConfigRepository ssoConfigRepository)
{
_applicationCacheService = applicationCacheService;
_currentContext = currentContext;
_organizationRepository = organizationRepository;
_paymentService = paymentService;
_referenceEventService = referenceEventService;
_ssoConfigRepository = ssoConfigRepository;
}
@ -48,8 +38,6 @@ public class OrganizationDeleteCommand : IOrganizationDeleteCommand
var eop = !organization.ExpirationDate.HasValue ||
organization.ExpirationDate.Value >= DateTime.UtcNow;
await _paymentService.CancelSubscriptionAsync(organization, eop);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.DeleteAccount, organization, _currentContext));
}
catch (GatewayException) { }
}

View File

@ -8,9 +8,6 @@ using Bit.Core.Models.Business;
using Bit.Core.Models.StaticStore;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
namespace Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
@ -37,7 +34,6 @@ public class ProviderClientOrganizationSignUpCommand : IProviderClientOrganizati
private readonly ICurrentContext _currentContext;
private readonly IPricingClient _pricingClient;
private readonly IReferenceEventService _referenceEventService;
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
private readonly IApplicationCacheService _applicationCacheService;
@ -46,7 +42,6 @@ public class ProviderClientOrganizationSignUpCommand : IProviderClientOrganizati
public ProviderClientOrganizationSignUpCommand(
ICurrentContext currentContext,
IPricingClient pricingClient,
IReferenceEventService referenceEventService,
IOrganizationRepository organizationRepository,
IOrganizationApiKeyRepository organizationApiKeyRepository,
IApplicationCacheService applicationCacheService,
@ -54,7 +49,6 @@ public class ProviderClientOrganizationSignUpCommand : IProviderClientOrganizati
{
_currentContext = currentContext;
_pricingClient = pricingClient;
_referenceEventService = referenceEventService;
_organizationRepository = organizationRepository;
_organizationApiKeyRepository = organizationApiKeyRepository;
_applicationCacheService = applicationCacheService;
@ -108,16 +102,6 @@ public class ProviderClientOrganizationSignUpCommand : IProviderClientOrganizati
var returnValue = await SignUpAsync(organization, signup.CollectionName);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.Signup, organization, _currentContext)
{
PlanName = plan.Name,
PlanType = plan.Type,
Seats = returnValue.Organization.Seats,
SignupInitiationPath = signup.InitiationPath,
Storage = returnValue.Organization.MaxStorageGb,
});
return returnValue;
}

View File

@ -104,8 +104,8 @@ public class SavePolicyCommand : ISavePolicyCommand
var dependentPolicyTypes = _policyValidators.Values
.Where(otherValidator => otherValidator.RequiredPolicies.Contains(policyUpdate.Type))
.Select(otherValidator => otherValidator.Type)
.Where(otherPolicyType => savedPoliciesDict.ContainsKey(otherPolicyType) &&
savedPoliciesDict[otherPolicyType].Enabled)
.Where(otherPolicyType => savedPoliciesDict.TryGetValue(otherPolicyType, out var savedPolicy) &&
savedPolicy.Enabled)
.ToList();
switch (dependentPolicyTypes)

View File

@ -462,13 +462,13 @@ public class EventService : IEventService
private bool CanUseEvents(IDictionary<Guid, OrganizationAbility> orgAbilities, Guid orgId)
{
return orgAbilities != null && orgAbilities.ContainsKey(orgId) &&
orgAbilities[orgId].Enabled && orgAbilities[orgId].UseEvents;
return orgAbilities != null && orgAbilities.TryGetValue(orgId, out var orgAbility) &&
orgAbility.Enabled && orgAbility.UseEvents;
}
private bool CanUseProviderEvents(IDictionary<Guid, ProviderAbility> providerAbilities, Guid providerId)
{
return providerAbilities != null && providerAbilities.ContainsKey(providerId) &&
providerAbilities[providerId].Enabled && providerAbilities[providerId].UseEvents;
return providerAbilities != null && providerAbilities.TryGetValue(providerId, out var providerAbility) &&
providerAbility.Enabled && providerAbility.UseEvents;
}
}

View File

@ -29,9 +29,6 @@ using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
using Bit.Core.Platform.Push;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Microsoft.Extensions.Logging;
using Stripe;
@ -56,7 +53,6 @@ public class OrganizationService : IOrganizationService
private readonly IPolicyRepository _policyRepository;
private readonly IPolicyService _policyService;
private readonly ISsoUserRepository _ssoUserRepository;
private readonly IReferenceEventService _referenceEventService;
private readonly IGlobalSettings _globalSettings;
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
private readonly ICurrentContext _currentContext;
@ -88,7 +84,6 @@ public class OrganizationService : IOrganizationService
IPolicyRepository policyRepository,
IPolicyService policyService,
ISsoUserRepository ssoUserRepository,
IReferenceEventService referenceEventService,
IGlobalSettings globalSettings,
IOrganizationApiKeyRepository organizationApiKeyRepository,
ICurrentContext currentContext,
@ -120,7 +115,6 @@ public class OrganizationService : IOrganizationService
_policyRepository = policyRepository;
_policyService = policyService;
_ssoUserRepository = ssoUserRepository;
_referenceEventService = referenceEventService;
_globalSettings = globalSettings;
_organizationApiKeyRepository = organizationApiKeyRepository;
_currentContext = currentContext;
@ -153,11 +147,6 @@ public class OrganizationService : IOrganizationService
}
await _paymentService.CancelSubscriptionAsync(organization, eop);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.CancelSubscription, organization, _currentContext)
{
EndOfPeriod = endOfPeriod,
});
}
public async Task ReinstateSubscriptionAsync(Guid organizationId)
@ -169,8 +158,6 @@ public class OrganizationService : IOrganizationService
}
await _paymentService.ReinstateSubscriptionAsync(organization);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.ReinstateSubscription, organization, _currentContext));
}
public async Task<string> AdjustStorageAsync(Guid organizationId, short storageAdjustmentGb)
@ -190,13 +177,6 @@ public class OrganizationService : IOrganizationService
var secret = await BillingHelpers.AdjustStorageAsync(_paymentService, organization, storageAdjustmentGb,
plan.PasswordManager.StripeStoragePlanId);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.AdjustStorage, organization, _currentContext)
{
PlanName = plan.Name,
PlanType = plan.Type,
Storage = storageAdjustmentGb,
});
await ReplaceAndUpdateCacheAsync(organization);
return secret;
}
@ -328,14 +308,6 @@ public class OrganizationService : IOrganizationService
}
var paymentIntentClientSecret = await _paymentService.AdjustSeatsAsync(organization, plan, additionalSeats);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.AdjustSeats, organization, _currentContext)
{
PlanName = plan.Name,
PlanType = plan.Type,
Seats = newSeatTotal,
PreviousSeats = organization.Seats
});
organization.Seats = (short?)newSeatTotal;
await ReplaceAndUpdateCacheAsync(organization);
@ -640,12 +612,12 @@ public class OrganizationService : IOrganizationService
}
var providers = organization.GetTwoFactorProviders();
if (!providers?.ContainsKey(type) ?? true)
if (providers is null || !providers.TryGetValue(type, out var provider))
{
return;
}
providers[type].Enabled = true;
provider.Enabled = true;
organization.SetTwoFactorProviders(providers);
await UpdateAsync(organization);
}
@ -886,12 +858,6 @@ public class OrganizationService : IOrganizationService
}
await SendInvitesAsync(allOrgUsers, organization);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.InvitedUsers, organization, _currentContext)
{
Users = orgUserInvitedCount
});
}
catch (Exception e)
{
@ -1149,7 +1115,7 @@ public class OrganizationService : IOrganizationService
var existingUsersDict = existingExternalUsers.ToDictionary(u => u.ExternalId);
var removeUsersSet = new HashSet<string>(removeUserExternalIds)
.Except(newUsersSet)
.Where(u => existingUsersDict.ContainsKey(u) && existingUsersDict[u].Type != OrganizationUserType.Owner)
.Where(u => existingUsersDict.TryGetValue(u, out var existingUser) && existingUser.Type != OrganizationUserType.Owner)
.Select(u => existingUsersDict[u]);
await _organizationUserRepository.DeleteManyAsync(removeUsersSet.Select(u => u.Id));
@ -1317,8 +1283,6 @@ public class OrganizationService : IOrganizationService
}
await _eventService.LogOrganizationUserEventsAsync(events.Select(e => (e.ou, e.e, eventSystemUser, e.d)));
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.DirectorySynced, organization, _currentContext));
}
public async Task DeleteSsoUserAsync(Guid userId, Guid? organizationId)
@ -1754,11 +1718,5 @@ public class OrganizationService : IOrganizationService
await SendInviteAsync(ownerOrganizationUser, organization, true);
await _eventService.LogOrganizationUserEventAsync(ownerOrganizationUser, EventType.OrganizationUser_Invited);
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.OrganizationCreatedByAdmin, organization, _currentContext)
{
EventRaisedByUser = userService.GetUserName(user),
SalesAssistedTrialStarted = salesAssistedTrialStarted,
});
}
}

View File

@ -68,7 +68,7 @@ public class PolicyService : IPolicyService
var excludedUserTypes = GetUserTypesExcludedFromPolicy(policyType);
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
return organizationUserPolicyDetails.Where(o =>
(!orgAbilities.ContainsKey(o.OrganizationId) || orgAbilities[o.OrganizationId].UsePolicies) &&
(!orgAbilities.TryGetValue(o.OrganizationId, out var orgAbility) || orgAbility.UsePolicies) &&
o.PolicyEnabled &&
!excludedUserTypes.Contains(o.OrganizationUserType) &&
o.OrganizationUserStatus >= minStatus &&

View File

@ -43,7 +43,7 @@ public class EmailTwoFactorTokenProvider : EmailTokenProvider
private static bool HasProperMetaData(TwoFactorProvider provider)
{
return provider?.MetaData != null && provider.MetaData.ContainsKey("Email") &&
!string.IsNullOrWhiteSpace((string)provider.MetaData["Email"]);
return provider?.MetaData != null && provider.MetaData.TryGetValue("Email", out var emailValue) &&
!string.IsNullOrWhiteSpace((string)emailValue);
}
}

View File

@ -80,7 +80,7 @@ public class WebAuthnTokenProvider : IUserTwoFactorTokenProvider<User>
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
var keys = LoadKeys(provider);
if (!provider.MetaData.TryGetValue("login", out var value))
if (!provider.MetaData.TryGetValue("login", out var login))
{
return false;
}
@ -88,7 +88,7 @@ public class WebAuthnTokenProvider : IUserTwoFactorTokenProvider<User>
var clientResponse = JsonSerializer.Deserialize<AuthenticatorAssertionRawResponse>(token,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
var jsonOptions = value.ToString();
var jsonOptions = login.ToString();
var options = AssertionOptions.FromJson(jsonOptions);
var webAuthCred = keys.Find(k => k.Item2.Descriptor.Id.SequenceEqual(clientResponse.Id));
@ -148,9 +148,9 @@ public class WebAuthnTokenProvider : IUserTwoFactorTokenProvider<User>
for (var i = 1; i <= 5; i++)
{
var keyName = $"Key{i}";
if (provider.MetaData.ContainsKey(keyName))
if (provider.MetaData.TryGetValue(keyName, out var value))
{
var key = new TwoFactorProvider.WebAuthnData((dynamic)provider.MetaData[keyName]);
var key = new TwoFactorProvider.WebAuthnData((dynamic)value);
keys.Add(new Tuple<string, TwoFactorProvider.WebAuthnData>(keyName, key));
}

View File

@ -3,7 +3,6 @@ using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
@ -11,9 +10,6 @@ using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tokens;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
@ -26,15 +22,12 @@ public class RegisterUserCommand : IRegisterUserCommand
private readonly IGlobalSettings _globalSettings;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IPolicyRepository _policyRepository;
private readonly IReferenceEventService _referenceEventService;
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
private readonly IDataProtectorTokenFactory<RegistrationEmailVerificationTokenable> _registrationEmailVerificationTokenDataFactory;
private readonly IDataProtector _organizationServiceDataProtector;
private readonly IDataProtector _providerServiceDataProtector;
private readonly ICurrentContext _currentContext;
private readonly IUserService _userService;
private readonly IMailService _mailService;
@ -48,11 +41,9 @@ public class RegisterUserCommand : IRegisterUserCommand
IGlobalSettings globalSettings,
IOrganizationUserRepository organizationUserRepository,
IPolicyRepository policyRepository,
IReferenceEventService referenceEventService,
IDataProtectionProvider dataProtectionProvider,
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
IDataProtectorTokenFactory<RegistrationEmailVerificationTokenable> registrationEmailVerificationTokenDataFactory,
ICurrentContext currentContext,
IUserService userService,
IMailService mailService,
IValidateRedemptionTokenCommand validateRedemptionTokenCommand,
@ -62,14 +53,12 @@ public class RegisterUserCommand : IRegisterUserCommand
_globalSettings = globalSettings;
_organizationUserRepository = organizationUserRepository;
_policyRepository = policyRepository;
_referenceEventService = referenceEventService;
_organizationServiceDataProtector = dataProtectionProvider.CreateProtector(
"OrganizationServiceDataProtector");
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
_registrationEmailVerificationTokenDataFactory = registrationEmailVerificationTokenDataFactory;
_currentContext = currentContext;
_userService = userService;
_mailService = mailService;
@ -86,7 +75,6 @@ public class RegisterUserCommand : IRegisterUserCommand
if (result == IdentityResult.Success)
{
await _mailService.SendWelcomeEmailAsync(user);
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.Signup, user, _currentContext));
}
return result;
@ -119,12 +107,6 @@ public class RegisterUserCommand : IRegisterUserCommand
sentWelcomeEmail = true;
if (!string.IsNullOrEmpty(initiationPath))
{
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.Signup, user, _currentContext)
{
SignupInitiationPath = initiationPath
});
return result;
}
}
@ -134,8 +116,6 @@ public class RegisterUserCommand : IRegisterUserCommand
{
await _mailService.SendWelcomeEmailAsync(user);
}
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.Signup, user, _currentContext));
}
return result;
@ -263,10 +243,6 @@ public class RegisterUserCommand : IRegisterUserCommand
if (result == IdentityResult.Success)
{
await _mailService.SendWelcomeEmailAsync(user);
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.Signup, user, _currentContext)
{
ReceiveMarketingEmails = tokenable.ReceiveMarketingEmails
});
}
return result;
@ -285,7 +261,6 @@ public class RegisterUserCommand : IRegisterUserCommand
if (result == IdentityResult.Success)
{
await _mailService.SendWelcomeEmailAsync(user);
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.Signup, user, _currentContext));
}
return result;
@ -306,7 +281,6 @@ public class RegisterUserCommand : IRegisterUserCommand
if (result == IdentityResult.Success)
{
await _mailService.SendWelcomeEmailAsync(user);
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.Signup, user, _currentContext));
}
return result;
@ -325,7 +299,6 @@ public class RegisterUserCommand : IRegisterUserCommand
if (result == IdentityResult.Success)
{
await _mailService.SendWelcomeEmailAsync(user);
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.Signup, user, _currentContext));
}
return result;

View File

@ -23,6 +23,7 @@ public static class Constants
public const string Fido2KeyCipherMinimumVersion = "2023.10.0";
public const string SSHKeyCipherMinimumVersion = "2024.12.0";
public const string DenyLegacyUserMinimumVersion = "2025.6.0";
/// <summary>
/// Used by IdentityServer to identify our own provider.

View File

@ -64,39 +64,39 @@ public class CurrentContext : ICurrentContext
HttpContext = httpContext;
await BuildAsync(httpContext.User, globalSettings);
if (DeviceIdentifier == null && httpContext.Request.Headers.ContainsKey("Device-Identifier"))
if (DeviceIdentifier == null && httpContext.Request.Headers.TryGetValue("Device-Identifier", out var deviceIdentifier))
{
DeviceIdentifier = httpContext.Request.Headers["Device-Identifier"];
DeviceIdentifier = deviceIdentifier;
}
if (httpContext.Request.Headers.ContainsKey("Device-Type") &&
Enum.TryParse(httpContext.Request.Headers["Device-Type"].ToString(), out DeviceType dType))
if (httpContext.Request.Headers.TryGetValue("Device-Type", out var deviceType) &&
Enum.TryParse(deviceType.ToString(), out DeviceType dType))
{
DeviceType = dType;
}
if (!BotScore.HasValue && httpContext.Request.Headers.ContainsKey("X-Cf-Bot-Score") &&
int.TryParse(httpContext.Request.Headers["X-Cf-Bot-Score"], out var parsedBotScore))
if (!BotScore.HasValue && httpContext.Request.Headers.TryGetValue("X-Cf-Bot-Score", out var cfBotScore) &&
int.TryParse(cfBotScore, out var parsedBotScore))
{
BotScore = parsedBotScore;
}
if (httpContext.Request.Headers.ContainsKey("X-Cf-Worked-Proxied"))
if (httpContext.Request.Headers.TryGetValue("X-Cf-Worked-Proxied", out var cfWorkedProxied))
{
CloudflareWorkerProxied = httpContext.Request.Headers["X-Cf-Worked-Proxied"] == "1";
CloudflareWorkerProxied = cfWorkedProxied == "1";
}
if (httpContext.Request.Headers.ContainsKey("X-Cf-Is-Bot"))
if (httpContext.Request.Headers.TryGetValue("X-Cf-Is-Bot", out var cfIsBot))
{
IsBot = httpContext.Request.Headers["X-Cf-Is-Bot"] == "1";
IsBot = cfIsBot == "1";
}
if (httpContext.Request.Headers.ContainsKey("X-Cf-Maybe-Bot"))
if (httpContext.Request.Headers.TryGetValue("X-Cf-Maybe-Bot", out var cfMaybeBot))
{
MaybeBot = httpContext.Request.Headers["X-Cf-Maybe-Bot"] == "1";
MaybeBot = cfMaybeBot == "1";
}
if (httpContext.Request.Headers.ContainsKey("Bitwarden-Client-Version") && Version.TryParse(httpContext.Request.Headers["Bitwarden-Client-Version"], out var cVersion))
if (httpContext.Request.Headers.TryGetValue("Bitwarden-Client-Version", out var bitWardenClientVersion) && Version.TryParse(bitWardenClientVersion, out var cVersion))
{
ClientVersion = cVersion;
}
@ -190,14 +190,14 @@ public class CurrentContext : ICurrentContext
private List<CurrentContextOrganization> GetOrganizations(Dictionary<string, IEnumerable<Claim>> claimsDict, bool orgApi)
{
var accessSecretsManager = claimsDict.ContainsKey(Claims.SecretsManagerAccess)
? claimsDict[Claims.SecretsManagerAccess].ToDictionary(s => s.Value, _ => true)
var accessSecretsManager = claimsDict.TryGetValue(Claims.SecretsManagerAccess, out var secretsManagerAccessClaim)
? secretsManagerAccessClaim.ToDictionary(s => s.Value, _ => true)
: new Dictionary<string, bool>();
var organizations = new List<CurrentContextOrganization>();
if (claimsDict.ContainsKey(Claims.OrganizationOwner))
if (claimsDict.TryGetValue(Claims.OrganizationOwner, out var organizationOwnerClaim))
{
organizations.AddRange(claimsDict[Claims.OrganizationOwner].Select(c =>
organizations.AddRange(organizationOwnerClaim.Select(c =>
new CurrentContextOrganization
{
Id = new Guid(c.Value),
@ -214,9 +214,9 @@ public class CurrentContext : ICurrentContext
});
}
if (claimsDict.ContainsKey(Claims.OrganizationAdmin))
if (claimsDict.TryGetValue(Claims.OrganizationAdmin, out var organizationAdminClaim))
{
organizations.AddRange(claimsDict[Claims.OrganizationAdmin].Select(c =>
organizations.AddRange(organizationAdminClaim.Select(c =>
new CurrentContextOrganization
{
Id = new Guid(c.Value),
@ -225,9 +225,9 @@ public class CurrentContext : ICurrentContext
}));
}
if (claimsDict.ContainsKey(Claims.OrganizationUser))
if (claimsDict.TryGetValue(Claims.OrganizationUser, out var organizationUserClaim))
{
organizations.AddRange(claimsDict[Claims.OrganizationUser].Select(c =>
organizations.AddRange(organizationUserClaim.Select(c =>
new CurrentContextOrganization
{
Id = new Guid(c.Value),
@ -236,9 +236,9 @@ public class CurrentContext : ICurrentContext
}));
}
if (claimsDict.ContainsKey(Claims.OrganizationCustom))
if (claimsDict.TryGetValue(Claims.OrganizationCustom, out var organizationCustomClaim))
{
organizations.AddRange(claimsDict[Claims.OrganizationCustom].Select(c =>
organizations.AddRange(organizationCustomClaim.Select(c =>
new CurrentContextOrganization
{
Id = new Guid(c.Value),
@ -254,9 +254,9 @@ public class CurrentContext : ICurrentContext
private List<CurrentContextProvider> GetProviders(Dictionary<string, IEnumerable<Claim>> claimsDict)
{
var providers = new List<CurrentContextProvider>();
if (claimsDict.ContainsKey(Claims.ProviderAdmin))
if (claimsDict.TryGetValue(Claims.ProviderAdmin, out var providerAdminClaim))
{
providers.AddRange(claimsDict[Claims.ProviderAdmin].Select(c =>
providers.AddRange(providerAdminClaim.Select(c =>
new CurrentContextProvider
{
Id = new Guid(c.Value),
@ -264,9 +264,9 @@ public class CurrentContext : ICurrentContext
}));
}
if (claimsDict.ContainsKey(Claims.ProviderServiceUser))
if (claimsDict.TryGetValue(Claims.ProviderServiceUser, out var providerServiceUserClaim))
{
providers.AddRange(claimsDict[Claims.ProviderServiceUser].Select(c =>
providers.AddRange(providerServiceUserClaim.Select(c =>
new CurrentContextProvider
{
Id = new Guid(c.Value),
@ -504,20 +504,20 @@ public class CurrentContext : ICurrentContext
private string GetClaimValue(Dictionary<string, IEnumerable<Claim>> claims, string type)
{
if (!claims.ContainsKey(type))
if (!claims.TryGetValue(type, out var claim))
{
return null;
}
return claims[type].FirstOrDefault()?.Value;
return claim.FirstOrDefault()?.Value;
}
private Permissions SetOrganizationPermissionsFromClaims(string organizationId, Dictionary<string, IEnumerable<Claim>> claimsDict)
{
bool hasClaim(string claimKey)
{
return claimsDict.ContainsKey(claimKey) ?
claimsDict[claimKey].Any(x => x.Value == organizationId) : false;
return claimsDict.TryGetValue(claimKey, out var claim) ?
claim.Any(x => x.Value == organizationId) : false;
}
return new Permissions

View File

@ -3,7 +3,6 @@ using System.Text.Json;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models;
using Bit.Core.Enums;
using Bit.Core.Tools.Entities;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Identity;
@ -11,7 +10,7 @@ using Microsoft.AspNetCore.Identity;
namespace Bit.Core.Entities;
public class User : ITableObject<Guid>, IStorableSubscriber, IRevisable, ITwoFactorProvidersUser, IReferenceable
public class User : ITableObject<Guid>, IStorableSubscriber, IRevisable, ITwoFactorProvidersUser
{
private Dictionary<TwoFactorProviderType, TwoFactorProvider>? _twoFactorProviders;
@ -212,12 +211,7 @@ public class User : ITableObject<Guid>, IStorableSubscriber, IRevisable, ITwoFac
public TwoFactorProvider? GetTwoFactorProvider(TwoFactorProviderType provider)
{
var providers = GetTwoFactorProviders();
if (providers == null || !providers.TryGetValue(provider, out var value))
{
return null;
}
return value;
return providers?.GetValueOrDefault(provider);
}
public long StorageBytesRemaining()

View File

@ -63,6 +63,6 @@ public class DistributedCacheCookieManager : ICookieManager
private string GetKey(string key, string id) => $"{CacheKeyPrefix}-{key}-{id}";
private string GetId(HttpContext context, string key) =>
context.Request.Cookies.ContainsKey(key) ?
context.Request.Cookies[key] : null;
context.Request.Cookies.TryGetValue(key, out var cookie) ?
cookie : null;
}

View File

@ -8,7 +8,6 @@ using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Models.Sales;
using Bit.Core.Billing.Pricing;
using Bit.Core.Billing.Services;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Business;
@ -16,9 +15,6 @@ using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
using Bit.Core.Repositories;
using Bit.Core.SecretsManager.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
namespace Bit.Core.OrganizationFeatures.OrganizationSubscriptions;
@ -30,9 +26,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
private readonly IPaymentService _paymentService;
private readonly IPolicyRepository _policyRepository;
private readonly ISsoConfigRepository _ssoConfigRepository;
private readonly IReferenceEventService _referenceEventService;
private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
private readonly ICurrentContext _currentContext;
private readonly IServiceAccountRepository _serviceAccountRepository;
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationService _organizationService;
@ -47,9 +41,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
IPaymentService paymentService,
IPolicyRepository policyRepository,
ISsoConfigRepository ssoConfigRepository,
IReferenceEventService referenceEventService,
IOrganizationConnectionRepository organizationConnectionRepository,
ICurrentContext currentContext,
IServiceAccountRepository serviceAccountRepository,
IOrganizationRepository organizationRepository,
IOrganizationService organizationService,
@ -63,9 +55,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
_paymentService = paymentService;
_policyRepository = policyRepository;
_ssoConfigRepository = ssoConfigRepository;
_referenceEventService = referenceEventService;
_organizationConnectionRepository = organizationConnectionRepository;
_currentContext = currentContext;
_serviceAccountRepository = serviceAccountRepository;
_organizationRepository = organizationRepository;
_organizationService = organizationService;
@ -285,25 +275,6 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
}
await _organizationService.ReplaceAndUpdateCacheAsync(organization);
if (success)
{
var upgradePath = GetUpgradePath(existingPlan.ProductTier, newPlan.ProductTier);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.UpgradePlan, organization, _currentContext)
{
PlanName = newPlan.Name,
PlanType = newPlan.Type,
OldPlanName = existingPlan.Name,
OldPlanType = existingPlan.Type,
Seats = organization.Seats,
SignupInitiationPath = "Upgrade in-product",
PlanUpgradePath = upgradePath,
Storage = organization.MaxStorageGb,
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
});
}
return new Tuple<bool, string>(success, paymentIntentClientSecret);
}

View File

@ -1,13 +1,9 @@
#nullable enable
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
namespace Bit.Core.Services;
@ -17,23 +13,17 @@ public class CollectionService : ICollectionService
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly ICollectionRepository _collectionRepository;
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
public CollectionService(
IEventService eventService,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
ICollectionRepository collectionRepository,
IReferenceEventService referenceEventService,
ICurrentContext currentContext)
ICollectionRepository collectionRepository)
{
_eventService = eventService;
_organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository;
_collectionRepository = collectionRepository;
_referenceEventService = referenceEventService;
_currentContext = currentContext;
}
public async Task SaveAsync(Collection collection, IEnumerable<CollectionAccessSelection>? groups = null,
@ -78,7 +68,6 @@ public class CollectionService : ICollectionService
await _collectionRepository.CreateAsync(collection, org.UseGroups ? groupsList : null, usersList);
await _eventService.LogCollectionEventAsync(collection, Enums.EventType.Collection_Created);
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.CollectionCreated, org, _currentContext));
}
else
{

View File

@ -50,14 +50,7 @@ public class InMemoryApplicationCacheService : IApplicationCacheService
await InitProviderAbilitiesAsync();
var newAbility = new ProviderAbility(provider);
if (_providerAbilities.ContainsKey(provider.Id))
{
_providerAbilities[provider.Id] = newAbility;
}
else
{
_providerAbilities.Add(provider.Id, newAbility);
}
_providerAbilities[provider.Id] = newAbility;
}
public virtual async Task UpsertOrganizationAbilityAsync(Organization organization)
@ -65,32 +58,19 @@ public class InMemoryApplicationCacheService : IApplicationCacheService
await InitOrganizationAbilitiesAsync();
var newAbility = new OrganizationAbility(organization);
if (_orgAbilities.ContainsKey(organization.Id))
{
_orgAbilities[organization.Id] = newAbility;
}
else
{
_orgAbilities.Add(organization.Id, newAbility);
}
_orgAbilities[organization.Id] = newAbility;
}
public virtual Task DeleteOrganizationAbilityAsync(Guid organizationId)
{
if (_orgAbilities != null && _orgAbilities.ContainsKey(organizationId))
{
_orgAbilities.Remove(organizationId);
}
_orgAbilities?.Remove(organizationId);
return Task.FromResult(0);
}
public virtual Task DeleteProviderAbilityAsync(Guid providerId)
{
if (_providerAbilities != null && _providerAbilities.ContainsKey(providerId))
{
_providerAbilities.Remove(providerId);
}
_providerAbilities?.Remove(providerId);
return Task.FromResult(0);
}

View File

@ -182,9 +182,8 @@ public class LicensingService : ILicensingService
// Only check once per day
var now = DateTime.UtcNow;
if (_userCheckCache.ContainsKey(user.Id))
if (_userCheckCache.TryGetValue(user.Id, out var lastCheck))
{
var lastCheck = _userCheckCache[user.Id];
if (lastCheck < now && now - lastCheck < TimeSpan.FromDays(1))
{
return user.Premium;

View File

@ -72,8 +72,8 @@ public class SendGridMailDeliveryService : IMailDeliveryService, IDisposable
msg.SetOpenTracking(false);
if (message.MetaData != null &&
message.MetaData.ContainsKey("SendGridBypassListManagement") &&
Convert.ToBoolean(message.MetaData["SendGridBypassListManagement"]))
message.MetaData.TryGetValue("SendGridBypassListManagement", out var sendGridBypassListManagement) &&
Convert.ToBoolean(sendGridBypassListManagement))
{
msg.SetBypassListManagement(true);
}

View File

@ -30,9 +30,6 @@ using Bit.Core.Platform.Push;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Bit.Core.Tokens;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Bit.Core.Vault.Repositories;
using Fido2NetLib;
@ -69,7 +66,6 @@ public class UserService : UserManager<User>, IUserService, IDisposable
private readonly IPolicyRepository _policyRepository;
private readonly IPolicyService _policyService;
private readonly IDataProtector _organizationServiceDataProtector;
private readonly IReferenceEventService _referenceEventService;
private readonly IFido2 _fido2;
private readonly ICurrentContext _currentContext;
private readonly IGlobalSettings _globalSettings;
@ -109,7 +105,6 @@ public class UserService : UserManager<User>, IUserService, IDisposable
IPaymentService paymentService,
IPolicyRepository policyRepository,
IPolicyService policyService,
IReferenceEventService referenceEventService,
IFido2 fido2,
ICurrentContext currentContext,
IGlobalSettings globalSettings,
@ -154,7 +149,6 @@ public class UserService : UserManager<User>, IUserService, IDisposable
_policyService = policyService;
_organizationServiceDataProtector = dataProtectionProvider.CreateProtector(
"OrganizationServiceDataProtector");
_referenceEventService = referenceEventService;
_fido2 = fido2;
_currentContext = currentContext;
_globalSettings = globalSettings;
@ -299,8 +293,6 @@ public class UserService : UserManager<User>, IUserService, IDisposable
}
await _userRepository.DeleteAsync(user);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.DeleteAccount, user, _currentContext));
await _pushService.PushLogOutAsync(user.Id);
return IdentityResult.Success;
}
@ -365,12 +357,12 @@ public class UserService : UserManager<User>, IUserService, IDisposable
public async Task SendTwoFactorEmailAsync(User user, bool authentication = true)
{
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
if (provider == null || provider.MetaData == null || !provider.MetaData.ContainsKey("Email"))
if (provider == null || provider.MetaData == null || !provider.MetaData.TryGetValue("Email", out var emailValue))
{
throw new ArgumentNullException("No email.");
}
var email = ((string)provider.MetaData["Email"]).ToLowerInvariant();
var email = ((string)emailValue).ToLowerInvariant();
var token = await base.GenerateTwoFactorTokenAsync(user,
CoreHelpers.CustomProviderName(TwoFactorProviderType.Email));
@ -398,12 +390,12 @@ public class UserService : UserManager<User>, IUserService, IDisposable
public async Task<bool> VerifyTwoFactorEmailAsync(User user, string token)
{
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
if (provider == null || provider.MetaData == null || !provider.MetaData.ContainsKey("Email"))
if (provider == null || provider.MetaData == null || !provider.MetaData.TryGetValue("Email", out var emailValue))
{
throw new ArgumentNullException("No email.");
}
var email = ((string)provider.MetaData["Email"]).ToLowerInvariant();
var email = ((string)emailValue).ToLowerInvariant();
return await base.VerifyTwoFactorTokenAsync(user,
CoreHelpers.CustomProviderName(TwoFactorProviderType.Email), token);
}
@ -461,12 +453,12 @@ public class UserService : UserManager<User>, IUserService, IDisposable
var keyId = $"Key{id}";
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
if (!provider?.MetaData?.ContainsKey("pending") ?? true)
if (provider?.MetaData is null || !provider.MetaData.TryGetValue("pending", out var pendingValue))
{
return false;
}
var options = CredentialCreateOptions.FromJson((string)provider.MetaData["pending"]);
var options = CredentialCreateOptions.FromJson((string)pendingValue);
// Callback to ensure credential ID is unique. Always return true since we don't care if another
// account uses the same 2FA key.
@ -1046,12 +1038,6 @@ public class UserService : UserManager<User>, IUserService, IDisposable
{
await SaveUserAsync(user);
await _pushService.PushSyncVaultAsync(user.Id);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.UpgradePlan, user, _currentContext)
{
Storage = user.MaxStorageGb,
PlanName = PremiumPlanId,
});
}
catch when (!_globalSettings.SelfHosted)
{
@ -1117,12 +1103,6 @@ public class UserService : UserManager<User>, IUserService, IDisposable
var secret = await BillingHelpers.AdjustStorageAsync(_paymentService, user, storageAdjustmentGb,
StripeConstants.Prices.StoragePlanPersonal);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.AdjustStorage, user, _currentContext)
{
Storage = storageAdjustmentGb,
PlanName = StripeConstants.Prices.StoragePlanPersonal,
});
await SaveUserAsync(user);
return secret;
}
@ -1150,18 +1130,11 @@ public class UserService : UserManager<User>, IUserService, IDisposable
eop = false;
}
await _paymentService.CancelSubscriptionAsync(user, eop);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.CancelSubscription, user, _currentContext)
{
EndOfPeriod = eop
});
}
public async Task ReinstatePremiumAsync(User user)
{
await _paymentService.ReinstateSubscriptionAsync(user);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.ReinstateSubscription, user, _currentContext));
}
public async Task EnablePremiumAsync(Guid userId, DateTime? expirationDate)
@ -1380,14 +1353,14 @@ public class UserService : UserManager<User>, IUserService, IDisposable
public void SetTwoFactorProvider(User user, TwoFactorProviderType type, bool setEnabled = true)
{
var providers = user.GetTwoFactorProviders();
if (!providers?.ContainsKey(type) ?? true)
if (providers is null || !providers.TryGetValue(type, out var provider))
{
return;
}
if (setEnabled)
{
providers[type].Enabled = true;
provider.Enabled = true;
}
user.SetTwoFactorProviders(providers);
@ -1446,17 +1419,6 @@ public class UserService : UserManager<User>, IUserService, IDisposable
await Task.WhenAll(legacyRevokeOrgUserTasks);
}
public override async Task<IdentityResult> ConfirmEmailAsync(User user, string token)
{
var result = await base.ConfirmEmailAsync(user, token);
if (result.Succeeded)
{
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.ConfirmEmailAddress, user, _currentContext));
}
return result;
}
public async Task RotateApiKeyAsync(User user)
{
user.ApiKey = CoreHelpers.SecureRandomString(30);

View File

@ -1,29 +0,0 @@
#nullable enable
using Bit.Core.Tools.Models.Business;
namespace Bit.Core.Tools.Entities;
/// <summary>
/// An entity that can be referenced by a <see cref="ReferenceEvent"/>.
/// </summary>
public interface IReferenceable
{
/// <summary>
/// Identifies the entity that generated the event.
/// </summary>
Guid Id { get; set; }
/// <summary>
/// Contextual information included in the event.
/// </summary>
/// <remarks>
/// Do not store secrets in this field.
/// </remarks>
string? ReferenceData { get; set; }
/// <summary>
/// Returns <see langword="true" /> when the entity is a user.
/// Otherwise returns <see langword="false" />.
/// </summary>
bool IsUser();
}

View File

@ -1,15 +0,0 @@
using System.Runtime.Serialization;
namespace Bit.Core.Tools.Enums;
public enum ReferenceEventSource
{
[EnumMember(Value = "organization")]
Organization,
[EnumMember(Value = "user")]
User,
[EnumMember(Value = "provider")]
Provider,
[EnumMember(Value = "registration")]
Registration,
}

View File

@ -1,53 +0,0 @@
using System.Runtime.Serialization;
namespace Bit.Core.Tools.Enums;
public enum ReferenceEventType
{
[EnumMember(Value = "signup-email-submit")]
SignupEmailSubmit,
[EnumMember(Value = "signup-email-clicked")]
SignupEmailClicked,
[EnumMember(Value = "signup")]
Signup,
[EnumMember(Value = "upgrade-plan")]
UpgradePlan,
[EnumMember(Value = "adjust-storage")]
AdjustStorage,
[EnumMember(Value = "adjust-seats")]
AdjustSeats,
[EnumMember(Value = "cancel-subscription")]
CancelSubscription,
[EnumMember(Value = "reinstate-subscription")]
ReinstateSubscription,
[EnumMember(Value = "delete-account")]
DeleteAccount,
[EnumMember(Value = "confirm-email")]
ConfirmEmailAddress,
[EnumMember(Value = "invited-users")]
InvitedUsers,
[EnumMember(Value = "rebilled")]
Rebilled,
[EnumMember(Value = "send-created")]
SendCreated,
[EnumMember(Value = "send-accessed")]
SendAccessed,
[EnumMember(Value = "directory-synced")]
DirectorySynced,
[EnumMember(Value = "vault-imported")]
VaultImported,
[EnumMember(Value = "cipher-created")]
CipherCreated,
[EnumMember(Value = "group-created")]
GroupCreated,
[EnumMember(Value = "collection-created")]
CollectionCreated,
[EnumMember(Value = "organization-edited-by-admin")]
OrganizationEditedByAdmin,
[EnumMember(Value = "organization-created-by-admin")]
OrganizationCreatedByAdmin,
[EnumMember(Value = "organization-edited-in-stripe")]
OrganizationEditedInStripe,
[EnumMember(Value = "sm-service-account-accessed-secret")]
SmServiceAccountAccessedSecret,
}

View File

@ -2,16 +2,12 @@
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Platform.Push;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.ImportFeatures.Interfaces;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Vault.Entities;
using Bit.Core.Vault.Models.Data;
using Bit.Core.Vault.Repositories;
@ -27,8 +23,6 @@ public class ImportCiphersCommand : IImportCiphersCommand
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly ICollectionRepository _collectionRepository;
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
private readonly IPolicyRequirementQuery _policyRequirementQuery;
private readonly IFeatureService _featureService;
@ -40,8 +34,6 @@ public class ImportCiphersCommand : IImportCiphersCommand
IOrganizationUserRepository organizationUserRepository,
IPushNotificationService pushService,
IPolicyService policyService,
IReferenceEventService referenceEventService,
ICurrentContext currentContext,
IPolicyRequirementQuery policyRequirementQuery,
IFeatureService featureService)
{
@ -52,8 +44,6 @@ public class ImportCiphersCommand : IImportCiphersCommand
_collectionRepository = collectionRepository;
_pushService = pushService;
_policyService = policyService;
_referenceEventService = referenceEventService;
_currentContext = currentContext;
_policyRequirementQuery = policyRequirementQuery;
_featureService = featureService;
}
@ -194,12 +184,5 @@ public class ImportCiphersCommand : IImportCiphersCommand
// push
await _pushService.PushSyncVaultAsync(importingUserId);
if (org != null)
{
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.VaultImported, org, _currentContext));
}
}
}

View File

@ -1,274 +0,0 @@
#nullable enable
using System.Text.Json.Serialization;
using Bit.Core.Billing.Enums;
using Bit.Core.Context;
using Bit.Core.Tools.Entities;
using Bit.Core.Tools.Enums;
namespace Bit.Core.Tools.Models.Business;
/// <summary>
/// Product support monitoring.
/// </summary>
/// <remarks>
/// Do not store secrets in this type.
/// </remarks>
public class ReferenceEvent
{
/// <summary>
/// Instantiates a <see cref="ReferenceEvent"/>.
/// </summary>
public ReferenceEvent() { }
/// <inheritdoc cref="ReferenceEvent()" />
/// <param name="type">Monitored event type.</param>
/// <param name="source">Entity that created the event.</param>
/// <param name="currentContext">The conditions in which the event occurred.</param>
public ReferenceEvent(ReferenceEventType type, IReferenceable source, ICurrentContext currentContext)
{
Type = type;
if (source != null)
{
Source = source.IsUser() ? ReferenceEventSource.User : ReferenceEventSource.Organization;
Id = source.Id;
ReferenceData = source.ReferenceData;
}
if (currentContext != null)
{
ClientId = currentContext.ClientId;
ClientVersion = currentContext.ClientVersion;
}
}
/// <summary>
/// Monitored event type.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public ReferenceEventType Type { get; set; }
/// <summary>
/// The kind of entity that created the event.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public ReferenceEventSource Source { get; set; }
/// <inheritdoc cref="IReferenceable.Id"/>
public Guid Id { get; set; }
/// <inheritdoc cref="IReferenceable.ReferenceData"/>
public string? ReferenceData { get; set; }
/// <summary>
/// Moment the event occurred.
/// </summary>
public DateTime EventDate { get; set; } = DateTime.UtcNow;
/// <summary>
/// Number of users sent invitations by an organization.
/// </summary>
/// <value>
/// Should contain a value only on <see cref="ReferenceEventType.InvitedUsers"/> events.
/// Otherwise the value should be <see langword="null"/>.
/// </value>
public int? Users { get; set; }
/// <summary>
/// Whether or not a subscription was canceled immediately or at the end of the billing period.
/// </summary>
/// <value>
/// <see langword="true"/> when a cancellation occurs immediately.
/// <see langword="false"/> when a cancellation occurs at the end of a customer's billing period.
/// Should contain a value only on <see cref="ReferenceEventType.CancelSubscription"/> events.
/// Otherwise the value should be <see langword="null"/>.
/// </value>
public bool? EndOfPeriod { get; set; }
/// <summary>
/// Branded name of the subscription.
/// </summary>
/// <value>
/// Should contain a value only for subscription management events.
/// Otherwise the value should be <see langword="null"/>.
/// </value>
public string? PlanName { get; set; }
/// <summary>
/// Identifies a subscription.
/// </summary>
/// <value>
/// Should contain a value only for subscription management events.
/// Otherwise the value should be <see langword="null"/>.
/// </value>
public PlanType? PlanType { get; set; }
/// <summary>
/// The branded name of the prior plan.
/// </summary>
/// <value>
/// Should contain a value only on <see cref="ReferenceEventType.UpgradePlan"/> events
/// initiated by organizations.
/// Otherwise the value should be <see langword="null"/>.
/// </value>
public string? OldPlanName { get; set; }
/// <summary>
/// Identifies the prior plan
/// </summary>
/// <value>
/// Should contain a value only on <see cref="ReferenceEventType.UpgradePlan"/> events
/// initiated by organizations.
/// Otherwise the value should be <see langword="null"/>.
/// </value>
public PlanType? OldPlanType { get; set; }
/// <summary>
/// Seat count when a billable action occurs. When adjusting seats, contains
/// the new seat count.
/// </summary>
/// <value>
/// Should contain a value only on <see cref="ReferenceEventType.Rebilled"/>,
/// <see cref="ReferenceEventType.AdjustSeats"/>, <see cref="ReferenceEventType.UpgradePlan"/>,
/// and <see cref="ReferenceEventType.Signup"/> events initiated by organizations.
/// Otherwise the value should be <see langword="null"/>.
/// </value>
public int? Seats { get; set; }
/// <summary>
/// Seat count when a seat adjustment occurs.
/// </summary>
/// <value>
/// Should contain a value only on <see cref="ReferenceEventType.AdjustSeats"/>
/// events initiated by organizations.
/// Otherwise the value should be <see langword="null"/>.
/// </value>
public int? PreviousSeats { get; set; }
/// <summary>
/// Qty in GB of storage. When adjusting storage, contains the adjusted
/// storage qty. Otherwise contains the total storage quantity.
/// </summary>
/// <value>
/// Should contain a value only on <see cref="ReferenceEventType.Rebilled"/>,
/// <see cref="ReferenceEventType.AdjustStorage"/>, <see cref="ReferenceEventType.UpgradePlan"/>,
/// and <see cref="ReferenceEventType.Signup"/> events.
/// Otherwise the value should be <see langword="null"/>.
/// </value>
public short? Storage { get; set; }
/// <summary>
/// The type of send created or accessed.
/// </summary>
/// <value>
/// Should contain a value only on <see cref="ReferenceEventType.SendAccessed"/>
/// and <see cref="ReferenceEventType.SendCreated"/> events.
/// Otherwise the value should be <see langword="null"/>.
/// </value>
[JsonConverter(typeof(JsonStringEnumConverter))]
public SendType? SendType { get; set; }
/// <summary>
/// Whether the send has private notes.
/// </summary>
/// <value>
/// <see langword="true"/> when the send has private notes, otherwise <see langword="false"/>.
/// Should contain a value only on <see cref="ReferenceEventType.SendAccessed"/>
/// and <see cref="ReferenceEventType.SendCreated"/> events.
/// Otherwise the value should be <see langword="null"/>.
/// </value>
public bool? SendHasNotes { get; set; }
/// <summary>
/// The send expires after its access count exceeds this value.
/// </summary>
/// <value>
/// This field only contains a value when the send has a max access count
/// and <see cref="Type"/> is <see cref="ReferenceEventType.SendAccessed"/>
/// or <see cref="ReferenceEventType.SendCreated"/> events.
/// Otherwise, the value should be <see langword="null"/>.
/// </value>
public int? MaxAccessCount { get; set; }
/// <summary>
/// Whether the created send has a password.
/// </summary>
/// <value>
/// Should contain a value only on <see cref="ReferenceEventType.SendAccessed"/>
/// and <see cref="ReferenceEventType.SendCreated"/> events.
/// Otherwise the value should be <see langword="null"/>.
/// </value>
public bool? HasPassword { get; set; }
/// <summary>
/// The administrator that performed the action.
/// </summary>
/// <value>
/// Should contain a value only on <see cref="ReferenceEventType.OrganizationCreatedByAdmin"/>
/// and <see cref="ReferenceEventType.OrganizationEditedByAdmin"/> events.
/// Otherwise the value should be <see langword="null"/>.
/// </value>
public string? EventRaisedByUser { get; set; }
/// <summary>
/// Whether or not an organization's trial period was started by a sales person.
/// </summary>
/// <value>
/// Should contain a value only on <see cref="ReferenceEventType.OrganizationCreatedByAdmin"/>
/// and <see cref="ReferenceEventType.OrganizationEditedByAdmin"/> events.
/// Otherwise the value should be <see langword="null"/>.
/// </value>
public bool? SalesAssistedTrialStarted { get; set; }
/// <summary>
/// The installation id of the application that originated the event.
/// </summary>
/// <value>
/// <see langword="null"/> when the event was not originated by an application.
/// </value>
public string? ClientId { get; set; }
/// <summary>
/// The version of the client application that originated the event.
/// </summary>
/// <value>
/// <see langword="null"/> when the event was not originated by an application.
/// </value>
public Version? ClientVersion { get; set; }
/// <summary>
/// The initiation path of a user who signed up for a paid version of Bitwarden. For example, "Trial from marketing website".
/// </summary>
/// <value>
/// This value should only be populated when the <see cref="ReferenceEventType"/> is <see cref="ReferenceEventType.Signup"/>. Otherwise,
/// the value should be <see langword="null" />.
/// </value>
public string? SignupInitiationPath { get; set; }
/// <summary>
/// The upgrade applied to an account. The current plan is listed first,
/// followed by the plan they are migrating to. For example,
/// "Teams Starter → Teams, Enterprise".
/// </summary>
/// <value>
/// <see langword="null"/> when the event was not originated by an application,
/// or when a downgrade occurred.
/// </value>
public string? PlanUpgradePath { get; set; }
/// <summary>
/// Used for the <see cref="ReferenceEventType.Signup"/> event to determine if the user has opted in to marketing emails.
/// </summary>
public bool? ReceiveMarketingEmails { get; set; }
/// <summary>
/// Used for the <see cref="ReferenceEventType.SignupEmailClicked"/> event to indicate if the user
/// landed on the registration finish screen with a valid or invalid email verification token.
/// </summary>
public bool? EmailVerificationTokenValid { get; set; }
/// <summary>
/// Used for the <see cref="ReferenceEventType.SignupEmailClicked"/> event to indicate if the user
/// landed on the registration finish screen after re-clicking an already used link.
/// </summary>
public bool? UserAlreadyExists { get; set; }
}

View File

@ -1,10 +1,8 @@
using System.Text.Json;
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Platform.Push;
using Bit.Core.Tools.Entities;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Models.Data;
using Bit.Core.Tools.Repositories;
using Bit.Core.Tools.SendFeatures.Commands.Interfaces;
@ -19,8 +17,6 @@ public class NonAnonymousSendCommand : INonAnonymousSendCommand
private readonly ISendFileStorageService _sendFileStorageService;
private readonly IPushNotificationService _pushNotificationService;
private readonly ISendValidationService _sendValidationService;
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
private readonly ISendCoreHelperService _sendCoreHelperService;
public NonAnonymousSendCommand(ISendRepository sendRepository,
@ -28,16 +24,12 @@ public class NonAnonymousSendCommand : INonAnonymousSendCommand
IPushNotificationService pushNotificationService,
ISendAuthorizationService sendAuthorizationService,
ISendValidationService sendValidationService,
IReferenceEventService referenceEventService,
ICurrentContext currentContext,
ISendCoreHelperService sendCoreHelperService)
{
_sendRepository = sendRepository;
_sendFileStorageService = sendFileStorageService;
_pushNotificationService = pushNotificationService;
_sendValidationService = sendValidationService;
_referenceEventService = referenceEventService;
_currentContext = currentContext;
_sendCoreHelperService = sendCoreHelperService;
}
@ -50,18 +42,6 @@ public class NonAnonymousSendCommand : INonAnonymousSendCommand
{
await _sendRepository.CreateAsync(send);
await _pushNotificationService.PushSyncSendCreateAsync(send);
await _referenceEventService.RaiseEventAsync(new ReferenceEvent
{
Id = send.UserId ?? default,
Type = ReferenceEventType.SendCreated,
Source = ReferenceEventSource.User,
SendType = send.Type,
MaxAccessCount = send.MaxAccessCount,
HasPassword = !string.IsNullOrWhiteSpace(send.Password),
SendHasNotes = send.Data?.Contains("Notes"),
ClientId = _currentContext.ClientId,
ClientVersion = _currentContext.ClientVersion
});
}
else
{

View File

@ -1,9 +1,7 @@
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Entities;
using Bit.Core.Platform.Push;
using Bit.Core.Tools.Entities;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Models.Data;
using Bit.Core.Tools.Repositories;
using Microsoft.AspNetCore.Identity;
@ -15,21 +13,15 @@ public class SendAuthorizationService : ISendAuthorizationService
private readonly ISendRepository _sendRepository;
private readonly IPasswordHasher<User> _passwordHasher;
private readonly IPushNotificationService _pushNotificationService;
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
public SendAuthorizationService(
ISendRepository sendRepository,
IPasswordHasher<User> passwordHasher,
IPushNotificationService pushNotificationService,
IReferenceEventService referenceEventService,
ICurrentContext currentContext)
IPushNotificationService pushNotificationService)
{
_sendRepository = sendRepository;
_passwordHasher = passwordHasher;
_pushNotificationService = pushNotificationService;
_referenceEventService = referenceEventService;
_currentContext = currentContext;
}
public SendAccessResult SendCanBeAccessed(Send send,
@ -79,18 +71,6 @@ public class SendAuthorizationService : ISendAuthorizationService
await _sendRepository.ReplaceAsync(sendToBeAccessed);
await _pushNotificationService.PushSyncSendUpdateAsync(sendToBeAccessed);
await _referenceEventService.RaiseEventAsync(new ReferenceEvent
{
Id = sendToBeAccessed.UserId ?? default,
Type = ReferenceEventType.SendAccessed,
Source = ReferenceEventSource.User,
SendType = sendToBeAccessed.Type,
MaxAccessCount = sendToBeAccessed.MaxAccessCount,
HasPassword = !string.IsNullOrWhiteSpace(sendToBeAccessed.Password),
SendHasNotes = sendToBeAccessed.Data?.Contains("Notes"),
ClientId = _currentContext.ClientId,
ClientVersion = _currentContext.ClientVersion
});
return accessResult;
}

View File

@ -1,8 +0,0 @@
using Bit.Core.Tools.Models.Business;
namespace Bit.Core.Tools.Services;
public interface IReferenceEventService
{
Task RaiseEventAsync(ReferenceEvent referenceEvent);
}

View File

@ -1,48 +0,0 @@
using System.Text;
using System.Text.Json;
using Azure.Storage.Queues;
using Bit.Core.Settings;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Utilities;
namespace Bit.Core.Tools.Services;
public class AzureQueueReferenceEventService : IReferenceEventService
{
private const string _queueName = "reference-events";
private readonly QueueClient _queueClient;
private readonly GlobalSettings _globalSettings;
public AzureQueueReferenceEventService(
GlobalSettings globalSettings)
{
_queueClient = new QueueClient(globalSettings.Events.ConnectionString, _queueName);
_globalSettings = globalSettings;
}
public async Task RaiseEventAsync(ReferenceEvent referenceEvent)
{
await SendMessageAsync(referenceEvent);
}
private async Task SendMessageAsync(ReferenceEvent referenceEvent)
{
if (_globalSettings.SelfHosted)
{
// Ignore for self-hosted
return;
}
try
{
var message = JsonSerializer.Serialize(referenceEvent, JsonHelpers.IgnoreWritingNullAndCamelCase);
// Messages need to be base64 encoded
var encodedMessage = Convert.ToBase64String(Encoding.UTF8.GetBytes(message));
await _queueClient.SendMessageAsync(encodedMessage);
}
catch
{
// Ignore failure
}
}
}

View File

@ -1,11 +0,0 @@
using Bit.Core.Tools.Models.Business;
namespace Bit.Core.Tools.Services;
public class NoopReferenceEventService : IReferenceEventService
{
public Task RaiseEventAsync(ReferenceEvent referenceEvent)
{
return Task.CompletedTask;
}
}

View File

@ -637,9 +637,9 @@ public static class CoreHelpers
return null;
}
if (!globalSettings.SelfHosted && httpContext.Request.Headers.ContainsKey(RealConnectingIp))
if (!globalSettings.SelfHosted && httpContext.Request.Headers.TryGetValue(RealConnectingIp, out var realConnectingIp))
{
return httpContext.Request.Headers[RealConnectingIp].ToString();
return realConnectingIp.ToString();
}
return httpContext.Connection?.RemoteIpAddress?.ToString();

View File

@ -46,7 +46,7 @@ public static class LoggerFactoryExtensions
{
return true;
}
var eventId = e.Properties.ContainsKey("EventId") ? e.Properties["EventId"].ToString() : null;
var eventId = e.Properties.TryGetValue("EventId", out var eventIdValue) ? eventIdValue.ToString() : null;
if (eventId?.Contains(Constants.BypassFiltersEventId.ToString()) ?? false)
{
return true;

View File

@ -24,7 +24,7 @@ public interface ICipherService
Task DeleteFolderAsync(Folder folder);
Task ShareAsync(Cipher originalCipher, Cipher cipher, Guid organizationId, IEnumerable<Guid> collectionIds,
Guid userId, DateTime? lastKnownRevisionDate);
Task ShareManyAsync(IEnumerable<(Cipher cipher, DateTime? lastKnownRevisionDate)> ciphers, Guid organizationId,
Task<IEnumerable<Cipher>> ShareManyAsync(IEnumerable<(Cipher cipher, DateTime? lastKnownRevisionDate)> ciphers, Guid organizationId,
IEnumerable<Guid> collectionIds, Guid sharingUserId);
Task SaveCollectionsAsync(Cipher cipher, IEnumerable<Guid> collectionIds, Guid savingUserId, bool orgAdmin);
Task SoftDeleteAsync(CipherDetails cipherDetails, Guid deletingUserId, bool orgAdmin = false);

View File

@ -251,16 +251,17 @@ public class AzureAttachmentStorageService : IAttachmentStorageService
private async Task InitAsync(string containerName)
{
if (!_attachmentContainers.ContainsKey(containerName) || _attachmentContainers[containerName] == null)
if (!_attachmentContainers.TryGetValue(containerName, out var attachmentContainer) || attachmentContainer == null)
{
_attachmentContainers[containerName] = _blobServiceClient.GetBlobContainerClient(containerName);
attachmentContainer = _blobServiceClient.GetBlobContainerClient(containerName);
_attachmentContainers[containerName] = attachmentContainer;
if (containerName == "attachments")
{
await _attachmentContainers[containerName].CreateIfNotExistsAsync(PublicAccessType.Blob, null, null);
await attachmentContainer.CreateIfNotExistsAsync(PublicAccessType.Blob, null, null);
}
else
{
await _attachmentContainers[containerName].CreateIfNotExistsAsync(PublicAccessType.None, null, null);
await attachmentContainer.CreateIfNotExistsAsync(PublicAccessType.None, null, null);
}
}
}

View File

@ -3,16 +3,12 @@ using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Platform.Push;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Bit.Core.Vault.Authorization.Permissions;
using Bit.Core.Vault.Entities;
@ -41,8 +37,6 @@ public class CipherService : ICipherService
private readonly IPolicyService _policyService;
private readonly GlobalSettings _globalSettings;
private const long _fileSizeLeeway = 1024L * 1024L; // 1MB
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
private readonly IGetCipherPermissionsForUserQuery _getCipherPermissionsForUserQuery;
private readonly IPolicyRequirementQuery _policyRequirementQuery;
private readonly IApplicationCacheService _applicationCacheService;
@ -62,8 +56,6 @@ public class CipherService : ICipherService
IUserService userService,
IPolicyService policyService,
GlobalSettings globalSettings,
IReferenceEventService referenceEventService,
ICurrentContext currentContext,
IGetCipherPermissionsForUserQuery getCipherPermissionsForUserQuery,
IPolicyRequirementQuery policyRequirementQuery,
IApplicationCacheService applicationCacheService,
@ -82,8 +74,6 @@ public class CipherService : ICipherService
_userService = userService;
_policyService = policyService;
_globalSettings = globalSettings;
_referenceEventService = referenceEventService;
_currentContext = currentContext;
_getCipherPermissionsForUserQuery = getCipherPermissionsForUserQuery;
_policyRequirementQuery = policyRequirementQuery;
_applicationCacheService = applicationCacheService;
@ -108,9 +98,6 @@ public class CipherService : ICipherService
cipher.UserId = savingUserId;
}
await _cipherRepository.CreateAsync(cipher, collectionIds);
await _referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.CipherCreated, await _organizationRepository.GetByIdAsync(cipher.OrganizationId.Value), _currentContext));
}
else
{
@ -325,13 +312,11 @@ public class CipherService : ICipherService
}
var attachments = cipher.GetAttachments();
if (!attachments.ContainsKey(attachmentId))
if (!attachments.TryGetValue(attachmentId, out var originalAttachmentMetadata))
{
throw new BadRequestException($"Cipher does not own specified attachment");
}
var originalAttachmentMetadata = attachments[attachmentId];
if (originalAttachmentMetadata.TempMetadata != null)
{
throw new BadRequestException("Another process is trying to migrate this attachment");
@ -408,12 +393,11 @@ public class CipherService : ICipherService
{
var attachments = cipher?.GetAttachments() ?? new Dictionary<string, CipherAttachment.MetaData>();
if (!attachments.ContainsKey(attachmentId))
if (!attachments.TryGetValue(attachmentId, out var data))
{
throw new NotFoundException();
}
var data = attachments[attachmentId];
var response = new AttachmentResponseData
{
Cipher = cipher,
@ -641,7 +625,7 @@ public class CipherService : ICipherService
await _pushService.PushSyncCipherUpdateAsync(cipher, collectionIds);
}
public async Task ShareManyAsync(IEnumerable<(Cipher cipher, DateTime? lastKnownRevisionDate)> cipherInfos,
public async Task<IEnumerable<Cipher>> ShareManyAsync(IEnumerable<(Cipher cipher, DateTime? lastKnownRevisionDate)> cipherInfos,
Guid organizationId, IEnumerable<Guid> collectionIds, Guid sharingUserId)
{
var cipherIds = new List<Guid>();
@ -668,6 +652,7 @@ public class CipherService : ICipherService
// push
await _pushService.PushSyncCiphersAsync(sharingUserId);
return cipherInfos.Select(c => c.cipher);
}
public async Task SaveCollectionsAsync(Cipher cipher, IEnumerable<Guid> collectionIds, Guid savingUserId,

View File

@ -69,9 +69,9 @@ public class CollectController : Controller
continue;
}
Cipher cipher = null;
if (ciphersCache.ContainsKey(eventModel.CipherId.Value))
if (ciphersCache.TryGetValue(eventModel.CipherId.Value, out var cachedCipher))
{
cipher = ciphersCache[eventModel.CipherId.Value];
cipher = cachedCipher;
}
else
{
@ -96,10 +96,7 @@ public class CollectController : Controller
continue;
}
}
if (!ciphersCache.ContainsKey(eventModel.CipherId.Value))
{
ciphersCache.Add(eventModel.CipherId.Value, cipher);
}
ciphersCache.TryAdd(eventModel.CipherId.Value, cipher);
cipherEvents.Add(new Tuple<Cipher, EventType, DateTime?>(cipher, eventModel.Type, eventModel.Date));
break;
case EventType.Organization_ClientExportedVault:

View File

@ -22,8 +22,8 @@ public class Program
return e.Level >= globalSettings.MinLogLevel.EventsSettings.IdentityToken;
}
if (e.Properties.ContainsKey("RequestPath") &&
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
if (e.Properties.TryGetValue("RequestPath", out var requestPath) &&
!string.IsNullOrWhiteSpace(requestPath?.ToString()) &&
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
{
return false;

View File

@ -13,9 +13,9 @@ public class DomainMappingService : IDomainMappingService
public string MapDomain(string hostname)
{
if (_map.ContainsKey(hostname))
if (_map.TryGetValue(hostname, out var mappedDomain))
{
return _map[hostname];
return mappedDomain;
}
return hostname;

View File

@ -1,11 +1,7 @@
using Bit.Core;
using Bit.Core.Billing.Models.Api.Requests.Accounts;
using Bit.Core.Billing.TrialInitiation.Registration;
using Bit.Core.Context;
using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Bit.SharedWeb.Utilities;
using Microsoft.AspNetCore.Mvc;
@ -15,9 +11,7 @@ namespace Bit.Identity.Billing.Controller;
[Route("accounts")]
[ExceptionHandlerFilter]
public class AccountsController(
ICurrentContext currentContext,
ISendTrialInitiationEmailForRegistrationCommand sendTrialInitiationEmailForRegistrationCommand,
IReferenceEventService referenceEventService,
IFeatureService featureService) : Microsoft.AspNetCore.Mvc.Controller
{
[HttpPost("trial/send-verification-email")]
@ -36,15 +30,6 @@ public class AccountsController(
model.Products,
trialLength);
var refEvent = new ReferenceEvent
{
Type = ReferenceEventType.SignupEmailSubmit,
ClientId = currentContext.ClientId,
ClientVersion = currentContext.ClientVersion,
Source = ReferenceEventSource.Registration
};
await referenceEventService.RaiseEventAsync(refEvent);
if (token != null)
{
return Ok(token);

View File

@ -16,9 +16,6 @@ using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tokens;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Bit.Identity.Models.Request.Accounts;
using Bit.Identity.Models.Response.Accounts;
@ -39,7 +36,6 @@ public class AccountsController : Controller
private readonly IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> _assertionOptionsDataProtector;
private readonly IGetWebAuthnLoginCredentialAssertionOptionsCommand _getWebAuthnLoginCredentialAssertionOptionsCommand;
private readonly ISendVerificationEmailForRegistrationCommand _sendVerificationEmailForRegistrationCommand;
private readonly IReferenceEventService _referenceEventService;
private readonly IFeatureService _featureService;
private readonly IDataProtectorTokenFactory<RegistrationEmailVerificationTokenable> _registrationEmailVerificationTokenDataFactory;
@ -86,7 +82,6 @@ public class AccountsController : Controller
IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> assertionOptionsDataProtector,
IGetWebAuthnLoginCredentialAssertionOptionsCommand getWebAuthnLoginCredentialAssertionOptionsCommand,
ISendVerificationEmailForRegistrationCommand sendVerificationEmailForRegistrationCommand,
IReferenceEventService referenceEventService,
IFeatureService featureService,
IDataProtectorTokenFactory<RegistrationEmailVerificationTokenable> registrationEmailVerificationTokenDataFactory,
GlobalSettings globalSettings
@ -99,7 +94,6 @@ public class AccountsController : Controller
_assertionOptionsDataProtector = assertionOptionsDataProtector;
_getWebAuthnLoginCredentialAssertionOptionsCommand = getWebAuthnLoginCredentialAssertionOptionsCommand;
_sendVerificationEmailForRegistrationCommand = sendVerificationEmailForRegistrationCommand;
_referenceEventService = referenceEventService;
_featureService = featureService;
_registrationEmailVerificationTokenDataFactory = registrationEmailVerificationTokenDataFactory;
@ -115,15 +109,6 @@ public class AccountsController : Controller
var token = await _sendVerificationEmailForRegistrationCommand.Run(model.Email, model.Name,
model.ReceiveMarketingEmails);
var refEvent = new ReferenceEvent
{
Type = ReferenceEventType.SignupEmailSubmit,
ClientId = _currentContext.ClientId,
ClientVersion = _currentContext.ClientVersion,
Source = ReferenceEventSource.Registration
};
await _referenceEventService.RaiseEventAsync(refEvent);
if (token != null)
{
return Ok(token);
@ -142,18 +127,6 @@ public class AccountsController : Controller
var user = await _userRepository.GetByEmailAsync(model.Email);
var userExists = user != null;
var refEvent = new ReferenceEvent
{
Type = ReferenceEventType.SignupEmailClicked,
ClientId = _currentContext.ClientId,
ClientVersion = _currentContext.ClientVersion,
Source = ReferenceEventSource.Registration,
EmailVerificationTokenValid = tokenValid,
UserAlreadyExists = userExists
};
await _referenceEventService.RaiseEventAsync(refEvent);
if (!tokenValid || userExists)
{
throw new BadRequestException("Expired link. Please restart registration or try logging in. You may already have an account");

View File

@ -193,7 +193,7 @@ public abstract class BaseRequestValidator<T> where T : class
protected async Task FailAuthForLegacyUserAsync(User user, T context)
{
await BuildErrorResultAsync(
$"Encryption key migration is required. Please log in to the web vault at {_globalSettings.BaseServiceUri.VaultWithHash}",
$"Legacy encryption without a userkey is no longer supported. To recover your account, please contact support",
false, context, user);
}

View File

@ -27,6 +27,7 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
{
private readonly UserManager<User> _userManager;
private readonly IUpdateInstallationCommand _updateInstallationCommand;
private readonly Version _denyLegacyUserMinimumVersion = new(Constants.DenyLegacyUserMinimumVersion);
public CustomTokenRequestValidator(
UserManager<User> userManager,
@ -73,7 +74,7 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
{
// Force legacy users to the web for migration
if (await _userService.IsLegacyUser(GetSubject(context)?.GetSubjectId()) &&
context.Result.ValidatedRequest.ClientId != "web")
(context.Result.ValidatedRequest.ClientId != "web" || CurrentContext.ClientVersion >= _denyLegacyUserMinimumVersion))
{
await FailAuthForLegacyUserAsync(null, context);
return;

View File

@ -240,7 +240,7 @@ public class TwoFactorAuthenticationValidator(
private bool OrgUsing2fa(IDictionary<Guid, OrganizationAbility> orgAbilities, Guid orgId)
{
return orgAbilities != null && orgAbilities.ContainsKey(orgId) &&
orgAbilities[orgId].Enabled && orgAbilities[orgId].Using2fa;
return orgAbilities != null && orgAbilities.TryGetValue(orgId, out var orgAbility) &&
orgAbility.Enabled && orgAbility.Using2fa;
}
}

View File

@ -120,9 +120,9 @@ public class Startup
// Pass domain_hint onto the sso idp
context.ProtocolMessage.DomainHint = context.Properties.Items["domain_hint"];
context.ProtocolMessage.Parameters.Add("organizationId", context.Properties.Items["organizationId"]);
if (context.Properties.Items.ContainsKey("user_identifier"))
if (context.Properties.Items.TryGetValue("user_identifier", out var userIdentifier))
{
context.ProtocolMessage.SessionState = context.Properties.Items["user_identifier"];
context.ProtocolMessage.SessionState = userIdentifier;
}
if (context.Properties.Parameters.Count > 0 &&

View File

@ -352,15 +352,6 @@ public static class ServiceCollectionExtensions
{
services.AddSingleton<ISendFileStorageService, NoopSendFileStorageService>();
}
if (globalSettings.SelfHosted)
{
services.AddSingleton<IReferenceEventService, NoopReferenceEventService>();
}
else
{
services.AddSingleton<IReferenceEventService, AzureQueueReferenceEventService>();
}
}
public static void AddOosServices(this IServiceCollection services)

View File

@ -23,7 +23,6 @@ using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces;
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Services;
using NSubstitute;
using NSubstitute.ReturnsExtensions;
using Xunit;
@ -46,7 +45,6 @@ public class OrganizationsControllerTests : IDisposable
private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand;
private readonly IUpgradeOrganizationPlanCommand _upgradeOrganizationPlanCommand;
private readonly IAddSecretsManagerSubscriptionCommand _addSecretsManagerSubscriptionCommand;
private readonly IReferenceEventService _referenceEventService;
private readonly ISubscriberService _subscriberService;
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
private readonly IOrganizationInstallationRepository _organizationInstallationRepository;
@ -71,7 +69,6 @@ public class OrganizationsControllerTests : IDisposable
_updateSecretsManagerSubscriptionCommand = Substitute.For<IUpdateSecretsManagerSubscriptionCommand>();
_upgradeOrganizationPlanCommand = Substitute.For<IUpgradeOrganizationPlanCommand>();
_addSecretsManagerSubscriptionCommand = Substitute.For<IAddSecretsManagerSubscriptionCommand>();
_referenceEventService = Substitute.For<IReferenceEventService>();
_subscriberService = Substitute.For<ISubscriberService>();
_removeOrganizationUserCommand = Substitute.For<IRemoveOrganizationUserCommand>();
_organizationInstallationRepository = Substitute.For<IOrganizationInstallationRepository>();
@ -90,7 +87,6 @@ public class OrganizationsControllerTests : IDisposable
_updateSecretsManagerSubscriptionCommand,
_upgradeOrganizationPlanCommand,
_addSecretsManagerSubscriptionCommand,
_referenceEventService,
_subscriberService,
_organizationInstallationRepository,
_pricingClient);

View File

@ -3,7 +3,6 @@ using AutoFixture.Xunit2;
using Bit.Api.Tools.Controllers;
using Bit.Api.Tools.Models.Request;
using Bit.Api.Tools.Models.Response;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Services;
@ -33,7 +32,6 @@ public class SendsControllerTests : IDisposable
private readonly ISendAuthorizationService _sendAuthorizationService;
private readonly ISendFileStorageService _sendFileStorageService;
private readonly ILogger<SendsController> _logger;
private readonly ICurrentContext _currentContext;
public SendsControllerTests()
{
@ -45,7 +43,6 @@ public class SendsControllerTests : IDisposable
_sendFileStorageService = Substitute.For<ISendFileStorageService>();
_globalSettings = new GlobalSettings();
_logger = Substitute.For<ILogger<SendsController>>();
_currentContext = Substitute.For<ICurrentContext>();
_sut = new SendsController(
_sendRepository,
@ -55,8 +52,7 @@ public class SendsControllerTests : IDisposable
_nonAnonymousSendCommand,
_sendFileStorageService,
_logger,
_globalSettings,
_currentContext
_globalSettings
);
}

View File

@ -1741,8 +1741,180 @@ public class CiphersControllerTests
{
model.OrganizationId = Guid.NewGuid();
sutProvider.GetDependency<ICurrentContext>().ProviderUserForOrgAsync(model.OrganizationId).Returns(true);
sutProvider.GetDependency<ICurrentContext>()
.ProviderUserForOrgAsync(new Guid(model.OrganizationId.ToString()))
.Returns(Task.FromResult(true));
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.PutRestoreManyAdmin(model));
await Assert.ThrowsAsync<NotFoundException>(
() => sutProvider.Sut.PutRestoreManyAdmin(model)
);
}
[Theory]
[BitAutoData]
public async Task PutShareMany_ShouldShareCiphersAndReturnRevisionDateMap(
User user,
Guid organizationId,
Guid userId,
SutProvider<CiphersController> sutProvider)
{
var oldDate1 = DateTime.UtcNow.AddDays(-1);
var oldDate2 = DateTime.UtcNow.AddDays(-2);
var detail1 = new CipherDetails
{
Id = Guid.NewGuid(),
UserId = userId,
OrganizationId = organizationId,
Type = CipherType.Login,
Data = JsonSerializer.Serialize(new CipherLoginData()),
RevisionDate = oldDate1
};
var detail2 = new CipherDetails
{
Id = Guid.NewGuid(),
UserId = userId,
OrganizationId = organizationId,
Type = CipherType.SecureNote,
Data = JsonSerializer.Serialize(new CipherSecureNoteData()),
RevisionDate = oldDate2
};
var preloadedDetails = new List<CipherDetails> { detail1, detail2 };
var newDate1 = oldDate1.AddMinutes(5);
var newDate2 = oldDate2.AddMinutes(5);
var updatedCipher1 = new Cipher { Id = detail1.Id, RevisionDate = newDate1, Type = detail1.Type, Data = detail1.Data };
var updatedCipher2 = new Cipher { Id = detail2.Id, RevisionDate = newDate2, Type = detail2.Type, Data = detail2.Data };
sutProvider.GetDependency<ICurrentContext>()
.OrganizationUser(organizationId)
.Returns(Task.FromResult(true));
sutProvider.GetDependency<IUserService>()
.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
.Returns(Task.FromResult(user));
sutProvider.GetDependency<IUserService>()
.GetProperUserId(default!)
.ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICipherRepository>()
.GetManyByUserIdAsync(userId, withOrganizations: false)
.Returns(Task.FromResult((ICollection<CipherDetails>)preloadedDetails));
sutProvider.GetDependency<ICipherService>()
.ShareManyAsync(
Arg.Any<IEnumerable<(Cipher, DateTime?)>>(),
organizationId,
Arg.Any<IEnumerable<Guid>>(),
userId
)
.Returns(Task.FromResult<IEnumerable<Cipher>>(new[] { updatedCipher1, updatedCipher2 }));
var cipherRequests = preloadedDetails.Select(d => new CipherWithIdRequestModel
{
Id = d.Id,
OrganizationId = d.OrganizationId!.Value.ToString(),
LastKnownRevisionDate = d.RevisionDate,
Type = d.Type
}).ToList();
var model = new CipherBulkShareRequestModel
{
Ciphers = cipherRequests,
CollectionIds = new[] { Guid.NewGuid().ToString() }
};
var result = await sutProvider.Sut.PutShareMany(model);
Assert.Equal(2, result.Length);
var revisionDates = result.Select(r => r.RevisionDate).ToList();
Assert.Contains(newDate1, revisionDates);
Assert.Contains(newDate2, revisionDates);
await sutProvider.GetDependency<ICipherService>()
.Received(1)
.ShareManyAsync(
Arg.Is<IEnumerable<(Cipher, DateTime?)>>(list =>
list.Select(x => x.Item1.Id).OrderBy(id => id)
.SequenceEqual(new[] { detail1.Id, detail2.Id }.OrderBy(id => id))
),
organizationId,
Arg.Any<IEnumerable<Guid>>(),
userId
);
}
[Theory, BitAutoData]
public async Task PutShareMany_OrganizationUserFalse_ThrowsNotFound(
CipherBulkShareRequestModel model,
SutProvider<CiphersController> sut)
{
model.Ciphers = new[] {
new CipherWithIdRequestModel { Id = Guid.NewGuid(), OrganizationId = Guid.NewGuid().ToString() }
};
sut.GetDependency<ICurrentContext>()
.OrganizationUser(Arg.Any<Guid>())
.Returns(Task.FromResult(false));
await Assert.ThrowsAsync<NotFoundException>(() => sut.Sut.PutShareMany(model));
}
[Theory, BitAutoData]
public async Task PutShareMany_CipherNotOwned_ThrowsNotFoundException(
Guid organizationId,
Guid userId,
CipherWithIdRequestModel request,
SutProvider<CiphersController> sutProvider)
{
request.EncryptedFor = userId;
var model = new CipherBulkShareRequestModel
{
Ciphers = new[] { request },
CollectionIds = new[] { Guid.NewGuid().ToString() }
};
sutProvider.GetDependency<ICurrentContext>()
.OrganizationUser(organizationId)
.Returns(Task.FromResult(true));
sutProvider.GetDependency<IUserService>()
.GetProperUserId(default)
.ReturnsForAnyArgs(userId);
sutProvider.GetDependency<ICipherRepository>()
.GetManyByUserIdAsync(userId, withOrganizations: false)
.Returns(Task.FromResult((ICollection<CipherDetails>)new List<CipherDetails>()));
await Assert.ThrowsAsync<NotFoundException>(
() => sutProvider.Sut.PutShareMany(model)
);
}
[Theory, BitAutoData]
public async Task PutShareMany_EncryptedForWrongUser_ThrowsNotFoundException(
Guid organizationId,
Guid userId,
CipherWithIdRequestModel request,
SutProvider<CiphersController> sutProvider)
{
request.EncryptedFor = Guid.NewGuid(); // not equal to userId
var model = new CipherBulkShareRequestModel
{
Ciphers = new[] { request },
CollectionIds = new[] { Guid.NewGuid().ToString() }
};
sutProvider.GetDependency<ICurrentContext>()
.OrganizationUser(organizationId)
.Returns(Task.FromResult(true));
sutProvider.GetDependency<IUserService>()
.GetProperUserId(default)
.ReturnsForAnyArgs(userId);
var existing = new CipherDetails { Id = request.Id.Value };
sutProvider.GetDependency<ICipherRepository>()
.GetManyByUserIdAsync(userId, withOrganizations: false)
.Returns(Task.FromResult((ICollection<CipherDetails>)(new[] { existing })));
await Assert.ThrowsAsync<NotFoundException>(
() => sutProvider.Sut.PutShareMany(model)
);
}
}

View File

@ -27,9 +27,9 @@ public class SutProvider<TSut> : ISutProvider
=> SetDependency(typeof(T), dependency, parameterName);
public SutProvider<TSut> SetDependency(Type dependencyType, object dependency, string parameterName = "")
{
if (_dependencies.ContainsKey(dependencyType))
if (_dependencies.TryGetValue(dependencyType, out var dependencyForType))
{
_dependencies[dependencyType][parameterName] = dependency;
dependencyForType[parameterName] = dependency;
}
else
{
@ -46,12 +46,11 @@ public class SutProvider<TSut> : ISutProvider
{
return _dependencies[dependencyType][parameterName];
}
else if (_dependencies.ContainsKey(dependencyType))
else if (_dependencies.TryGetValue(dependencyType, out var knownDependencies))
{
var knownDependencies = _dependencies[dependencyType];
if (knownDependencies.Values.Count == 1)
{
return _dependencies[dependencyType].Values.Single();
return knownDependencies.Values.Single();
}
else
{

View File

@ -6,9 +6,6 @@ using Bit.Core.Exceptions;
using Bit.Core.Models.Data;
using Bit.Core.Services;
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Bit.Test.Common.Helpers;
@ -27,7 +24,6 @@ public class CreateGroupCommandTests
await sutProvider.GetDependency<IGroupRepository>().Received(1).CreateAsync(group);
await sutProvider.GetDependency<IEventService>().Received(1).LogGroupEventAsync(group, Enums.EventType.Group_Created);
await sutProvider.GetDependency<IReferenceEventService>().Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(r => r.Type == ReferenceEventType.GroupCreated && r.Id == organization.Id && r.Source == ReferenceEventSource.Organization));
AssertHelper.AssertRecent(group.CreationDate);
AssertHelper.AssertRecent(group.RevisionDate);
}
@ -48,7 +44,6 @@ public class CreateGroupCommandTests
await sutProvider.GetDependency<IGroupRepository>().Received(1).CreateAsync(group, collections);
await sutProvider.GetDependency<IEventService>().Received(1).LogGroupEventAsync(group, Enums.EventType.Group_Created);
await sutProvider.GetDependency<IReferenceEventService>().Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(r => r.Type == ReferenceEventType.GroupCreated && r.Id == organization.Id && r.Source == ReferenceEventSource.Organization));
AssertHelper.AssertRecent(group.CreationDate);
AssertHelper.AssertRecent(group.RevisionDate);
}
@ -60,7 +55,6 @@ public class CreateGroupCommandTests
await sutProvider.GetDependency<IGroupRepository>().Received(1).CreateAsync(group);
await sutProvider.GetDependency<IEventService>().Received(1).LogGroupEventAsync(group, Enums.EventType.Group_Created, eventSystemUser);
await sutProvider.GetDependency<IReferenceEventService>().Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(r => r.Type == ReferenceEventType.GroupCreated && r.Id == organization.Id && r.Source == ReferenceEventSource.Organization));
AssertHelper.AssertRecent(group.CreationDate);
AssertHelper.AssertRecent(group.RevisionDate);
}
@ -74,7 +68,6 @@ public class CreateGroupCommandTests
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogGroupEventAsync(default, default, default);
await sutProvider.GetDependency<IReferenceEventService>().DidNotReceiveWithAnyArgs().RaiseEventAsync(default);
}
[Theory, OrganizationCustomize(UseGroups = false), BitAutoData]
@ -86,6 +79,5 @@ public class CreateGroupCommandTests
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogGroupEventAsync(default, default, default);
await sutProvider.GetDependency<IReferenceEventService>().DidNotReceiveWithAnyArgs().RaiseEventAsync(default);
}
}

View File

@ -10,9 +10,6 @@ using Bit.Core.Exceptions;
using Bit.Core.Models.Business;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
@ -51,15 +48,6 @@ public class CloudICloudOrganizationSignUpCommandTests
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).CreateAsync(
Arg.Is<OrganizationUser>(o => o.AccessSecretsManager == signup.UseSecretsManager));
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
referenceEvent.Type == ReferenceEventType.Signup &&
referenceEvent.PlanName == plan.Name &&
referenceEvent.PlanType == plan.Type &&
referenceEvent.Seats == result.Organization.Seats &&
referenceEvent.Storage == result.Organization.MaxStorageGb));
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
Assert.NotNull(result.Organization);
Assert.NotNull(result.OrganizationUser);
@ -145,15 +133,6 @@ public class CloudICloudOrganizationSignUpCommandTests
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).CreateAsync(
Arg.Is<OrganizationUser>(o => o.AccessSecretsManager == signup.UseSecretsManager));
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
referenceEvent.Type == ReferenceEventType.Signup &&
referenceEvent.PlanName == plan.Name &&
referenceEvent.PlanType == plan.Type &&
referenceEvent.Seats == result.Organization.Seats &&
referenceEvent.Storage == result.Organization.MaxStorageGb));
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
Assert.NotNull(result.Organization);
Assert.NotNull(result.OrganizationUser);

View File

@ -10,9 +10,6 @@ using Bit.Core.Models.Data;
using Bit.Core.Models.StaticStore;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
@ -65,17 +62,6 @@ public class ProviderClientOrganizationSignUpCommandTests
)
);
await sutProvider.GetDependency<IReferenceEventService>()
.Received(1)
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
referenceEvent.Type == ReferenceEventType.Signup &&
referenceEvent.PlanName == plan.Name &&
referenceEvent.PlanType == plan.Type &&
referenceEvent.Seats == result.Organization.Seats &&
referenceEvent.Storage == result.Organization.MaxStorageGb &&
referenceEvent.SignupInitiationPath == signup.InitiationPath
));
await sutProvider.GetDependency<ICollectionRepository>()
.Received(1)
.CreateAsync(

View File

@ -22,9 +22,6 @@ using Bit.Core.Settings;
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
using Bit.Core.Test.AutoFixture.OrganizationUserFixtures;
using Bit.Core.Tokens;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
@ -100,10 +97,6 @@ public class OrganizationServiceTests
await sutProvider.GetDependency<IEventService>().Received(1)
.LogOrganizationUserEventsAsync(Arg.Is<IEnumerable<(OrganizationUser, EventType, EventSystemUser, DateTime?)>>(events =>
events.Count() == expectedNewUsersCount));
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
referenceEvent.Type == ReferenceEventType.InvitedUsers && referenceEvent.Id == org.Id &&
referenceEvent.Users == expectedNewUsersCount));
}
[Theory, PaidOrganizationCustomize, BitAutoData]
@ -170,10 +163,6 @@ public class OrganizationServiceTests
await sutProvider.GetDependency<IEventService>().Received(1)
.LogOrganizationUserEventsAsync(Arg.Is<IEnumerable<(OrganizationUser, EventType, EventSystemUser, DateTime?)>>(events =>
events.Count(e => e.Item2 == EventType.OrganizationUser_Invited) == expectedNewUsersCount));
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
referenceEvent.Type == ReferenceEventType.InvitedUsers && referenceEvent.Id == org.Id &&
referenceEvent.Users == expectedNewUsersCount));
}
[Theory]
@ -728,9 +717,8 @@ public class OrganizationServiceTests
.UpdateSubscriptionAsync(Arg.Any<SecretsManagerSubscriptionUpdate>())
.ReturnsForAnyArgs(Task.FromResult(0)).AndDoes(x => organization.SmSeats += invitedSmUsers);
// Throw error at the end of the try block
sutProvider.GetDependency<IReferenceEventService>().RaiseEventAsync(default)
.ThrowsForAnyArgs<BadRequestException>();
sutProvider.GetDependency<ISendOrganizationInvitesCommand>()
.SendInvitesAsync(Arg.Any<SendInvitesRequest>()).ThrowsAsync<Exception>();
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType)
.Returns(StaticStore.GetPlan(organization.PlanType));

View File

@ -14,9 +14,6 @@ using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tokens;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
@ -57,10 +54,6 @@ public class RegisterUserCommandTests
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendWelcomeEmailAsync(user);
await sutProvider.GetDependency<IReferenceEventService>()
.Received(1)
.RaiseEventAsync(Arg.Is<ReferenceEvent>(refEvent => refEvent.Type == ReferenceEventType.Signup));
}
[Theory]
@ -85,10 +78,6 @@ public class RegisterUserCommandTests
await sutProvider.GetDependency<IMailService>()
.DidNotReceive()
.SendWelcomeEmailAsync(Arg.Any<User>());
await sutProvider.GetDependency<IReferenceEventService>()
.DidNotReceive()
.RaiseEventAsync(Arg.Any<ReferenceEvent>());
}
// -----------------------------------------------------------------------------------------------
@ -117,10 +106,6 @@ public class RegisterUserCommandTests
await sutProvider.GetDependency<IUserService>()
.Received(1)
.CreateUserAsync(user, masterPasswordHash);
await sutProvider.GetDependency<IReferenceEventService>()
.Received(1)
.RaiseEventAsync(Arg.Is<ReferenceEvent>(refEvent => refEvent.Type == ReferenceEventType.Signup));
}
// Complex happy path test
@ -215,18 +200,9 @@ public class RegisterUserCommandTests
.Received(1)
.SendWelcomeEmailAsync(user);
}
await sutProvider.GetDependency<IReferenceEventService>()
.Received(1)
.RaiseEventAsync(Arg.Is<ReferenceEvent>(refEvent => refEvent.Type == ReferenceEventType.Signup && refEvent.SignupInitiationPath == initiationPath));
}
else
{
await sutProvider.GetDependency<IReferenceEventService>()
.Received(1)
.RaiseEventAsync(Arg.Is<ReferenceEvent>(refEvent => refEvent.Type == ReferenceEventType.Signup && refEvent.SignupInitiationPath == default));
// Even if user doesn't have reference data, we should send them welcome email
await sutProvider.GetDependency<IMailService>()
.Received(1)
@ -359,10 +335,6 @@ public class RegisterUserCommandTests
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendWelcomeEmailAsync(user);
await sutProvider.GetDependency<IReferenceEventService>()
.Received(1)
.RaiseEventAsync(Arg.Is<ReferenceEvent>(refEvent => refEvent.Type == ReferenceEventType.Signup && refEvent.ReceiveMarketingEmails == receiveMarketingMaterials));
}
[Theory]
@ -429,10 +401,6 @@ public class RegisterUserCommandTests
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendWelcomeEmailAsync(user);
await sutProvider.GetDependency<IReferenceEventService>()
.Received(1)
.RaiseEventAsync(Arg.Is<ReferenceEvent>(refEvent => refEvent.Type == ReferenceEventType.Signup));
}
[Theory]
@ -506,10 +474,6 @@ public class RegisterUserCommandTests
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendWelcomeEmailAsync(user);
await sutProvider.GetDependency<IReferenceEventService>()
.Received(1)
.RaiseEventAsync(Arg.Is<ReferenceEvent>(refEvent => refEvent.Type == ReferenceEventType.Signup));
}
[Theory]
@ -604,10 +568,6 @@ public class RegisterUserCommandTests
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendWelcomeEmailAsync(user);
await sutProvider.GetDependency<IReferenceEventService>()
.Received(1)
.RaiseEventAsync(Arg.Is<ReferenceEvent>(refEvent => refEvent.Type == ReferenceEventType.Signup));
}
[Theory]

View File

@ -26,7 +26,6 @@ using Bit.Core.Platform.Push;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Bit.Core.Vault.Repositories;
using Bit.Test.Common.AutoFixture;
@ -316,7 +315,6 @@ public class UserServiceTests
sutProvider.GetDependency<IPaymentService>(),
sutProvider.GetDependency<IPolicyRepository>(),
sutProvider.GetDependency<IPolicyService>(),
sutProvider.GetDependency<IReferenceEventService>(),
sutProvider.GetDependency<IFido2>(),
sutProvider.GetDependency<ICurrentContext>(),
sutProvider.GetDependency<IGlobalSettings>(),
@ -910,7 +908,6 @@ public class UserServiceTests
sutProvider.GetDependency<IPaymentService>(),
sutProvider.GetDependency<IPolicyRepository>(),
sutProvider.GetDependency<IPolicyService>(),
sutProvider.GetDependency<IReferenceEventService>(),
sutProvider.GetDependency<IFido2>(),
sutProvider.GetDependency<ICurrentContext>(),
sutProvider.GetDependency<IGlobalSettings>(),

View File

@ -9,10 +9,7 @@ using Bit.Core.Platform.Push;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Test.AutoFixture.CipherFixtures;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.ImportFeatures;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Core.Vault.Entities;
using Bit.Core.Vault.Models.Data;
using Bit.Core.Vault.Repositories;
@ -183,8 +180,6 @@ public class ImportCiphersAsyncCommandTests
!cus.Any(cu => cu.CollectionId == collections[0].Id) && // Check that access was not added for the collection that already existed in the organization
cus.All(cu => cu.OrganizationUserId == importingOrganizationUser.Id && cu.Manage == true)));
await sutProvider.GetDependency<IPushNotificationService>().Received(1).PushSyncVaultAsync(importingUserId);
await sutProvider.GetDependency<IReferenceEventService>().Received(1).RaiseEventAsync(
Arg.Is<ReferenceEvent>(e => e.Type == ReferenceEventType.VaultImported));
}
[Theory, BitAutoData]

View File

@ -8,7 +8,6 @@ using Bit.Core.Test.AutoFixture.CurrentContextFixtures;
using Bit.Core.Test.Tools.AutoFixture.SendFixtures;
using Bit.Core.Tools.Entities;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Models.Data;
using Bit.Core.Tools.Repositories;
using Bit.Core.Tools.SendFeatures;
@ -32,7 +31,6 @@ public class NonAnonymousSendCommandTests
private readonly ISendAuthorizationService _sendAuthorizationService;
private readonly ISendValidationService _sendValidationService;
private readonly IFeatureService _featureService;
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
private readonly ISendCoreHelperService _sendCoreHelperService;
private readonly NonAnonymousSendCommand _nonAnonymousSendCommand;
@ -45,7 +43,6 @@ public class NonAnonymousSendCommandTests
_sendAuthorizationService = Substitute.For<ISendAuthorizationService>();
_featureService = Substitute.For<IFeatureService>();
_sendValidationService = Substitute.For<ISendValidationService>();
_referenceEventService = Substitute.For<IReferenceEventService>();
_currentContext = Substitute.For<ICurrentContext>();
_sendCoreHelperService = Substitute.For<ISendCoreHelperService>();
@ -55,8 +52,6 @@ public class NonAnonymousSendCommandTests
_pushNotificationService,
_sendAuthorizationService,
_sendValidationService,
_referenceEventService,
_currentContext,
_sendCoreHelperService
);
}
@ -135,14 +130,6 @@ public class NonAnonymousSendCommandTests
// For new Sends
await _sendRepository.Received(1).CreateAsync(send);
await _pushNotificationService.Received(1).PushSyncSendCreateAsync(send);
await _referenceEventService.Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(e =>
e.Id == userId &&
e.Type == ReferenceEventType.SendCreated &&
e.Source == ReferenceEventSource.User &&
e.SendType == send.Type &&
e.SendHasNotes == true &&
e.ClientId == "test-client" &&
e.ClientVersion == Version.Parse("1.0.0")));
}
else
{
@ -150,7 +137,6 @@ public class NonAnonymousSendCommandTests
await _sendRepository.Received(1).UpsertAsync(send);
Assert.NotEqual(initialDate, send.RevisionDate);
await _pushNotificationService.Received(1).PushSyncSendUpdateAsync(send);
await _referenceEventService.DidNotReceive().RaiseEventAsync(Arg.Any<ReferenceEvent>());
}
}
@ -234,14 +220,6 @@ public class NonAnonymousSendCommandTests
// For new Sends
await _sendRepository.Received(1).CreateAsync(send);
await _pushNotificationService.Received(1).PushSyncSendCreateAsync(send);
await _referenceEventService.Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(e =>
e.Id == userId &&
e.Type == ReferenceEventType.SendCreated &&
e.Source == ReferenceEventSource.User &&
e.SendType == send.Type &&
e.HasPassword == false &&
e.ClientId == "test-client" &&
e.ClientVersion == Version.Parse("1.0.0")));
}
else
{
@ -249,7 +227,6 @@ public class NonAnonymousSendCommandTests
await _sendRepository.Received(1).UpsertAsync(send);
Assert.NotEqual(initialDate, send.RevisionDate);
await _pushNotificationService.Received(1).PushSyncSendUpdateAsync(send);
await _referenceEventService.DidNotReceive().RaiseEventAsync(Arg.Any<ReferenceEvent>());
}
}
@ -285,7 +262,6 @@ public class NonAnonymousSendCommandTests
await _sendRepository.DidNotReceive().UpsertAsync(Arg.Any<Send>());
await _pushNotificationService.DidNotReceive().PushSyncSendCreateAsync(Arg.Any<Send>());
await _pushNotificationService.DidNotReceive().PushSyncSendUpdateAsync(Arg.Any<Send>());
await _referenceEventService.DidNotReceive().RaiseEventAsync(Arg.Any<ReferenceEvent>());
}
[Theory]
@ -328,14 +304,6 @@ public class NonAnonymousSendCommandTests
// For new Sends
await _sendRepository.Received(1).CreateAsync(send);
await _pushNotificationService.Received(1).PushSyncSendCreateAsync(send);
await _referenceEventService.Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(e =>
e.Id == userId &&
e.Type == ReferenceEventType.SendCreated &&
e.Source == ReferenceEventSource.User &&
e.SendType == send.Type &&
e.SendHasNotes == true &&
e.ClientId == "test-client" &&
e.ClientVersion == Version.Parse("1.0.0")));
}
else
{
@ -343,7 +311,6 @@ public class NonAnonymousSendCommandTests
await _sendRepository.Received(1).UpsertAsync(send);
Assert.NotEqual(initialDate, send.RevisionDate);
await _pushNotificationService.Received(1).PushSyncSendUpdateAsync(send);
await _referenceEventService.DidNotReceive().RaiseEventAsync(Arg.Any<ReferenceEvent>());
}
}
@ -386,9 +353,6 @@ public class NonAnonymousSendCommandTests
// Verify push notification wasn't sent
await _pushNotificationService.DidNotReceive().PushSyncSendCreateAsync(Arg.Any<Send>());
await _pushNotificationService.DidNotReceive().PushSyncSendUpdateAsync(Arg.Any<Send>());
// Verify reference event service wasn't called
await _referenceEventService.DidNotReceive().RaiseEventAsync(Arg.Any<ReferenceEvent>());
}
[Theory]
@ -431,13 +395,6 @@ public class NonAnonymousSendCommandTests
// For new Sends
await _sendRepository.Received(1).CreateAsync(send);
await _pushNotificationService.Received(1).PushSyncSendCreateAsync(send);
await _referenceEventService.Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(e =>
e.Id == userId &&
e.Type == ReferenceEventType.SendCreated &&
e.Source == ReferenceEventSource.User &&
e.SendType == send.Type &&
e.ClientId == "test-client" &&
e.ClientVersion == Version.Parse("1.0.0")));
}
else
{
@ -445,7 +402,6 @@ public class NonAnonymousSendCommandTests
await _sendRepository.Received(1).UpsertAsync(send);
Assert.NotEqual(initialDate, send.RevisionDate);
await _pushNotificationService.Received(1).PushSyncSendUpdateAsync(send);
await _referenceEventService.DidNotReceive().RaiseEventAsync(Arg.Any<ReferenceEvent>());
}
}
@ -481,9 +437,6 @@ public class NonAnonymousSendCommandTests
// Verify push notification was sent for the update
await _pushNotificationService.Received(1).PushSyncSendUpdateAsync(send);
// Verify no reference event was raised (only happens for new sends)
await _referenceEventService.DidNotReceive().RaiseEventAsync(Arg.Any<ReferenceEvent>());
}
[Fact]

View File

@ -1,5 +1,4 @@
using Bit.Core.Context;
using Bit.Core.Platform.Push;
using Bit.Core.Platform.Push;
using Bit.Core.Tools.Entities;
using Bit.Core.Tools.Models.Data;
using Bit.Core.Tools.Repositories;
@ -15,8 +14,6 @@ public class SendAuthorizationServiceTests
private readonly ISendRepository _sendRepository;
private readonly IPasswordHasher<Bit.Core.Entities.User> _passwordHasher;
private readonly IPushNotificationService _pushNotificationService;
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
private readonly SendAuthorizationService _sendAuthorizationService;
public SendAuthorizationServiceTests()
@ -24,15 +21,11 @@ public class SendAuthorizationServiceTests
_sendRepository = Substitute.For<ISendRepository>();
_passwordHasher = Substitute.For<IPasswordHasher<Bit.Core.Entities.User>>();
_pushNotificationService = Substitute.For<IPushNotificationService>();
_referenceEventService = Substitute.For<IReferenceEventService>();
_currentContext = Substitute.For<ICurrentContext>();
_sendAuthorizationService = new SendAuthorizationService(
_sendRepository,
_passwordHasher,
_pushNotificationService,
_referenceEventService,
_currentContext);
_pushNotificationService);
}

View File

@ -238,7 +238,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
}
[Theory, BitAutoData, RegisterFinishRequestModelCustomize]
public async Task TokenEndpoint_GrantTypeClientCredentials_AsLegacyUser_NotOnWebClient_Fails(
public async Task TokenEndpoint_GrantTypeClientCredentials_AsLegacyUser_Fails(
RegisterFinishRequestModel model,
string deviceId)
{
@ -277,7 +277,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
var errorBody = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
var error = AssertHelper.AssertJsonProperty(errorBody.RootElement, "ErrorModel", JsonValueKind.Object);
var message = AssertHelper.AssertJsonProperty(error, "Message", JsonValueKind.String).GetString();
Assert.StartsWith("Encryption key migration is required.", message);
Assert.StartsWith("Legacy encryption without a userkey is no longer supported.", message);
}

View File

@ -14,9 +14,6 @@ using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tokens;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Bit.Identity.Controllers;
using Bit.Identity.Models.Request.Accounts;
using Bit.Test.Common.AutoFixture.Attributes;
@ -40,7 +37,6 @@ public class AccountsControllerTests : IDisposable
private readonly IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> _assertionOptionsDataProtector;
private readonly IGetWebAuthnLoginCredentialAssertionOptionsCommand _getWebAuthnLoginCredentialAssertionOptionsCommand;
private readonly ISendVerificationEmailForRegistrationCommand _sendVerificationEmailForRegistrationCommand;
private readonly IReferenceEventService _referenceEventService;
private readonly IFeatureService _featureService;
private readonly IDataProtectorTokenFactory<RegistrationEmailVerificationTokenable> _registrationEmailVerificationTokenDataFactory;
private readonly GlobalSettings _globalSettings;
@ -55,7 +51,6 @@ public class AccountsControllerTests : IDisposable
_assertionOptionsDataProtector = Substitute.For<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>();
_getWebAuthnLoginCredentialAssertionOptionsCommand = Substitute.For<IGetWebAuthnLoginCredentialAssertionOptionsCommand>();
_sendVerificationEmailForRegistrationCommand = Substitute.For<ISendVerificationEmailForRegistrationCommand>();
_referenceEventService = Substitute.For<IReferenceEventService>();
_featureService = Substitute.For<IFeatureService>();
_registrationEmailVerificationTokenDataFactory = Substitute.For<IDataProtectorTokenFactory<RegistrationEmailVerificationTokenable>>();
_globalSettings = Substitute.For<GlobalSettings>();
@ -68,7 +63,6 @@ public class AccountsControllerTests : IDisposable
_assertionOptionsDataProtector,
_getWebAuthnLoginCredentialAssertionOptionsCommand,
_sendVerificationEmailForRegistrationCommand,
_referenceEventService,
_featureService,
_registrationEmailVerificationTokenDataFactory,
_globalSettings
@ -163,8 +157,6 @@ public class AccountsControllerTests : IDisposable
var okResult = Assert.IsType<OkObjectResult>(result);
Assert.Equal(200, okResult.StatusCode);
Assert.Equal(token, okResult.Value);
await _referenceEventService.Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(e => e.Type == ReferenceEventType.SignupEmailSubmit));
}
[Theory]
@ -187,7 +179,6 @@ public class AccountsControllerTests : IDisposable
// Assert
var noContentResult = Assert.IsType<NoContentResult>(result);
Assert.Equal(204, noContentResult.StatusCode);
await _referenceEventService.Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(e => e.Type == ReferenceEventType.SignupEmailSubmit));
}
[Theory, BitAutoData]
@ -404,12 +395,6 @@ public class AccountsControllerTests : IDisposable
// Assert
var okResult = Assert.IsType<OkResult>(result);
Assert.Equal(200, okResult.StatusCode);
await _referenceEventService.Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(e =>
e.Type == ReferenceEventType.SignupEmailClicked
&& e.EmailVerificationTokenValid == true
&& e.UserAlreadyExists == false
));
}
[Theory, BitAutoData]
@ -435,12 +420,6 @@ public class AccountsControllerTests : IDisposable
// Act & assert
await Assert.ThrowsAsync<BadRequestException>(() => _sut.PostRegisterVerificationEmailClicked(requestModel));
await _referenceEventService.Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(e =>
e.Type == ReferenceEventType.SignupEmailClicked
&& e.EmailVerificationTokenValid == false
&& e.UserAlreadyExists == false
));
}
@ -467,12 +446,6 @@ public class AccountsControllerTests : IDisposable
// Act & assert
await Assert.ThrowsAsync<BadRequestException>(() => _sut.PostRegisterVerificationEmailClicked(requestModel));
await _referenceEventService.Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(e =>
e.Type == ReferenceEventType.SignupEmailClicked
&& e.EmailVerificationTokenValid == true
&& e.UserAlreadyExists == true
));
}
private void SetDefaultKdfHmacKey(byte[]? newKey)

View File

@ -373,8 +373,7 @@ public class BaseRequestValidatorTests
// Assert
Assert.True(context.GrantResult.IsError);
var errorResponse = (ErrorResponseModel)context.GrantResult.CustomResponse["ErrorModel"];
var expectedMessage = $"Encryption key migration is required. Please log in to the web " +
$"vault at {_globalSettings.BaseServiceUri.VaultWithHash}";
var expectedMessage = "Legacy encryption without a userkey is no longer supported. To recover your account, please contact support";
Assert.Equal(expectedMessage, errorResponse.Message);
}

View File

@ -4,7 +4,6 @@ using Bit.Core.Platform.Push;
using Bit.Core.Platform.Push.Internal;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Tools.Services;
using Bit.Infrastructure.EntityFramework.Repositories;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
@ -209,9 +208,6 @@ public abstract class WebApplicationFactoryBase<T> : WebApplicationFactory<T>
// TODO: Install and use azurite in CI pipeline
Replace<IInstallationDeviceRepository, NoopRepos.InstallationDeviceRepository>(services);
// TODO: Install and use azurite in CI pipeline
Replace<IReferenceEventService, NoopReferenceEventService>(services);
// Our Rate limiter works so well that it begins to fail tests unless we carve out
// one whitelisted ip. We should still test the rate limiter though and they should change the Ip
// to something that is NOT whitelisted

View File

@ -20,30 +20,29 @@ public class Program
ParseParameters();
if (_context.Parameters.ContainsKey("q"))
if (_context.Parameters.TryGetValue("q", out var q))
{
_context.Quiet = _context.Parameters["q"] == "true" || _context.Parameters["q"] == "1";
_context.Quiet = q == "true" || q == "1";
}
if (_context.Parameters.ContainsKey("os"))
if (_context.Parameters.TryGetValue("os", out var os))
{
_context.HostOS = _context.Parameters["os"];
_context.HostOS = os;
}
if (_context.Parameters.ContainsKey("corev"))
if (_context.Parameters.TryGetValue("corev", out var coreVersion))
{
_context.CoreVersion = _context.Parameters["corev"];
_context.CoreVersion = coreVersion;
}
if (_context.Parameters.ContainsKey("webv"))
if (_context.Parameters.TryGetValue("webv", out var webVersion))
{
_context.WebVersion = _context.Parameters["webv"];
_context.WebVersion = webVersion;
}
if (_context.Parameters.ContainsKey("keyconnectorv"))
if (_context.Parameters.TryGetValue("keyconnectorv", out var keyConnectorVersion))
{
_context.KeyConnectorVersion = _context.Parameters["keyconnectorv"];
_context.KeyConnectorVersion = keyConnectorVersion;
}
if (_context.Parameters.ContainsKey("stub"))
if (_context.Parameters.TryGetValue("stub", out var stub))
{
_context.Stub = _context.Parameters["stub"] == "true" ||
_context.Parameters["stub"] == "1";
_context.Stub = stub == "true" || stub == "1";
}
Helpers.WriteLine(_context);
@ -68,18 +67,18 @@ public class Program
private static void Install()
{
if (_context.Parameters.ContainsKey("letsencrypt"))
if (_context.Parameters.TryGetValue("letsencrypt", out var sslManagedLetsEncrypt))
{
_context.Config.SslManagedLetsEncrypt =
_context.Parameters["letsencrypt"].ToLowerInvariant() == "y";
sslManagedLetsEncrypt.ToLowerInvariant() == "y";
}
if (_context.Parameters.ContainsKey("domain"))
if (_context.Parameters.TryGetValue("domain", out var domain))
{
_context.Install.Domain = _context.Parameters["domain"].ToLowerInvariant();
_context.Install.Domain = domain.ToLowerInvariant();
}
if (_context.Parameters.ContainsKey("dbname"))
if (_context.Parameters.TryGetValue("dbname", out var database))
{
_context.Install.Database = _context.Parameters["dbname"];
_context.Install.Database = database;
}
if (_context.Stub)