mirror of
https://github.com/bitwarden/server.git
synced 2024-11-26 12:55:17 +01:00
[send.key] Update send.key when account encryption key is rotated (#1417)
* Rotate send.key with account encryption key
* Update tests
* Improve and refactor style, fix typo
* Use null instead of empty lists
* Revert "Use null instead of empty lists"
This reverts commit 775a52ca56
.
* Fix style (use AddRange instead of reassignment)
This commit is contained in:
parent
30ea8b728d
commit
86a12efa76
@ -35,6 +35,8 @@ namespace Bit.Api.Controllers
|
|||||||
private readonly IPaymentService _paymentService;
|
private readonly IPaymentService _paymentService;
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
|
private readonly ISendRepository _sendRepository;
|
||||||
|
private readonly ISendService _sendService;
|
||||||
|
|
||||||
public AccountsController(
|
public AccountsController(
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
@ -46,7 +48,9 @@ namespace Bit.Api.Controllers
|
|||||||
IPaymentService paymentService,
|
IPaymentService paymentService,
|
||||||
ISsoUserRepository ssoUserRepository,
|
ISsoUserRepository ssoUserRepository,
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
IUserService userService)
|
IUserService userService,
|
||||||
|
ISendRepository sendRepository,
|
||||||
|
ISendService sendService)
|
||||||
{
|
{
|
||||||
_cipherRepository = cipherRepository;
|
_cipherRepository = cipherRepository;
|
||||||
_folderRepository = folderRepository;
|
_folderRepository = folderRepository;
|
||||||
@ -57,6 +61,8 @@ namespace Bit.Api.Controllers
|
|||||||
_paymentService = paymentService;
|
_paymentService = paymentService;
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
|
_sendRepository = sendRepository;
|
||||||
|
_sendService = sendService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("prelogin")]
|
[HttpPost("prelogin")]
|
||||||
@ -283,26 +289,28 @@ namespace Bit.Api.Controllers
|
|||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var existingCiphers = await _cipherRepository.GetManyByUserIdAsync(user.Id);
|
|
||||||
var ciphersDict = model.Ciphers?.ToDictionary(c => c.Id.Value);
|
|
||||||
var ciphers = new List<Cipher>();
|
var ciphers = new List<Cipher>();
|
||||||
if (existingCiphers.Any() && ciphersDict != null)
|
if (model.Ciphers.Any())
|
||||||
{
|
{
|
||||||
foreach (var cipher in existingCiphers.Where(c => ciphersDict.ContainsKey(c.Id)))
|
var existingCiphers = await _cipherRepository.GetManyByUserIdAsync(user.Id);
|
||||||
{
|
ciphers.AddRange(existingCiphers
|
||||||
ciphers.Add(ciphersDict[cipher.Id].ToCipher(cipher));
|
.Join(model.Ciphers, c => c.Id, c => c.Id, (existing, c) => c.ToCipher(existing)));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var existingFolders = await _folderRepository.GetManyByUserIdAsync(user.Id);
|
|
||||||
var foldersDict = model.Folders?.ToDictionary(f => f.Id);
|
|
||||||
var folders = new List<Folder>();
|
var folders = new List<Folder>();
|
||||||
if (existingFolders.Any() && foldersDict != null)
|
if (model.Folders.Any())
|
||||||
{
|
{
|
||||||
foreach (var folder in existingFolders.Where(f => foldersDict.ContainsKey(f.Id)))
|
var existingFolders = await _folderRepository.GetManyByUserIdAsync(user.Id);
|
||||||
{
|
folders.AddRange(existingFolders
|
||||||
folders.Add(foldersDict[folder.Id].ToFolder(folder));
|
.Join(model.Folders, f => f.Id, f => f.Id, (existing, f) => f.ToFolder(existing)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sends = new List<Send>();
|
||||||
|
if (model.Sends?.Any() == true)
|
||||||
|
{
|
||||||
|
var existingSends = await _sendRepository.GetManyByUserIdAsync(user.Id);
|
||||||
|
sends.AddRange(existingSends
|
||||||
|
.Join(model.Sends, s => s.Id, s => s.Id, (existing, s) => s.ToSend(existing, _sendService)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await _userService.UpdateKeyAsync(
|
var result = await _userService.UpdateKeyAsync(
|
||||||
@ -311,7 +319,8 @@ namespace Bit.Api.Controllers
|
|||||||
model.Key,
|
model.Key,
|
||||||
model.PrivateKey,
|
model.PrivateKey,
|
||||||
ciphers,
|
ciphers,
|
||||||
folders);
|
folders,
|
||||||
|
sends);
|
||||||
|
|
||||||
if (result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
|
@ -12,6 +12,7 @@ namespace Bit.Core.Models.Api
|
|||||||
public IEnumerable<CipherWithIdRequestModel> Ciphers { get; set; }
|
public IEnumerable<CipherWithIdRequestModel> Ciphers { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
public IEnumerable<FolderWithIdRequestModel> Folders { get; set; }
|
public IEnumerable<FolderWithIdRequestModel> Folders { get; set; }
|
||||||
|
public IEnumerable<SendWithIdRequestModel> Sends { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
public string PrivateKey { get; set; }
|
public string PrivateKey { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
|
@ -130,4 +130,10 @@ namespace Bit.Core.Models.Api
|
|||||||
return existingSend;
|
return existingSend;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SendWithIdRequestModel : SendRequestModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public Guid? Id { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ namespace Bit.Core.Repositories
|
|||||||
Task MoveAsync(IEnumerable<Guid> ids, Guid? folderId, Guid userId);
|
Task MoveAsync(IEnumerable<Guid> ids, Guid? folderId, Guid userId);
|
||||||
Task DeleteByUserIdAsync(Guid userId);
|
Task DeleteByUserIdAsync(Guid userId);
|
||||||
Task DeleteByOrganizationIdAsync(Guid organizationId);
|
Task DeleteByOrganizationIdAsync(Guid organizationId);
|
||||||
Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders);
|
Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders, IEnumerable<Send> sends);
|
||||||
Task UpdateCiphersAsync(Guid userId, IEnumerable<Cipher> ciphers);
|
Task UpdateCiphersAsync(Guid userId, IEnumerable<Cipher> ciphers);
|
||||||
Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders);
|
Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders);
|
||||||
Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections,
|
Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections,
|
||||||
|
@ -282,7 +282,7 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders)
|
public Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders, IEnumerable<Send> sends)
|
||||||
{
|
{
|
||||||
using (var connection = new SqlConnection(ConnectionString))
|
using (var connection = new SqlConnection(ConnectionString))
|
||||||
{
|
{
|
||||||
@ -323,7 +323,11 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
|
|
||||||
SELECT TOP 0 *
|
SELECT TOP 0 *
|
||||||
INTO #TempFolder
|
INTO #TempFolder
|
||||||
FROM [dbo].[Folder]";
|
FROM [dbo].[Folder]
|
||||||
|
|
||||||
|
SELECT TOP 0 *
|
||||||
|
INTO #TempSend
|
||||||
|
FROM [dbo].[Send]";
|
||||||
|
|
||||||
using (var cmd = new SqlCommand(sqlCreateTemp, connection, transaction))
|
using (var cmd = new SqlCommand(sqlCreateTemp, connection, transaction))
|
||||||
{
|
{
|
||||||
@ -352,6 +356,16 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sends.Any())
|
||||||
|
{
|
||||||
|
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
|
||||||
|
{
|
||||||
|
bulkCopy.DestinationTableName = "#TempSend";
|
||||||
|
var dataTable = BuildSendsTable(bulkCopy, sends);
|
||||||
|
bulkCopy.WriteToServer(dataTable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Insert into real tables from temp tables and clean up.
|
// 4. Insert into real tables from temp tables and clean up.
|
||||||
|
|
||||||
var sql = string.Empty;
|
var sql = string.Empty;
|
||||||
@ -389,9 +403,26 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
F.[UserId] = @UserId";
|
F.[UserId] = @UserId";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sends.Any())
|
||||||
|
{
|
||||||
|
sql += @"
|
||||||
|
UPDATE
|
||||||
|
[dbo].[Send]
|
||||||
|
SET
|
||||||
|
[Key] = TS.[Key],
|
||||||
|
[RevisionDate] = TS.[RevisionDate]
|
||||||
|
FROM
|
||||||
|
[dbo].[Send] S
|
||||||
|
INNER JOIN
|
||||||
|
#TempSend TS ON S.Id = TS.Id
|
||||||
|
WHERE
|
||||||
|
S.[UserId] = @UserId";
|
||||||
|
}
|
||||||
|
|
||||||
sql += @"
|
sql += @"
|
||||||
DROP TABLE #TempCipher
|
DROP TABLE #TempCipher
|
||||||
DROP TABLE #TempFolder";
|
DROP TABLE #TempFolder
|
||||||
|
DROP TABLE #TempSend";
|
||||||
|
|
||||||
using (var cmd = new SqlCommand(sql, connection, transaction))
|
using (var cmd = new SqlCommand(sql, connection, transaction))
|
||||||
{
|
{
|
||||||
@ -833,6 +864,82 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
return collectionCiphersTable;
|
return collectionCiphersTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DataTable BuildSendsTable(SqlBulkCopy bulkCopy, IEnumerable<Send> sends)
|
||||||
|
{
|
||||||
|
var s = sends.FirstOrDefault();
|
||||||
|
if (s == null)
|
||||||
|
{
|
||||||
|
throw new ApplicationException("Must have some Sends to bulk import.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var sendsTable = new DataTable("SendsDataTable");
|
||||||
|
|
||||||
|
var idColumn = new DataColumn(nameof(s.Id), s.Id.GetType());
|
||||||
|
sendsTable.Columns.Add(idColumn);
|
||||||
|
var userIdColumn = new DataColumn(nameof(s.UserId), typeof(Guid));
|
||||||
|
sendsTable.Columns.Add(userIdColumn);
|
||||||
|
var organizationIdColumn = new DataColumn(nameof(s.OrganizationId), typeof(Guid));
|
||||||
|
sendsTable.Columns.Add(organizationIdColumn);
|
||||||
|
var typeColumn = new DataColumn(nameof(s.Type), s.Type.GetType());
|
||||||
|
sendsTable.Columns.Add(typeColumn);
|
||||||
|
var dataColumn = new DataColumn(nameof(s.Data), s.Data.GetType());
|
||||||
|
sendsTable.Columns.Add(dataColumn);
|
||||||
|
var keyColumn = new DataColumn(nameof(s.Key), s.Key.GetType());
|
||||||
|
sendsTable.Columns.Add(keyColumn);
|
||||||
|
var passwordColumn = new DataColumn(nameof(s.Password), typeof(string));
|
||||||
|
sendsTable.Columns.Add(passwordColumn);
|
||||||
|
var maxAccessCountColumn = new DataColumn(nameof(s.MaxAccessCount), typeof(int));
|
||||||
|
sendsTable.Columns.Add(maxAccessCountColumn);
|
||||||
|
var accessCountColumn = new DataColumn(nameof(s.AccessCount), s.AccessCount.GetType());
|
||||||
|
sendsTable.Columns.Add(accessCountColumn);
|
||||||
|
var creationDateColumn = new DataColumn(nameof(s.CreationDate), s.CreationDate.GetType());
|
||||||
|
sendsTable.Columns.Add(creationDateColumn);
|
||||||
|
var revisionDateColumn = new DataColumn(nameof(s.RevisionDate), s.RevisionDate.GetType());
|
||||||
|
sendsTable.Columns.Add(revisionDateColumn);
|
||||||
|
var expirationDateColumn = new DataColumn(nameof(s.ExpirationDate), typeof(DateTime));
|
||||||
|
sendsTable.Columns.Add(expirationDateColumn);
|
||||||
|
var deletionDateColumn = new DataColumn(nameof(s.DeletionDate), s.DeletionDate.GetType());
|
||||||
|
sendsTable.Columns.Add(deletionDateColumn);
|
||||||
|
var disabledColumn = new DataColumn(nameof(s.Disabled), s.Disabled.GetType());
|
||||||
|
sendsTable.Columns.Add(disabledColumn);
|
||||||
|
var hideEmailColumn = new DataColumn(nameof(s.HideEmail), typeof(bool));
|
||||||
|
sendsTable.Columns.Add(hideEmailColumn);
|
||||||
|
|
||||||
|
foreach (DataColumn col in sendsTable.Columns)
|
||||||
|
{
|
||||||
|
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = new DataColumn[1];
|
||||||
|
keys[0] = idColumn;
|
||||||
|
sendsTable.PrimaryKey = keys;
|
||||||
|
|
||||||
|
foreach (var send in sends)
|
||||||
|
{
|
||||||
|
var row = sendsTable.NewRow();
|
||||||
|
|
||||||
|
row[idColumn] = send.Id;
|
||||||
|
row[userIdColumn] = send.UserId.HasValue ? (object)send.UserId.Value : DBNull.Value;
|
||||||
|
row[organizationIdColumn] = send.OrganizationId.HasValue ? (object)send.OrganizationId.Value : DBNull.Value;
|
||||||
|
row[typeColumn] = (short)send.Type;
|
||||||
|
row[dataColumn] = send.Data;
|
||||||
|
row[keyColumn] = send.Key;
|
||||||
|
row[passwordColumn] = send.Password;
|
||||||
|
row[maxAccessCountColumn] = send.MaxAccessCount.HasValue ? (object)send.MaxAccessCount : DBNull.Value;
|
||||||
|
row[accessCountColumn] = send.AccessCount;
|
||||||
|
row[creationDateColumn] = send.CreationDate;
|
||||||
|
row[revisionDateColumn] = send.RevisionDate;
|
||||||
|
row[expirationDateColumn] = send.ExpirationDate.HasValue ? (object)send.ExpirationDate : DBNull.Value;
|
||||||
|
row[deletionDateColumn] = send.DeletionDate;
|
||||||
|
row[disabledColumn] = send.Disabled;
|
||||||
|
row[hideEmailColumn] = send.HideEmail.HasValue ? (object)send.HideEmail : DBNull.Value;
|
||||||
|
|
||||||
|
sendsTable.Rows.Add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sendsTable;
|
||||||
|
}
|
||||||
|
|
||||||
public class CipherDetailsWithCollections : CipherDetails
|
public class CipherDetailsWithCollections : CipherDetails
|
||||||
{
|
{
|
||||||
public DataTable CollectionIds { get; set; }
|
public DataTable CollectionIds { get; set; }
|
||||||
|
@ -38,7 +38,7 @@ namespace Bit.Core.Services
|
|||||||
Task<IdentityResult> ChangeKdfAsync(User user, string masterPassword, string newMasterPassword, string key,
|
Task<IdentityResult> ChangeKdfAsync(User user, string masterPassword, string newMasterPassword, string key,
|
||||||
KdfType kdf, int kdfIterations);
|
KdfType kdf, int kdfIterations);
|
||||||
Task<IdentityResult> UpdateKeyAsync(User user, string masterPassword, string key, string privateKey,
|
Task<IdentityResult> UpdateKeyAsync(User user, string masterPassword, string key, string privateKey,
|
||||||
IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders);
|
IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders, IEnumerable<Send> sends);
|
||||||
Task<IdentityResult> RefreshSecurityStampAsync(User user, string masterPasswordHash);
|
Task<IdentityResult> RefreshSecurityStampAsync(User user, string masterPasswordHash);
|
||||||
Task UpdateTwoFactorProviderAsync(User user, TwoFactorProviderType type, bool setEnabled = true);
|
Task UpdateTwoFactorProviderAsync(User user, TwoFactorProviderType type, bool setEnabled = true);
|
||||||
Task DisableTwoFactorProviderAsync(User user, TwoFactorProviderType type,
|
Task DisableTwoFactorProviderAsync(User user, TwoFactorProviderType type,
|
||||||
|
@ -52,6 +52,7 @@ namespace Bit.Core.Services
|
|||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
private readonly IOrganizationService _organizationService;
|
private readonly IOrganizationService _organizationService;
|
||||||
|
private readonly ISendRepository _sendRepository;
|
||||||
|
|
||||||
public UserService(
|
public UserService(
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
@ -79,7 +80,8 @@ namespace Bit.Core.Services
|
|||||||
IFido2 fido2,
|
IFido2 fido2,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
IOrganizationService organizationService)
|
IOrganizationService organizationService,
|
||||||
|
ISendRepository sendRepository)
|
||||||
: base(
|
: base(
|
||||||
store,
|
store,
|
||||||
optionsAccessor,
|
optionsAccessor,
|
||||||
@ -113,6 +115,7 @@ namespace Bit.Core.Services
|
|||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
_organizationService = organizationService;
|
_organizationService = organizationService;
|
||||||
|
_sendRepository = sendRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid? GetProperUserId(ClaimsPrincipal principal)
|
public Guid? GetProperUserId(ClaimsPrincipal principal)
|
||||||
@ -726,7 +729,7 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IdentityResult> UpdateKeyAsync(User user, string masterPassword, string key, string privateKey,
|
public async Task<IdentityResult> UpdateKeyAsync(User user, string masterPassword, string key, string privateKey,
|
||||||
IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders)
|
IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders, IEnumerable<Send> sends)
|
||||||
{
|
{
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
@ -739,9 +742,9 @@ namespace Bit.Core.Services
|
|||||||
user.SecurityStamp = Guid.NewGuid().ToString();
|
user.SecurityStamp = Guid.NewGuid().ToString();
|
||||||
user.Key = key;
|
user.Key = key;
|
||||||
user.PrivateKey = privateKey;
|
user.PrivateKey = privateKey;
|
||||||
if (ciphers.Any() || folders.Any())
|
if (ciphers.Any() || folders.Any() || sends.Any())
|
||||||
{
|
{
|
||||||
await _cipherRepository.UpdateUserKeysAndCiphersAsync(user, ciphers, folders);
|
await _cipherRepository.UpdateUserKeysAndCiphersAsync(user, ciphers, folders, sends);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -30,6 +30,8 @@ namespace Bit.Api.Test.Controllers
|
|||||||
private readonly ISsoUserRepository _ssoUserRepository;
|
private readonly ISsoUserRepository _ssoUserRepository;
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
|
private readonly ISendRepository _sendRepository;
|
||||||
|
private readonly ISendService _sendService;
|
||||||
private readonly IProviderUserRepository _providerUserRepository;
|
private readonly IProviderUserRepository _providerUserRepository;
|
||||||
|
|
||||||
public AccountsControllerTests()
|
public AccountsControllerTests()
|
||||||
@ -43,6 +45,8 @@ namespace Bit.Api.Test.Controllers
|
|||||||
_providerUserRepository = Substitute.For<IProviderUserRepository>();
|
_providerUserRepository = Substitute.For<IProviderUserRepository>();
|
||||||
_paymentService = Substitute.For<IPaymentService>();
|
_paymentService = Substitute.For<IPaymentService>();
|
||||||
_globalSettings = new GlobalSettings();
|
_globalSettings = new GlobalSettings();
|
||||||
|
_sendRepository = Substitute.For<ISendRepository>();
|
||||||
|
_sendService = Substitute.For<ISendService>();
|
||||||
_sut = new AccountsController(
|
_sut = new AccountsController(
|
||||||
_globalSettings,
|
_globalSettings,
|
||||||
_cipherRepository,
|
_cipherRepository,
|
||||||
@ -53,7 +57,9 @@ namespace Bit.Api.Test.Controllers
|
|||||||
_paymentService,
|
_paymentService,
|
||||||
_ssoUserRepository,
|
_ssoUserRepository,
|
||||||
_userRepository,
|
_userRepository,
|
||||||
_userService
|
_userService,
|
||||||
|
_sendRepository,
|
||||||
|
_sendService
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ namespace Bit.Core.Test.Services
|
|||||||
private readonly CurrentContext _currentContext;
|
private readonly CurrentContext _currentContext;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
private readonly IOrganizationService _organizationService;
|
private readonly IOrganizationService _organizationService;
|
||||||
|
private readonly ISendRepository _sendRepository;
|
||||||
|
|
||||||
public UserServiceTests()
|
public UserServiceTests()
|
||||||
{
|
{
|
||||||
@ -74,6 +75,7 @@ namespace Bit.Core.Test.Services
|
|||||||
_currentContext = new CurrentContext();
|
_currentContext = new CurrentContext();
|
||||||
_globalSettings = new GlobalSettings();
|
_globalSettings = new GlobalSettings();
|
||||||
_organizationService = Substitute.For<IOrganizationService>();
|
_organizationService = Substitute.For<IOrganizationService>();
|
||||||
|
_sendRepository = Substitute.For<ISendRepository>();
|
||||||
|
|
||||||
_sut = new UserService(
|
_sut = new UserService(
|
||||||
_userRepository,
|
_userRepository,
|
||||||
@ -101,7 +103,8 @@ namespace Bit.Core.Test.Services
|
|||||||
_fido2,
|
_fido2,
|
||||||
_currentContext,
|
_currentContext,
|
||||||
_globalSettings,
|
_globalSettings,
|
||||||
_organizationService
|
_organizationService,
|
||||||
|
_sendRepository
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user