1
0
mirror of https://github.com/bitwarden/server synced 2026-01-13 05:53:37 +00:00

Merge branch 'main' of github.com:bitwarden/server into arch/seeder-api

This commit is contained in:
Hinton
2026-01-08 11:15:13 +01:00
500 changed files with 54684 additions and 3482 deletions

View File

@@ -95,6 +95,7 @@ public static class OrganizationTestHelpers
SyncSeats = false,
UseAutomaticUserConfirmation = true,
UsePhishingBlocker = true,
UseDisableSmAdsForUsers = true,
});
}

View File

@@ -144,4 +144,69 @@ public class CollectionRepositoryReplaceTests
await userRepository.DeleteAsync(user);
await organizationRepository.DeleteAsync(organization);
}
[Theory, DatabaseData]
public async Task ReplaceAsync_WhenNotPassingGroupsOrUsers_DoesNotDeleteAccess(
IUserRepository userRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IGroupRepository groupRepository,
ICollectionRepository collectionRepository)
{
// Arrange
var organization = await organizationRepository.CreateTestOrganizationAsync();
var user1 = await userRepository.CreateTestUserAsync();
var orgUser1 = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user1);
var user2 = await userRepository.CreateTestUserAsync();
var orgUser2 = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user2);
var group1 = await groupRepository.CreateTestGroupAsync(organization);
var group2 = await groupRepository.CreateTestGroupAsync(organization);
var collection = new Collection
{
Name = "Test Collection Name",
OrganizationId = organization.Id,
};
await collectionRepository.CreateAsync(collection,
[
new CollectionAccessSelection { Id = group1.Id, Manage = true, HidePasswords = true, ReadOnly = false, },
new CollectionAccessSelection { Id = group2.Id, Manage = false, HidePasswords = false, ReadOnly = true, },
],
[
new CollectionAccessSelection { Id = orgUser1.Id, Manage = true, HidePasswords = false, ReadOnly = true },
new CollectionAccessSelection { Id = orgUser2.Id, Manage = false, HidePasswords = true, ReadOnly = false },
]
);
// Act
collection.Name = "Updated Collection Name";
await collectionRepository.ReplaceAsync(collection, null, null);
// Assert
var (actualCollection, actualAccess) = await collectionRepository.GetByIdWithAccessAsync(collection.Id);
Assert.NotNull(actualCollection);
Assert.Equal("Updated Collection Name", actualCollection.Name);
var groups = actualAccess.Groups.ToArray();
Assert.Equal(2, groups.Length);
Assert.Single(groups, g => g.Id == group1.Id && g.Manage && g.HidePasswords && !g.ReadOnly);
Assert.Single(groups, g => g.Id == group2.Id && !g.Manage && !g.HidePasswords && g.ReadOnly);
var users = actualAccess.Users.ToArray();
Assert.Equal(2, users.Length);
Assert.Single(users, u => u.Id == orgUser1.Id && u.Manage && !u.HidePasswords && u.ReadOnly);
Assert.Single(users, u => u.Id == orgUser2.Id && !u.Manage && u.HidePasswords && !u.ReadOnly);
// Clean up data
await userRepository.DeleteAsync(user1);
await userRepository.DeleteAsync(user2);
await organizationRepository.DeleteAsync(organization);
}
}

View File

@@ -675,6 +675,7 @@ public class OrganizationUserRepositoryTests
UseRiskInsights = false,
UseAdminSponsoredFamilies = false,
UsePhishingBlocker = false,
UseDisableSmAdsForUsers = false,
});
var organizationDomain = new OrganizationDomain

View File

@@ -1,9 +1,11 @@
using Bit.Core.AdminConsole.Repositories;
using Bit.Core;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Entities;
using Bit.Core.Enums;
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;
@@ -500,4 +502,54 @@ public class UserRepositoryTests
// 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));
}
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();
}
}
}

View File

@@ -1207,10 +1207,110 @@ public class CipherRepositoryTests
// Act
await sutRepository.ArchiveAsync(new List<Guid> { cipher.Id }, user.Id);
// Assert
var archivedCipher = await sutRepository.GetByIdAsync(cipher.Id, user.Id);
Assert.NotNull(archivedCipher);
Assert.NotNull(archivedCipher.ArchivedDate);
// Assert per-user view should show an archive date
var archivedCipherForUser = await sutRepository.GetByIdAsync(cipher.Id, user.Id);
Assert.NotNull(archivedCipherForUser);
Assert.NotNull(archivedCipherForUser.ArchivedDate);
}
[DatabaseTheory, DatabaseData]
public async Task ArchiveAsync_IsPerUserForSharedCipher(
ICipherRepository cipherRepository,
IUserRepository userRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
ICollectionRepository collectionRepository,
ICollectionCipherRepository collectionCipherRepository)
{
// Arrange: two users in the same org, both with access to the same cipher
var user1 = await userRepository.CreateAsync(new User
{
Name = "Test User 1",
Email = $"test+{Guid.NewGuid()}@email.com",
ApiKey = "TEST",
SecurityStamp = "stamp",
});
var user2 = await userRepository.CreateAsync(new User
{
Name = "Test User 2",
Email = $"test+{Guid.NewGuid()}@email.com",
ApiKey = "TEST",
SecurityStamp = "stamp",
});
var org = await organizationRepository.CreateAsync(new Organization
{
Name = "Test Organization",
BillingEmail = user1.Email,
Plan = "Test",
});
var orgUser1 = await organizationUserRepository.CreateAsync(new OrganizationUser
{
UserId = user1.Id,
OrganizationId = org.Id,
Status = OrganizationUserStatusType.Confirmed,
Type = OrganizationUserType.Owner,
});
var orgUser2 = await organizationUserRepository.CreateAsync(new OrganizationUser
{
UserId = user2.Id,
OrganizationId = org.Id,
Status = OrganizationUserStatusType.Confirmed,
Type = OrganizationUserType.User,
});
var sharedCollection = await collectionRepository.CreateAsync(new Collection
{
Name = "Shared Collection",
OrganizationId = org.Id,
});
var cipher = await cipherRepository.CreateAsync(new Cipher
{
Type = CipherType.Login,
OrganizationId = org.Id,
Data = "",
});
await collectionCipherRepository.UpdateCollectionsForAdminAsync(
cipher.Id,
org.Id,
new List<Guid> { sharedCollection.Id });
// Give both org users access to the shared collection
await collectionRepository.UpdateUsersAsync(sharedCollection.Id, new List<CollectionAccessSelection>
{
new()
{
Id = orgUser1.Id,
HidePasswords = false,
ReadOnly = false,
Manage = true,
},
new()
{
Id = orgUser2.Id,
HidePasswords = false,
ReadOnly = false,
Manage = true,
},
});
// Act: user1 archives the shared cipher
await cipherRepository.ArchiveAsync(new List<Guid> { cipher.Id }, user1.Id);
// Assert: user1 sees it as archived
var cipherForUser1 = await cipherRepository.GetByIdAsync(cipher.Id, user1.Id);
Assert.NotNull(cipherForUser1);
Assert.NotNull(cipherForUser1.ArchivedDate);
// Assert: user2 still sees it as *not* archived
var cipherForUser2 = await cipherRepository.GetByIdAsync(cipher.Id, user2.Id);
Assert.NotNull(cipherForUser2);
Assert.Null(cipherForUser2.ArchivedDate);
}
[DatabaseTheory, DatabaseData]