mirror of
https://github.com/bitwarden/server.git
synced 2024-11-25 12:45:18 +01:00
[PM-7029] Remove conditional logic for KeyRotationImprovements feature flag (#4002)
* Removed business logic that references flag * Removed using statement. * Undid accidental keystroke. * Removed unused method. * Removed unused imports.
This commit is contained in:
parent
479f8319c2
commit
7f9d7c0c5d
@ -43,7 +43,6 @@ using Bit.Core.Utilities;
|
|||||||
using Bit.Core.Vault.Entities;
|
using Bit.Core.Vault.Entities;
|
||||||
using Bit.Core.Vault.Repositories;
|
using Bit.Core.Vault.Repositories;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Identity;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Bit.Api.Auth.Controllers;
|
namespace Bit.Api.Auth.Controllers;
|
||||||
@ -438,59 +437,19 @@ public class AccountsController : Controller
|
|||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
|
|
||||||
IdentityResult result;
|
var dataModel = new RotateUserKeyData
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.KeyRotationImprovements))
|
|
||||||
{
|
{
|
||||||
var dataModel = new RotateUserKeyData
|
MasterPasswordHash = model.MasterPasswordHash,
|
||||||
{
|
Key = model.Key,
|
||||||
MasterPasswordHash = model.MasterPasswordHash,
|
PrivateKey = model.PrivateKey,
|
||||||
Key = model.Key,
|
Ciphers = await _cipherValidator.ValidateAsync(user, model.Ciphers),
|
||||||
PrivateKey = model.PrivateKey,
|
Folders = await _folderValidator.ValidateAsync(user, model.Folders),
|
||||||
Ciphers = await _cipherValidator.ValidateAsync(user, model.Ciphers),
|
Sends = await _sendValidator.ValidateAsync(user, model.Sends),
|
||||||
Folders = await _folderValidator.ValidateAsync(user, model.Folders),
|
EmergencyAccesses = await _emergencyAccessValidator.ValidateAsync(user, model.EmergencyAccessKeys),
|
||||||
Sends = await _sendValidator.ValidateAsync(user, model.Sends),
|
OrganizationUsers = await _organizationUserValidator.ValidateAsync(user, model.ResetPasswordKeys)
|
||||||
EmergencyAccesses = await _emergencyAccessValidator.ValidateAsync(user, model.EmergencyAccessKeys),
|
};
|
||||||
OrganizationUsers = await _organizationUserValidator.ValidateAsync(user, model.ResetPasswordKeys)
|
|
||||||
};
|
|
||||||
|
|
||||||
result = await _rotateUserKeyCommand.RotateUserKeyAsync(user, dataModel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var ciphers = new List<Cipher>();
|
|
||||||
if (model.Ciphers.Any())
|
|
||||||
{
|
|
||||||
var existingCiphers = await _cipherRepository.GetManyByUserIdAsync(user.Id, useFlexibleCollections: UseFlexibleCollections);
|
|
||||||
ciphers.AddRange(existingCiphers
|
|
||||||
.Join(model.Ciphers, c => c.Id, c => c.Id, (existing, c) => c.ToCipher(existing)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var folders = new List<Folder>();
|
|
||||||
if (model.Folders.Any())
|
|
||||||
{
|
|
||||||
var existingFolders = await _folderRepository.GetManyByUserIdAsync(user.Id);
|
|
||||||
folders.AddRange(existingFolders
|
|
||||||
.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)));
|
|
||||||
}
|
|
||||||
|
|
||||||
result = await _userService.UpdateKeyAsync(
|
|
||||||
user,
|
|
||||||
model.MasterPasswordHash,
|
|
||||||
model.Key,
|
|
||||||
model.PrivateKey,
|
|
||||||
ciphers,
|
|
||||||
folders,
|
|
||||||
sends);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var result = await _rotateUserKeyCommand.RotateUserKeyAsync(user, dataModel);
|
||||||
|
|
||||||
if (result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
|
@ -4,8 +4,6 @@ using Bit.Core.Auth.Models;
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Tools.Entities;
|
|
||||||
using Bit.Core.Vault.Entities;
|
|
||||||
using Fido2NetLib;
|
using Fido2NetLib;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
@ -39,8 +37,6 @@ public interface IUserService
|
|||||||
Task<IdentityResult> UpdateTempPasswordAsync(User user, string newMasterPassword, string key, string hint);
|
Task<IdentityResult> UpdateTempPasswordAsync(User user, string newMasterPassword, string key, string hint);
|
||||||
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, int? kdfMemory, int? kdfParallelism);
|
KdfType kdf, int kdfIterations, int? kdfMemory, int? kdfParallelism);
|
||||||
Task<IdentityResult> UpdateKeyAsync(User user, string masterPassword, string key, string privateKey,
|
|
||||||
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, bool logEvent = true);
|
Task UpdateTwoFactorProviderAsync(User user, TwoFactorProviderType type, bool setEnabled = true, bool logEvent = true);
|
||||||
Task DisableTwoFactorProviderAsync(User user, TwoFactorProviderType type,
|
Task DisableTwoFactorProviderAsync(User user, TwoFactorProviderType type,
|
||||||
|
@ -14,12 +14,10 @@ using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
|
|||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
using Bit.Core.Tokens;
|
using Bit.Core.Tokens;
|
||||||
using Bit.Core.Tools.Entities;
|
|
||||||
using Bit.Core.Tools.Enums;
|
using Bit.Core.Tools.Enums;
|
||||||
using Bit.Core.Tools.Models.Business;
|
using Bit.Core.Tools.Models.Business;
|
||||||
using Bit.Core.Tools.Services;
|
using Bit.Core.Tools.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Core.Vault.Entities;
|
|
||||||
using Bit.Core.Vault.Repositories;
|
using Bit.Core.Vault.Repositories;
|
||||||
using Fido2NetLib;
|
using Fido2NetLib;
|
||||||
using Fido2NetLib.Objects;
|
using Fido2NetLib.Objects;
|
||||||
@ -862,39 +860,6 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
return IdentityResult.Failed(_identityErrorDescriber.PasswordMismatch());
|
return IdentityResult.Failed(_identityErrorDescriber.PasswordMismatch());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IdentityResult> UpdateKeyAsync(User user, string masterPassword, string key, string privateKey,
|
|
||||||
IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders, IEnumerable<Send> sends)
|
|
||||||
{
|
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(user));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await CheckPasswordAsync(user, masterPassword))
|
|
||||||
{
|
|
||||||
var now = DateTime.UtcNow;
|
|
||||||
user.RevisionDate = user.AccountRevisionDate = now;
|
|
||||||
user.LastKeyRotationDate = now;
|
|
||||||
user.SecurityStamp = Guid.NewGuid().ToString();
|
|
||||||
user.Key = key;
|
|
||||||
user.PrivateKey = privateKey;
|
|
||||||
if (ciphers.Any() || folders.Any() || sends.Any())
|
|
||||||
{
|
|
||||||
await _cipherRepository.UpdateUserKeysAndCiphersAsync(user, ciphers, folders, sends);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _userRepository.ReplaceAsync(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
await _pushService.PushLogOutAsync(user.Id, excludeCurrentContextFromPush: true);
|
|
||||||
return IdentityResult.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.LogWarning("Update key failed for user {userId}.", user.Id);
|
|
||||||
return IdentityResult.Failed(_identityErrorDescriber.PasswordMismatch());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IdentityResult> RefreshSecurityStampAsync(User user, string secret)
|
public async Task<IdentityResult> RefreshSecurityStampAsync(User user, string secret)
|
||||||
{
|
{
|
||||||
if (user == null)
|
if (user == null)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using Bit.Core.Auth.UserFeatures.UserKey;
|
using Bit.Core.Auth.UserFeatures.UserKey;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Tools.Entities;
|
|
||||||
using Bit.Core.Vault.Entities;
|
using Bit.Core.Vault.Entities;
|
||||||
using Bit.Core.Vault.Models.Data;
|
using Bit.Core.Vault.Models.Data;
|
||||||
|
|
||||||
@ -30,7 +29,6 @@ public interface ICipherRepository : IRepository<Cipher, Guid>
|
|||||||
Task MoveAsync(IEnumerable<Guid> ids, Guid? folderId, Guid userId, bool useFlexibleCollections);
|
Task MoveAsync(IEnumerable<Guid> ids, Guid? folderId, Guid userId, bool useFlexibleCollections);
|
||||||
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, 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,
|
||||||
|
@ -380,170 +380,6 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders, IEnumerable<Send> sends)
|
|
||||||
{
|
|
||||||
using (var connection = new SqlConnection(ConnectionString))
|
|
||||||
{
|
|
||||||
connection.Open();
|
|
||||||
|
|
||||||
using (var transaction = connection.BeginTransaction())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 1. Update user.
|
|
||||||
|
|
||||||
using (var cmd = new SqlCommand("[dbo].[User_UpdateKeys]", connection, transaction))
|
|
||||||
{
|
|
||||||
cmd.CommandType = CommandType.StoredProcedure;
|
|
||||||
cmd.Parameters.Add("@Id", SqlDbType.UniqueIdentifier).Value = user.Id;
|
|
||||||
cmd.Parameters.Add("@SecurityStamp", SqlDbType.NVarChar).Value = user.SecurityStamp;
|
|
||||||
cmd.Parameters.Add("@Key", SqlDbType.VarChar).Value = user.Key;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(user.PrivateKey))
|
|
||||||
{
|
|
||||||
cmd.Parameters.Add("@PrivateKey", SqlDbType.VarChar).Value = DBNull.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cmd.Parameters.Add("@PrivateKey", SqlDbType.VarChar).Value = user.PrivateKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Parameters.Add("@RevisionDate", SqlDbType.DateTime2).Value = user.RevisionDate;
|
|
||||||
cmd.Parameters.Add("@AccountRevisionDate", SqlDbType.DateTime2).Value = user.AccountRevisionDate;
|
|
||||||
cmd.Parameters.Add("@LastKeyRotationDate", SqlDbType.DateTime2).Value = user.LastKeyRotationDate;
|
|
||||||
cmd.ExecuteNonQuery();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Create temp tables to bulk copy into.
|
|
||||||
|
|
||||||
var sqlCreateTemp = @"
|
|
||||||
SELECT TOP 0 *
|
|
||||||
INTO #TempCipher
|
|
||||||
FROM [dbo].[Cipher]
|
|
||||||
|
|
||||||
SELECT TOP 0 *
|
|
||||||
INTO #TempFolder
|
|
||||||
FROM [dbo].[Folder]
|
|
||||||
|
|
||||||
SELECT TOP 0 *
|
|
||||||
INTO #TempSend
|
|
||||||
FROM [dbo].[Send]";
|
|
||||||
|
|
||||||
using (var cmd = new SqlCommand(sqlCreateTemp, connection, transaction))
|
|
||||||
{
|
|
||||||
cmd.ExecuteNonQuery();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Bulk copy into temp tables.
|
|
||||||
|
|
||||||
if (ciphers.Any())
|
|
||||||
{
|
|
||||||
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
|
|
||||||
{
|
|
||||||
bulkCopy.DestinationTableName = "#TempCipher";
|
|
||||||
var dataTable = BuildCiphersTable(bulkCopy, ciphers);
|
|
||||||
bulkCopy.WriteToServer(dataTable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (folders.Any())
|
|
||||||
{
|
|
||||||
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
|
|
||||||
{
|
|
||||||
bulkCopy.DestinationTableName = "#TempFolder";
|
|
||||||
var dataTable = BuildFoldersTable(bulkCopy, folders);
|
|
||||||
bulkCopy.WriteToServer(dataTable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
var sql = string.Empty;
|
|
||||||
|
|
||||||
if (ciphers.Any())
|
|
||||||
{
|
|
||||||
sql += @"
|
|
||||||
UPDATE
|
|
||||||
[dbo].[Cipher]
|
|
||||||
SET
|
|
||||||
[Data] = TC.[Data],
|
|
||||||
[Attachments] = TC.[Attachments],
|
|
||||||
[RevisionDate] = TC.[RevisionDate],
|
|
||||||
[Key] = TC.[Key]
|
|
||||||
FROM
|
|
||||||
[dbo].[Cipher] C
|
|
||||||
INNER JOIN
|
|
||||||
#TempCipher TC ON C.Id = TC.Id
|
|
||||||
WHERE
|
|
||||||
C.[UserId] = @UserId";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (folders.Any())
|
|
||||||
{
|
|
||||||
sql += @"
|
|
||||||
UPDATE
|
|
||||||
[dbo].[Folder]
|
|
||||||
SET
|
|
||||||
[Name] = TF.[Name],
|
|
||||||
[RevisionDate] = TF.[RevisionDate]
|
|
||||||
FROM
|
|
||||||
[dbo].[Folder] F
|
|
||||||
INNER JOIN
|
|
||||||
#TempFolder TF ON F.Id = TF.Id
|
|
||||||
WHERE
|
|
||||||
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 += @"
|
|
||||||
DROP TABLE #TempCipher
|
|
||||||
DROP TABLE #TempFolder
|
|
||||||
DROP TABLE #TempSend";
|
|
||||||
|
|
||||||
using (var cmd = new SqlCommand(sql, connection, transaction))
|
|
||||||
{
|
|
||||||
cmd.Parameters.Add("@UserId", SqlDbType.UniqueIdentifier).Value = user.Id;
|
|
||||||
cmd.ExecuteNonQuery();
|
|
||||||
}
|
|
||||||
|
|
||||||
transaction.Commit();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
transaction.Rollback();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UpdateCiphersAsync(Guid userId, IEnumerable<Cipher> ciphers)
|
public async Task UpdateCiphersAsync(Guid userId, IEnumerable<Cipher> ciphers)
|
||||||
{
|
{
|
||||||
if (!ciphers.Any())
|
if (!ciphers.Any())
|
||||||
|
@ -19,7 +19,6 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using NS = Newtonsoft.Json;
|
using NS = Newtonsoft.Json;
|
||||||
using NSL = Newtonsoft.Json.Linq;
|
using NSL = Newtonsoft.Json.Linq;
|
||||||
using User = Bit.Core.Entities.User;
|
|
||||||
|
|
||||||
namespace Bit.Infrastructure.EntityFramework.Vault.Repositories;
|
namespace Bit.Infrastructure.EntityFramework.Vault.Repositories;
|
||||||
|
|
||||||
@ -865,23 +864,6 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable<Core.Vault.Entities.Cipher> ciphers, IEnumerable<Core.Vault.Entities.Folder> folders, IEnumerable<Core.Tools.Entities.Send> sends)
|
|
||||||
{
|
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
|
||||||
{
|
|
||||||
var dbContext = GetDatabaseContext(scope);
|
|
||||||
await UserUpdateKeys(user);
|
|
||||||
var cipherEntities = Mapper.Map<List<Cipher>>(ciphers);
|
|
||||||
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, cipherEntities);
|
|
||||||
var folderEntities = Mapper.Map<List<Folder>>(folders);
|
|
||||||
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, folderEntities);
|
|
||||||
var sendEntities = Mapper.Map<List<Send>>(sends);
|
|
||||||
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, sendEntities);
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UpsertAsync(CipherDetails cipher)
|
public async Task UpsertAsync(CipherDetails cipher)
|
||||||
{
|
{
|
||||||
if (cipher.Id.Equals(default))
|
if (cipher.Id.Equals(default))
|
||||||
|
Loading…
Reference in New Issue
Block a user