You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
625 lines
25 KiB
625 lines
25 KiB
using Bit.Core; |
|
using Bit.Core.AdminConsole.Repositories; |
|
using Bit.Core.Entities; |
|
using Bit.Core.Enums; |
|
using Bit.Core.KeyManagement.UserKey; |
|
using Bit.Core.Models.Data; |
|
using Bit.Core.Repositories; |
|
using Bit.Infrastructure.IntegrationTest.AdminConsole; |
|
using Microsoft.Data.SqlClient; |
|
using Xunit; |
|
|
|
namespace Bit.Infrastructure.IntegrationTest.Repositories; |
|
|
|
public class UserRepositoryTests |
|
{ |
|
[DatabaseTheory, DatabaseData] |
|
public async Task DeleteAsync_Works(IUserRepository userRepository) |
|
{ |
|
var user = await userRepository.CreateAsync(new User |
|
{ |
|
Name = "Test User", |
|
Email = $"test+{Guid.NewGuid()}@example.com", |
|
ApiKey = "TEST", |
|
SecurityStamp = "stamp", |
|
}); |
|
|
|
await userRepository.DeleteAsync(user); |
|
|
|
var deletedUser = await userRepository.GetByIdAsync(user.Id); |
|
Assert.Null(deletedUser); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task DeleteManyAsync_Works(IUserRepository userRepository, IOrganizationUserRepository organizationUserRepository, IOrganizationRepository organizationRepository, ICollectionRepository collectionRepository, IGroupRepository groupRepository) |
|
{ |
|
var user1 = await userRepository.CreateTestUserAsync(); |
|
var user2 = await userRepository.CreateTestUserAsync(); |
|
var user3 = await userRepository.CreateTestUserAsync(); |
|
|
|
var organization = await organizationRepository.CreateTestOrganizationAsync(); |
|
var orgUser1 = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user1); |
|
var orgUser3 = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user3); |
|
|
|
var group1 = await groupRepository.CreateTestGroupAsync(organization, "test-group-1"); |
|
var group2 = await groupRepository.CreateTestGroupAsync(organization, "test-group-2"); |
|
await groupRepository.UpdateUsersAsync(group1.Id, [orgUser1.Id]); |
|
await groupRepository.UpdateUsersAsync(group2.Id, [orgUser3.Id]); |
|
|
|
var collection1 = new Collection |
|
{ |
|
OrganizationId = organization.Id, |
|
Name = "test-collection-1" |
|
}; |
|
var collection2 = new Collection |
|
{ |
|
OrganizationId = organization.Id, |
|
Name = "test-collection-2" |
|
}; |
|
|
|
await collectionRepository.CreateAsync( |
|
collection1, |
|
groups: [new CollectionAccessSelection { Id = group1.Id, HidePasswords = false, ReadOnly = false, Manage = true }], |
|
users: [new CollectionAccessSelection { Id = orgUser1.Id, HidePasswords = false, ReadOnly = false, Manage = true }]); |
|
await collectionRepository.CreateAsync(collection2, |
|
groups: [new CollectionAccessSelection { Id = group2.Id, HidePasswords = false, ReadOnly = false, Manage = true }], |
|
users: [new CollectionAccessSelection { Id = orgUser3.Id, HidePasswords = false, ReadOnly = false, Manage = true }]); |
|
|
|
await userRepository.DeleteManyAsync(new List<User> |
|
{ |
|
user1, |
|
user2 |
|
}); |
|
|
|
var deletedUser1 = await userRepository.GetByIdAsync(user1.Id); |
|
var deletedUser2 = await userRepository.GetByIdAsync(user2.Id); |
|
var notDeletedUser3 = await userRepository.GetByIdAsync(user3.Id); |
|
|
|
var orgUser1Deleted = await organizationUserRepository.GetByIdAsync(user1.Id); |
|
|
|
var notDeletedOrgUsers = await organizationUserRepository.GetManyByUserAsync(user3.Id); |
|
|
|
Assert.Null(deletedUser1); |
|
Assert.Null(deletedUser2); |
|
Assert.NotNull(notDeletedUser3); |
|
|
|
Assert.Null(orgUser1Deleted); |
|
Assert.NotNull(notDeletedOrgUsers); |
|
Assert.True(notDeletedOrgUsers.Count > 0); |
|
|
|
var collection1WithUsers = await collectionRepository.GetByIdWithPermissionsAsync(collection1.Id, null, true); |
|
var collection2WithUsers = await collectionRepository.GetByIdWithPermissionsAsync(collection2.Id, null, true); |
|
Assert.Empty(collection1WithUsers.Users); // Collection1 should have no users (orgUser1 was deleted) |
|
Assert.Single(collection2WithUsers.Users); // Collection2 should still have orgUser3 (not deleted) |
|
Assert.Single(collection2WithUsers.Users); |
|
Assert.Equal(orgUser3.Id, collection2WithUsers.Users.First().Id); |
|
|
|
var group1Users = await groupRepository.GetManyUserIdsByIdAsync(group1.Id); |
|
var group2Users = await groupRepository.GetManyUserIdsByIdAsync(group2.Id); |
|
|
|
Assert.Empty(group1Users); // Group1 should have no users (orgUser1 was deleted) |
|
Assert.Single(group2Users); // Group2 should still have orgUser3 (not deleted) |
|
Assert.Equal(orgUser3.Id, group2Users.First()); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task DeleteAsync_WhenUserHasDefaultUserCollections_MigratesToSharedCollection( |
|
IUserRepository userRepository, |
|
IOrganizationRepository organizationRepository, |
|
IOrganizationUserRepository organizationUserRepository, |
|
ICollectionRepository collectionRepository) |
|
{ |
|
var user = await userRepository.CreateTestUserAsync(); |
|
var organization = await organizationRepository.CreateTestOrganizationAsync(); |
|
var orgUser = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user); |
|
|
|
var defaultUserCollection = new Collection |
|
{ |
|
Name = "Test Collection", |
|
Type = CollectionType.DefaultUserCollection, |
|
OrganizationId = organization.Id |
|
}; |
|
await collectionRepository.CreateAsync( |
|
defaultUserCollection, |
|
groups: null, |
|
users: [new CollectionAccessSelection { Id = orgUser.Id, HidePasswords = false, ReadOnly = false, Manage = true }]); |
|
|
|
await userRepository.DeleteAsync(user); |
|
|
|
var deletedUser = await userRepository.GetByIdAsync(user.Id); |
|
Assert.Null(deletedUser); |
|
|
|
var updatedCollection = await collectionRepository.GetByIdAsync(defaultUserCollection.Id); |
|
Assert.NotNull(updatedCollection); |
|
Assert.Equal(CollectionType.SharedCollection, updatedCollection.Type); |
|
Assert.Equal(user.Email, updatedCollection.DefaultUserCollectionEmail); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task DeleteManyAsync_WhenUsersHaveDefaultUserCollections_MigratesToSharedCollection( |
|
IUserRepository userRepository, |
|
IOrganizationRepository organizationRepository, |
|
IOrganizationUserRepository organizationUserRepository, |
|
ICollectionRepository collectionRepository) |
|
{ |
|
var user1 = await userRepository.CreateTestUserAsync(); |
|
var user2 = await userRepository.CreateTestUserAsync(); |
|
var organization = await organizationRepository.CreateTestOrganizationAsync(); |
|
var orgUser1 = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user1); |
|
var orgUser2 = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user2); |
|
|
|
var defaultUserCollection1 = new Collection |
|
{ |
|
Name = "Test Collection 1", |
|
Type = CollectionType.DefaultUserCollection, |
|
OrganizationId = organization.Id |
|
}; |
|
|
|
var defaultUserCollection2 = new Collection |
|
{ |
|
Name = "Test Collection 2", |
|
Type = CollectionType.DefaultUserCollection, |
|
OrganizationId = organization.Id |
|
}; |
|
|
|
await collectionRepository.CreateAsync(defaultUserCollection1, groups: null, users: [new CollectionAccessSelection { Id = orgUser1.Id, HidePasswords = false, ReadOnly = false, Manage = true }]); |
|
await collectionRepository.CreateAsync(defaultUserCollection2, groups: null, users: [new CollectionAccessSelection { Id = orgUser2.Id, HidePasswords = false, ReadOnly = false, Manage = true }]); |
|
|
|
await userRepository.DeleteManyAsync([user1, user2]); |
|
|
|
var deletedUser1 = await userRepository.GetByIdAsync(user1.Id); |
|
var deletedUser2 = await userRepository.GetByIdAsync(user2.Id); |
|
Assert.Null(deletedUser1); |
|
Assert.Null(deletedUser2); |
|
|
|
var updatedCollection1 = await collectionRepository.GetByIdAsync(defaultUserCollection1.Id); |
|
Assert.NotNull(updatedCollection1); |
|
Assert.Equal(CollectionType.SharedCollection, updatedCollection1.Type); |
|
Assert.Equal(user1.Email, updatedCollection1.DefaultUserCollectionEmail); |
|
|
|
var updatedCollection2 = await collectionRepository.GetByIdAsync(defaultUserCollection2.Id); |
|
Assert.NotNull(updatedCollection2); |
|
Assert.Equal(CollectionType.SharedCollection, updatedCollection2.Type); |
|
Assert.Equal(user2.Email, updatedCollection2.DefaultUserCollectionEmail); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task GetPremiumAccessAsync_WithPersonalPremium_ReturnsCorrectAccess( |
|
IUserRepository userRepository) |
|
{ |
|
// Arrange |
|
var user = await userRepository.CreateAsync(new User |
|
{ |
|
Name = "Premium User", |
|
Email = $"premium+{Guid.NewGuid()}@example.com", |
|
ApiKey = "TEST", |
|
SecurityStamp = "stamp", |
|
Premium = true |
|
}); |
|
|
|
// Act |
|
var result = await userRepository.GetPremiumAccessAsync(user.Id); |
|
|
|
// Assert |
|
Assert.NotNull(result); |
|
Assert.True(result.PersonalPremium); |
|
Assert.False(result.OrganizationPremium); |
|
Assert.True(result.HasPremiumAccess); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task GetPremiumAccessAsync_WithOrganizationPremium_ReturnsCorrectAccess( |
|
IUserRepository userRepository, |
|
IOrganizationRepository organizationRepository, |
|
IOrganizationUserRepository organizationUserRepository) |
|
{ |
|
// Arrange |
|
var user = await userRepository.CreateAsync(new User |
|
{ |
|
Name = "Org User", |
|
Email = $"org+{Guid.NewGuid()}@example.com", |
|
ApiKey = "TEST", |
|
SecurityStamp = "stamp", |
|
Premium = false |
|
}); |
|
|
|
var organization = await organizationRepository.CreateTestOrganizationAsync(); |
|
await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user); |
|
|
|
// Act |
|
var result = await userRepository.GetPremiumAccessAsync(user.Id); |
|
|
|
// Assert |
|
Assert.NotNull(result); |
|
Assert.False(result.PersonalPremium); |
|
Assert.True(result.OrganizationPremium); |
|
Assert.True(result.HasPremiumAccess); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task GetPremiumAccessAsync_WithDisabledOrganization_ReturnsNoOrganizationPremium( |
|
IUserRepository userRepository, |
|
IOrganizationRepository organizationRepository, |
|
IOrganizationUserRepository organizationUserRepository) |
|
{ |
|
// Arrange |
|
var user = await userRepository.CreateAsync(new User |
|
{ |
|
Name = "User", |
|
Email = $"user+{Guid.NewGuid()}@example.com", |
|
ApiKey = "TEST", |
|
SecurityStamp = "stamp", |
|
Premium = false |
|
}); |
|
|
|
var organization = await organizationRepository.CreateTestOrganizationAsync(); |
|
organization.Enabled = false; |
|
await organizationRepository.ReplaceAsync(organization); |
|
await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user); |
|
|
|
// Act |
|
var result = await userRepository.GetPremiumAccessAsync(user.Id); |
|
|
|
// Assert |
|
Assert.NotNull(result); |
|
Assert.False(result.OrganizationPremium); |
|
Assert.False(result.HasPremiumAccess); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task GetPremiumAccessAsync_WithOrganizationUsersGetPremiumFalse_ReturnsNoOrganizationPremium( |
|
IUserRepository userRepository, |
|
IOrganizationRepository organizationRepository, |
|
IOrganizationUserRepository organizationUserRepository) |
|
{ |
|
// Arrange |
|
var user = await userRepository.CreateAsync(new User |
|
{ |
|
Name = "User", |
|
Email = $"{Guid.NewGuid()}@example.com", |
|
ApiKey = "TEST", |
|
SecurityStamp = "stamp", |
|
Premium = false |
|
}); |
|
|
|
var organization = await organizationRepository.CreateTestOrganizationAsync(); |
|
organization.UsersGetPremium = false; |
|
await organizationRepository.ReplaceAsync(organization); |
|
await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user); |
|
|
|
// Act |
|
var result = await userRepository.GetPremiumAccessAsync(user.Id); |
|
|
|
// Assert |
|
Assert.NotNull(result); |
|
Assert.False(result.OrganizationPremium); |
|
Assert.False(result.HasPremiumAccess); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task GetPremiumAccessAsync_WithMultipleOrganizations_OneProvidesPremium_ReturnsOrganizationPremium( |
|
IUserRepository userRepository, |
|
IOrganizationRepository organizationRepository, |
|
IOrganizationUserRepository organizationUserRepository) |
|
{ |
|
// Arrange |
|
var user = await userRepository.CreateAsync(new User |
|
{ |
|
Name = "User With Premium Org", |
|
Email = $"{Guid.NewGuid()}@example.com", |
|
ApiKey = "TEST", |
|
SecurityStamp = "stamp", |
|
Premium = false |
|
}); |
|
|
|
var orgWithPremium = await organizationRepository.CreateTestOrganizationAsync(); |
|
await organizationUserRepository.CreateTestOrganizationUserAsync(orgWithPremium, user); |
|
|
|
var orgNoPremium = await organizationRepository.CreateTestOrganizationAsync(); |
|
orgNoPremium.UsersGetPremium = false; |
|
await organizationRepository.ReplaceAsync(orgNoPremium); |
|
await organizationUserRepository.CreateTestOrganizationUserAsync(orgNoPremium, user); |
|
|
|
// Act |
|
var result = await userRepository.GetPremiumAccessAsync(user.Id); |
|
|
|
// Assert |
|
Assert.NotNull(result); |
|
Assert.False(result.PersonalPremium); |
|
Assert.True(result.OrganizationPremium); |
|
Assert.True(result.HasPremiumAccess); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task GetPremiumAccessAsync_WithMultipleOrganizations_NoneProvidePremium_ReturnsNoOrganizationPremium( |
|
IUserRepository userRepository, |
|
IOrganizationRepository organizationRepository, |
|
IOrganizationUserRepository organizationUserRepository) |
|
{ |
|
// Arrange |
|
var user = await userRepository.CreateAsync(new User |
|
{ |
|
Name = "User With No Premium Orgs", |
|
Email = $"{Guid.NewGuid()}@example.com", |
|
ApiKey = "TEST", |
|
SecurityStamp = "stamp", |
|
Premium = false |
|
}); |
|
|
|
var disabledOrg = await organizationRepository.CreateTestOrganizationAsync(); |
|
disabledOrg.Enabled = false; |
|
await organizationRepository.ReplaceAsync(disabledOrg); |
|
await organizationUserRepository.CreateTestOrganizationUserAsync(disabledOrg, user); |
|
|
|
var orgNoPremium = await organizationRepository.CreateTestOrganizationAsync(); |
|
orgNoPremium.UsersGetPremium = false; |
|
await organizationRepository.ReplaceAsync(orgNoPremium); |
|
await organizationUserRepository.CreateTestOrganizationUserAsync(orgNoPremium, user); |
|
|
|
// Act |
|
var result = await userRepository.GetPremiumAccessAsync(user.Id); |
|
|
|
// Assert |
|
Assert.NotNull(result); |
|
Assert.False(result.PersonalPremium); |
|
Assert.False(result.OrganizationPremium); |
|
Assert.False(result.HasPremiumAccess); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task GetPremiumAccessAsync_NonExistentUser_ReturnsNull( |
|
IUserRepository userRepository) |
|
{ |
|
// Act |
|
var result = await userRepository.GetPremiumAccessAsync(Guid.NewGuid()); |
|
|
|
// Assert |
|
Assert.Null(result); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task GetPremiumAccessByIdsAsync_MultipleUsers_ReturnsCorrectAccessForEach( |
|
IUserRepository userRepository, |
|
IOrganizationRepository organizationRepository, |
|
IOrganizationUserRepository organizationUserRepository) |
|
{ |
|
// Arrange |
|
var personalPremiumUser = await userRepository.CreateAsync(new User |
|
{ |
|
Name = "Personal Premium", |
|
Email = $"{Guid.NewGuid()}@example.com", |
|
ApiKey = "TEST", |
|
SecurityStamp = "stamp", |
|
Premium = true |
|
}); |
|
|
|
var orgPremiumUser = await userRepository.CreateAsync(new User |
|
{ |
|
Name = "Org Premium", |
|
Email = $"{Guid.NewGuid()}@example.com", |
|
ApiKey = "TEST", |
|
SecurityStamp = "stamp", |
|
Premium = false |
|
}); |
|
|
|
var bothPremiumUser = await userRepository.CreateAsync(new User |
|
{ |
|
Name = "Both Premium", |
|
Email = $"{Guid.NewGuid()}@example.com", |
|
ApiKey = "TEST", |
|
SecurityStamp = "stamp", |
|
Premium = true |
|
}); |
|
|
|
var noPremiumUser = await userRepository.CreateAsync(new User |
|
{ |
|
Name = "No Premium", |
|
Email = $"{Guid.NewGuid()}@example.com", |
|
ApiKey = "TEST", |
|
SecurityStamp = "stamp", |
|
Premium = false |
|
}); |
|
|
|
var multiOrgUser = await userRepository.CreateAsync(new User |
|
{ |
|
Name = "Multi Org User", |
|
Email = $"{Guid.NewGuid()}@example.com", |
|
ApiKey = "TEST", |
|
SecurityStamp = "stamp", |
|
Premium = false |
|
}); |
|
|
|
var personalPremiumWithDisabledOrg = await userRepository.CreateAsync(new User |
|
{ |
|
Name = "Personal Premium With Disabled Org", |
|
Email = $"{Guid.NewGuid()}@example.com", |
|
ApiKey = "TEST", |
|
SecurityStamp = "stamp", |
|
Premium = true |
|
}); |
|
|
|
var organization = await organizationRepository.CreateTestOrganizationAsync(); |
|
await organizationUserRepository.CreateTestOrganizationUserAsync(organization, orgPremiumUser); |
|
await organizationUserRepository.CreateTestOrganizationUserAsync(organization, bothPremiumUser); |
|
await organizationUserRepository.CreateTestOrganizationUserAsync(organization, multiOrgUser); |
|
|
|
var orgWithoutPremium = await organizationRepository.CreateTestOrganizationAsync(); |
|
orgWithoutPremium.UsersGetPremium = false; |
|
await organizationRepository.ReplaceAsync(orgWithoutPremium); |
|
await organizationUserRepository.CreateTestOrganizationUserAsync(orgWithoutPremium, multiOrgUser); |
|
|
|
var disabledOrg = await organizationRepository.CreateTestOrganizationAsync(); |
|
disabledOrg.Enabled = false; |
|
await organizationRepository.ReplaceAsync(disabledOrg); |
|
await organizationUserRepository.CreateTestOrganizationUserAsync(disabledOrg, personalPremiumWithDisabledOrg); |
|
|
|
// Act |
|
var results = await userRepository.GetPremiumAccessByIdsAsync([ |
|
personalPremiumUser.Id, |
|
orgPremiumUser.Id, |
|
bothPremiumUser.Id, |
|
noPremiumUser.Id, |
|
multiOrgUser.Id, |
|
personalPremiumWithDisabledOrg.Id |
|
]); |
|
|
|
var resultsList = results.ToList(); |
|
|
|
// Assert |
|
Assert.Equal(6, resultsList.Count); |
|
|
|
var personalResult = resultsList.First(r => r.Id == personalPremiumUser.Id); |
|
Assert.True(personalResult.PersonalPremium); |
|
Assert.False(personalResult.OrganizationPremium); |
|
|
|
var orgResult = resultsList.First(r => r.Id == orgPremiumUser.Id); |
|
Assert.False(orgResult.PersonalPremium); |
|
Assert.True(orgResult.OrganizationPremium); |
|
|
|
var bothResult = resultsList.First(r => r.Id == bothPremiumUser.Id); |
|
Assert.True(bothResult.PersonalPremium); |
|
Assert.True(bothResult.OrganizationPremium); |
|
|
|
var noneResult = resultsList.First(r => r.Id == noPremiumUser.Id); |
|
Assert.False(noneResult.PersonalPremium); |
|
Assert.False(noneResult.OrganizationPremium); |
|
|
|
var multiResult = resultsList.First(r => r.Id == multiOrgUser.Id); |
|
Assert.False(multiResult.PersonalPremium); |
|
Assert.True(multiResult.OrganizationPremium); |
|
|
|
var personalWithDisabledOrgResult = resultsList.First(r => r.Id == personalPremiumWithDisabledOrg.Id); |
|
Assert.True(personalWithDisabledOrgResult.PersonalPremium); |
|
Assert.False(personalWithDisabledOrgResult.OrganizationPremium); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task GetPremiumAccessByIdsAsync_EmptyList_ReturnsEmptyResult( |
|
IUserRepository userRepository) |
|
{ |
|
// Act |
|
var results = await userRepository.GetPremiumAccessByIdsAsync([]); |
|
|
|
// Assert |
|
Assert.Empty(results); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task SetKeyConnectorUserKey_UpdatesUserKey(IUserRepository userRepository, Database database) |
|
{ |
|
var user = await userRepository.CreateTestUserAsync(); |
|
|
|
const string keyConnectorWrappedKey = "key-connector-wrapped-user-key"; |
|
|
|
var setKeyConnectorUserKeyDelegate = userRepository.SetKeyConnectorUserKey(user.Id, keyConnectorWrappedKey); |
|
|
|
await RunUpdateUserDataAsync(setKeyConnectorUserKeyDelegate, database); |
|
|
|
var updatedUser = await userRepository.GetByIdAsync(user.Id); |
|
|
|
Assert.NotNull(updatedUser); |
|
Assert.Equal(keyConnectorWrappedKey, updatedUser.Key); |
|
Assert.True(updatedUser.UsesKeyConnector); |
|
Assert.Equal(KdfType.Argon2id, updatedUser.Kdf); |
|
Assert.Equal(AuthConstants.ARGON2_ITERATIONS.Default, updatedUser.KdfIterations); |
|
Assert.Equal(AuthConstants.ARGON2_MEMORY.Default, updatedUser.KdfMemory); |
|
Assert.Equal(AuthConstants.ARGON2_PARALLELISM.Default, updatedUser.KdfParallelism); |
|
Assert.Equal(DateTime.UtcNow, updatedUser.RevisionDate, TimeSpan.FromMinutes(1)); |
|
Assert.Equal(DateTime.UtcNow, updatedUser.AccountRevisionDate, TimeSpan.FromMinutes(1)); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task UpdateUserKeyAndEncryptedDataV2Async_UpdatesAllUserFields(IUserRepository userRepository) |
|
{ |
|
// Arrange |
|
var user = await userRepository.CreateTestUserAsync(); |
|
|
|
var newSecurityStamp = Guid.NewGuid().ToString(); |
|
user.Key = "new-user-key"; |
|
user.PrivateKey = "new-private-key"; |
|
user.SecurityStamp = newSecurityStamp; |
|
user.Kdf = KdfType.Argon2id; |
|
user.KdfIterations = 3; |
|
user.KdfMemory = 64; |
|
user.KdfParallelism = 4; |
|
user.Email = $"updated+{Guid.NewGuid()}@example.com"; |
|
user.MasterPassword = "new-master-password-hash"; |
|
user.MasterPasswordHint = "new-hint"; |
|
user.LastKeyRotationDate = DateTime.UtcNow; |
|
user.RevisionDate = DateTime.UtcNow; |
|
user.AccountRevisionDate = DateTime.UtcNow; |
|
user.SignedPublicKey = "new-signed-public-key"; |
|
user.SecurityState = "new-security-state"; |
|
user.SecurityVersion = 2; |
|
user.V2UpgradeToken = null; |
|
|
|
// Act |
|
await userRepository.UpdateUserKeyAndEncryptedDataV2Async(user, []); |
|
|
|
// Assert |
|
var updatedUser = await userRepository.GetByIdAsync(user.Id); |
|
Assert.NotNull(updatedUser); |
|
Assert.Equal("new-user-key", updatedUser.Key); |
|
Assert.Equal("new-private-key", updatedUser.PrivateKey); |
|
Assert.Equal(newSecurityStamp, updatedUser.SecurityStamp); |
|
Assert.Equal(KdfType.Argon2id, updatedUser.Kdf); |
|
Assert.Equal(3, updatedUser.KdfIterations); |
|
Assert.Equal(64, updatedUser.KdfMemory); |
|
Assert.Equal(4, updatedUser.KdfParallelism); |
|
Assert.Equal(user.Email, updatedUser.Email); |
|
Assert.Equal("new-master-password-hash", updatedUser.MasterPassword); |
|
Assert.Equal("new-hint", updatedUser.MasterPasswordHint); |
|
Assert.Equal("new-signed-public-key", updatedUser.SignedPublicKey); |
|
Assert.Equal("new-security-state", updatedUser.SecurityState); |
|
Assert.Equal(2, updatedUser.SecurityVersion); |
|
Assert.Null(updatedUser.V2UpgradeToken); |
|
Assert.Equal(DateTime.UtcNow, updatedUser.RevisionDate, TimeSpan.FromMinutes(1)); |
|
} |
|
|
|
[Theory, DatabaseData] |
|
public async Task UpdateUserKeyAndEncryptedDataV2Async_InvokesUpdateDataActions(IUserRepository userRepository) |
|
{ |
|
// Arrange |
|
var user = await userRepository.CreateTestUserAsync(); |
|
user.RevisionDate = DateTime.UtcNow; |
|
|
|
var actionWasInvoked = false; |
|
UpdateEncryptedDataForKeyRotation action = (_, _) => |
|
{ |
|
actionWasInvoked = true; |
|
return Task.CompletedTask; |
|
}; |
|
|
|
// Act |
|
await userRepository.UpdateUserKeyAndEncryptedDataV2Async(user, [action]); |
|
|
|
// Assert |
|
Assert.True(actionWasInvoked); |
|
} |
|
|
|
private static async Task RunUpdateUserDataAsync(UpdateUserData task, Database database) |
|
{ |
|
if (database.Type == SupportedDatabaseProviders.SqlServer && !database.UseEf) |
|
{ |
|
await using var connection = new SqlConnection(database.ConnectionString); |
|
connection.Open(); |
|
|
|
await using var transaction = connection.BeginTransaction(); |
|
try |
|
{ |
|
await task(connection, transaction); |
|
|
|
transaction.Commit(); |
|
} |
|
catch |
|
{ |
|
transaction.Rollback(); |
|
throw; |
|
} |
|
} |
|
else |
|
{ |
|
await task(); |
|
} |
|
} |
|
}
|
|
|