mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 15:42:48 -05:00
Merge branch 'main' into auth/pm-20348/extension-auth-approvals-add-auth-request-endpoint
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Repositories;
|
||||
@ -26,15 +27,23 @@ public static class OrganizationTestHelpers
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an Enterprise organization.
|
||||
/// </summary>
|
||||
public static Task<Organization> CreateTestOrganizationAsync(this IOrganizationRepository organizationRepository,
|
||||
string identifier = "test")
|
||||
=> organizationRepository.CreateAsync(new Organization
|
||||
{
|
||||
Name = $"{identifier}-{Guid.NewGuid()}",
|
||||
BillingEmail = "billing@example.com", // TODO: EF does not enforce this being NOT NULL
|
||||
Plan = "Test", // TODO: EF does not enforce this being NOT NULl
|
||||
Plan = "Enterprise (Annually)", // TODO: EF does not enforce this being NOT NULl
|
||||
PlanType = PlanType.EnterpriseAnnually
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Creates a confirmed Owner for the specified organization and user.
|
||||
/// Does not include any cryptographic material.
|
||||
/// </summary>
|
||||
public static Task<OrganizationUser> CreateTestOrganizationUserAsync(
|
||||
this IOrganizationUserRepository organizationUserRepository,
|
||||
Organization organization,
|
||||
@ -47,6 +56,17 @@ public static class OrganizationTestHelpers
|
||||
Type = OrganizationUserType.Owner
|
||||
});
|
||||
|
||||
public static Task<OrganizationUser> CreateTestOrganizationUserInviteAsync(
|
||||
this IOrganizationUserRepository organizationUserRepository,
|
||||
Organization organization)
|
||||
=> organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = null, // Invites are not linked to a UserId
|
||||
Status = OrganizationUserStatusType.Invited,
|
||||
Type = OrganizationUserType.Owner
|
||||
});
|
||||
|
||||
public static Task<Group> CreateTestGroupAsync(
|
||||
this IGroupRepository groupRepository,
|
||||
Organization organization,
|
||||
@ -54,4 +74,14 @@ public static class OrganizationTestHelpers
|
||||
=> groupRepository.CreateAsync(
|
||||
new Group { OrganizationId = organization.Id, Name = $"{identifier} {Guid.NewGuid()}" }
|
||||
);
|
||||
|
||||
public static Task<Collection> CreateTestCollectionAsync(
|
||||
this ICollectionRepository collectionRepository,
|
||||
Organization organization,
|
||||
string identifier = "test")
|
||||
=> collectionRepository.CreateAsync(new Collection
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
Name = $"{identifier} {Guid.NewGuid()}"
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,105 @@
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Infrastructure.IntegrationTest.AdminConsole.Repositories.CollectionRepository;
|
||||
|
||||
public class CollectionRepositoryCreateTests
|
||||
{
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task CreateAsync_WithAccess_Works(
|
||||
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,
|
||||
};
|
||||
|
||||
// Act
|
||||
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 },
|
||||
]
|
||||
);
|
||||
|
||||
// Assert
|
||||
var (actualCollection, actualAccess) = await collectionRepository.GetByIdWithAccessAsync(collection.Id);
|
||||
|
||||
Assert.NotNull(actualCollection);
|
||||
Assert.Equal("Test 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);
|
||||
await groupRepository.DeleteManyAsync([group1.Id, group2.Id]);
|
||||
await organizationUserRepository.DeleteManyAsync([orgUser1.Id, orgUser2.Id]);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Makes sure that the sproc handles empty sets.
|
||||
/// </remarks>
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task CreateAsync_WithNoAccess_Works(
|
||||
IOrganizationRepository organizationRepository,
|
||||
ICollectionRepository collectionRepository)
|
||||
{
|
||||
// Arrange
|
||||
var organization = await organizationRepository.CreateTestOrganizationAsync();
|
||||
|
||||
var collection = new Collection
|
||||
{
|
||||
Name = "Test Collection Name",
|
||||
OrganizationId = organization.Id,
|
||||
};
|
||||
|
||||
// Act
|
||||
await collectionRepository.CreateAsync(collection, [], []);
|
||||
|
||||
// Assert
|
||||
var (actualCollection, actualAccess) = await collectionRepository.GetByIdWithAccessAsync(collection.Id);
|
||||
|
||||
Assert.NotNull(actualCollection);
|
||||
Assert.Equal("Test Collection Name", actualCollection.Name);
|
||||
|
||||
Assert.Empty(actualAccess.Groups);
|
||||
Assert.Empty(actualAccess.Users);
|
||||
|
||||
// Clean up
|
||||
await organizationRepository.DeleteAsync(organization);
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Infrastructure.IntegrationTest.AdminConsole.Repositories.CollectionRepository;
|
||||
|
||||
public class CollectionRepositoryReplaceTests
|
||||
{
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task ReplaceAsync_WithAccess_Works(
|
||||
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 user3 = await userRepository.CreateTestUserAsync();
|
||||
var orgUser3 = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user3);
|
||||
|
||||
var group1 = await groupRepository.CreateTestGroupAsync(organization);
|
||||
var group2 = await groupRepository.CreateTestGroupAsync(organization);
|
||||
var group3 = 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,
|
||||
[
|
||||
// Delete group1
|
||||
// Update group2:
|
||||
new CollectionAccessSelection { Id = group2.Id, Manage = true, HidePasswords = true, ReadOnly = false, },
|
||||
// Add group3:
|
||||
new CollectionAccessSelection { Id = group3.Id, Manage = false, HidePasswords = false, ReadOnly = true, },
|
||||
],
|
||||
[
|
||||
// Delete orgUser1
|
||||
// Update orgUser2:
|
||||
new CollectionAccessSelection { Id = orgUser2.Id, Manage = false, HidePasswords = false, ReadOnly = true },
|
||||
// Add orgUser3:
|
||||
new CollectionAccessSelection { Id = orgUser3.Id, Manage = true, HidePasswords = false, ReadOnly = true },
|
||||
]
|
||||
);
|
||||
|
||||
// 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 == group2.Id && g.Manage && g.HidePasswords && !g.ReadOnly);
|
||||
Assert.Single(groups, g => g.Id == group3.Id && !g.Manage && !g.HidePasswords && g.ReadOnly);
|
||||
|
||||
var users = actualAccess.Users.ToArray();
|
||||
|
||||
Assert.Equal(2, users.Length);
|
||||
Assert.Single(users, u => u.Id == orgUser2.Id && !u.Manage && !u.HidePasswords && u.ReadOnly);
|
||||
Assert.Single(users, u => u.Id == orgUser3.Id && u.Manage && !u.HidePasswords && u.ReadOnly);
|
||||
|
||||
// Clean up data
|
||||
await userRepository.DeleteAsync(user1);
|
||||
await userRepository.DeleteAsync(user2);
|
||||
await userRepository.DeleteAsync(user3);
|
||||
await organizationRepository.DeleteAsync(organization);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Makes sure that the sproc handles empty sets.
|
||||
/// </remarks>
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task ReplaceAsync_WithNoAccess_Works(
|
||||
IUserRepository userRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IGroupRepository groupRepository,
|
||||
ICollectionRepository collectionRepository)
|
||||
{
|
||||
// Arrange
|
||||
var organization = await organizationRepository.CreateTestOrganizationAsync();
|
||||
|
||||
var user = await userRepository.CreateTestUserAsync();
|
||||
var orgUser = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user);
|
||||
|
||||
var group = await groupRepository.CreateTestGroupAsync(organization);
|
||||
|
||||
var collection = new Collection
|
||||
{
|
||||
Name = "Test Collection Name",
|
||||
OrganizationId = organization.Id,
|
||||
};
|
||||
|
||||
await collectionRepository.CreateAsync(collection,
|
||||
[
|
||||
new CollectionAccessSelection { Id = group.Id, Manage = true, HidePasswords = false, ReadOnly = true },
|
||||
],
|
||||
[
|
||||
new CollectionAccessSelection { Id = orgUser.Id, Manage = true, HidePasswords = false, ReadOnly = true },
|
||||
]);
|
||||
|
||||
// Act
|
||||
collection.Name = "Updated Collection Name";
|
||||
|
||||
await collectionRepository.ReplaceAsync(collection, [], []);
|
||||
|
||||
// Assert
|
||||
var (actualCollection, actualAccess) = await collectionRepository.GetByIdWithAccessAsync(collection.Id);
|
||||
|
||||
Assert.NotNull(actualCollection);
|
||||
Assert.Equal("Updated Collection Name", actualCollection.Name);
|
||||
|
||||
Assert.Empty(actualAccess.Groups);
|
||||
Assert.Empty(actualAccess.Users);
|
||||
|
||||
// Clean up
|
||||
await userRepository.DeleteAsync(user);
|
||||
await organizationRepository.DeleteAsync(organization);
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Infrastructure.IntegrationTest.Repositories;
|
||||
namespace Bit.Infrastructure.IntegrationTest.AdminConsole.Repositories.CollectionRepository;
|
||||
|
||||
public class CollectionRepositoryTests
|
||||
{
|
||||
@ -463,147 +463,4 @@ public class CollectionRepositoryTests
|
||||
Assert.False(c3.Unmanaged);
|
||||
});
|
||||
}
|
||||
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task ReplaceAsync_Works(
|
||||
IUserRepository userRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IGroupRepository groupRepository,
|
||||
ICollectionRepository collectionRepository)
|
||||
{
|
||||
var user = await userRepository.CreateAsync(new User
|
||||
{
|
||||
Name = "Test User",
|
||||
Email = $"test+{Guid.NewGuid()}@email.com",
|
||||
ApiKey = "TEST",
|
||||
SecurityStamp = "stamp",
|
||||
});
|
||||
|
||||
var organization = await organizationRepository.CreateAsync(new Organization
|
||||
{
|
||||
Name = "Test Org",
|
||||
PlanType = PlanType.EnterpriseAnnually,
|
||||
Plan = "Test Plan",
|
||||
BillingEmail = "billing@email.com"
|
||||
});
|
||||
|
||||
var orgUser1 = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user.Id,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
});
|
||||
|
||||
var orgUser2 = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user.Id,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
});
|
||||
|
||||
var orgUser3 = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user.Id,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
});
|
||||
|
||||
var group1 = await groupRepository.CreateAsync(new Group
|
||||
{
|
||||
Name = "Test Group #1",
|
||||
OrganizationId = organization.Id,
|
||||
});
|
||||
|
||||
var group2 = await groupRepository.CreateAsync(new Group
|
||||
{
|
||||
Name = "Test Group #2",
|
||||
OrganizationId = organization.Id,
|
||||
});
|
||||
|
||||
var group3 = await groupRepository.CreateAsync(new Group
|
||||
{
|
||||
Name = "Test Group #3",
|
||||
OrganizationId = organization.Id,
|
||||
});
|
||||
|
||||
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 },
|
||||
]
|
||||
);
|
||||
|
||||
collection.Name = "Updated Collection Name";
|
||||
|
||||
await collectionRepository.ReplaceAsync(collection,
|
||||
[
|
||||
// Should delete group1
|
||||
new CollectionAccessSelection { Id = group2.Id, Manage = true, HidePasswords = true, ReadOnly = false, },
|
||||
// Should add group3
|
||||
new CollectionAccessSelection { Id = group3.Id, Manage = false, HidePasswords = false, ReadOnly = true, },
|
||||
],
|
||||
[
|
||||
// Should delete orgUser1
|
||||
new CollectionAccessSelection { Id = orgUser2.Id, Manage = false, HidePasswords = false, ReadOnly = true },
|
||||
// Should add orgUser3
|
||||
new CollectionAccessSelection { Id = orgUser3.Id, Manage = true, HidePasswords = false, ReadOnly = true },
|
||||
]
|
||||
);
|
||||
|
||||
// Assert it
|
||||
var info = await collectionRepository.GetByIdWithPermissionsAsync(collection.Id, user.Id, true);
|
||||
|
||||
Assert.NotNull(info);
|
||||
|
||||
Assert.Equal("Updated Collection Name", info.Name);
|
||||
|
||||
var groups = info.Groups.ToArray();
|
||||
|
||||
Assert.Equal(2, groups.Length);
|
||||
|
||||
var actualGroup2 = Assert.Single(groups.Where(g => g.Id == group2.Id));
|
||||
|
||||
Assert.True(actualGroup2.Manage);
|
||||
Assert.True(actualGroup2.HidePasswords);
|
||||
Assert.False(actualGroup2.ReadOnly);
|
||||
|
||||
var actualGroup3 = Assert.Single(groups.Where(g => g.Id == group3.Id));
|
||||
|
||||
Assert.False(actualGroup3.Manage);
|
||||
Assert.False(actualGroup3.HidePasswords);
|
||||
Assert.True(actualGroup3.ReadOnly);
|
||||
|
||||
var users = info.Users.ToArray();
|
||||
|
||||
Assert.Equal(2, users.Length);
|
||||
|
||||
var actualOrgUser2 = Assert.Single(users.Where(u => u.Id == orgUser2.Id));
|
||||
|
||||
Assert.False(actualOrgUser2.Manage);
|
||||
Assert.False(actualOrgUser2.HidePasswords);
|
||||
Assert.True(actualOrgUser2.ReadOnly);
|
||||
|
||||
var actualOrgUser3 = Assert.Single(users.Where(u => u.Id == orgUser3.Id));
|
||||
|
||||
Assert.True(actualOrgUser3.Manage);
|
||||
Assert.False(actualOrgUser3.HidePasswords);
|
||||
Assert.True(actualOrgUser3.ReadOnly);
|
||||
|
||||
// Clean up data
|
||||
await userRepository.DeleteAsync(user);
|
||||
await organizationRepository.DeleteAsync(organization);
|
||||
await groupRepository.DeleteManyAsync([group1.Id, group2.Id, group3.Id]);
|
||||
await organizationUserRepository.DeleteManyAsync([orgUser1.Id, orgUser2.Id, orgUser3.Id]);
|
||||
}
|
||||
}
|
@ -286,4 +286,141 @@ public class OrganizationRepositoryTests
|
||||
await organizationRepository.DeleteAsync(organization1);
|
||||
await organizationRepository.DeleteAsync(organization2);
|
||||
}
|
||||
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task GetOccupiedSeatCountByOrganizationIdAsync_WithUsersAndSponsorships_ReturnsCorrectCounts(
|
||||
IUserRepository userRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IOrganizationSponsorshipRepository organizationSponsorshipRepository)
|
||||
{
|
||||
// Arrange
|
||||
var organization = await organizationRepository.CreateTestOrganizationAsync();
|
||||
|
||||
// Create users in different states
|
||||
var user1 = await userRepository.CreateTestUserAsync("test1");
|
||||
var user2 = await userRepository.CreateTestUserAsync("test2");
|
||||
var user3 = await userRepository.CreateTestUserAsync("test3");
|
||||
|
||||
// Create organization users in different states
|
||||
await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user1); // Confirmed state
|
||||
await organizationUserRepository.CreateTestOrganizationUserInviteAsync(organization); // Invited state
|
||||
|
||||
// Create a revoked user manually since there's no helper for it
|
||||
await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user3.Id,
|
||||
Status = OrganizationUserStatusType.Revoked,
|
||||
});
|
||||
|
||||
// Create sponsorships in different states
|
||||
await organizationSponsorshipRepository.CreateAsync(new OrganizationSponsorship
|
||||
{
|
||||
SponsoringOrganizationId = organization.Id,
|
||||
IsAdminInitiated = true,
|
||||
ToDelete = false,
|
||||
ValidUntil = null,
|
||||
});
|
||||
|
||||
await organizationSponsorshipRepository.CreateAsync(new OrganizationSponsorship
|
||||
{
|
||||
SponsoringOrganizationId = organization.Id,
|
||||
IsAdminInitiated = true,
|
||||
ToDelete = true,
|
||||
ValidUntil = DateTime.UtcNow.AddDays(1),
|
||||
});
|
||||
|
||||
await organizationSponsorshipRepository.CreateAsync(new OrganizationSponsorship
|
||||
{
|
||||
SponsoringOrganizationId = organization.Id,
|
||||
IsAdminInitiated = true,
|
||||
ToDelete = true,
|
||||
ValidUntil = DateTime.UtcNow.AddDays(-1), // Expired
|
||||
});
|
||||
|
||||
await organizationSponsorshipRepository.CreateAsync(new OrganizationSponsorship
|
||||
{
|
||||
SponsoringOrganizationId = organization.Id,
|
||||
IsAdminInitiated = false, // Not admin initiated
|
||||
ToDelete = false,
|
||||
ValidUntil = null,
|
||||
});
|
||||
|
||||
// Act
|
||||
var result = await organizationRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, result.Users); // Confirmed + Invited users
|
||||
Assert.Equal(2, result.Sponsored); // Two valid sponsorships
|
||||
Assert.Equal(4, result.Total); // Total occupied seats
|
||||
}
|
||||
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task GetOccupiedSeatCountByOrganizationIdAsync_WithNoUsersOrSponsorships_ReturnsZero(
|
||||
IOrganizationRepository organizationRepository)
|
||||
{
|
||||
// Arrange
|
||||
var organization = await organizationRepository.CreateTestOrganizationAsync();
|
||||
|
||||
// Act
|
||||
var result = await organizationRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(0, result.Users);
|
||||
Assert.Equal(0, result.Sponsored);
|
||||
Assert.Equal(0, result.Total);
|
||||
}
|
||||
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task GetOccupiedSeatCountByOrganizationIdAsync_WithOnlyRevokedUsers_ReturnsZero(
|
||||
IUserRepository userRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository)
|
||||
{
|
||||
// Arrange
|
||||
var organization = await organizationRepository.CreateTestOrganizationAsync();
|
||||
|
||||
var user = await userRepository.CreateTestUserAsync("test1");
|
||||
|
||||
await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = user.Id,
|
||||
Status = OrganizationUserStatusType.Revoked,
|
||||
});
|
||||
|
||||
// Act
|
||||
var result = await organizationRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(0, result.Users);
|
||||
Assert.Equal(0, result.Sponsored);
|
||||
Assert.Equal(0, result.Total);
|
||||
}
|
||||
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task GetOccupiedSeatCountByOrganizationIdAsync_WithOnlyExpiredSponsorships_ReturnsZero(
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationSponsorshipRepository organizationSponsorshipRepository)
|
||||
{
|
||||
// Arrange
|
||||
var organization = await organizationRepository.CreateTestOrganizationAsync();
|
||||
|
||||
await organizationSponsorshipRepository.CreateAsync(new OrganizationSponsorship
|
||||
{
|
||||
SponsoringOrganizationId = organization.Id,
|
||||
IsAdminInitiated = true,
|
||||
ToDelete = true,
|
||||
ValidUntil = DateTime.UtcNow.AddDays(-1), // Expired
|
||||
});
|
||||
|
||||
// Act
|
||||
var result = await organizationRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(0, result.Users);
|
||||
Assert.Equal(0, result.Sponsored);
|
||||
Assert.Equal(0, result.Total);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,85 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Infrastructure.IntegrationTest.AdminConsole.Repositories.OrganizationUserRepository;
|
||||
|
||||
public class OrganizationUserReplaceTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifically tests OrganizationUsers in the invited state, which is unique because
|
||||
/// they're not linked to a UserId.
|
||||
/// </summary>
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task ReplaceAsync_WithCollectionAccess_WhenUserIsInvited_Success(
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
ICollectionRepository collectionRepository)
|
||||
{
|
||||
var organization = await organizationRepository.CreateTestOrganizationAsync();
|
||||
|
||||
var orgUser = await organizationUserRepository.CreateTestOrganizationUserInviteAsync(organization);
|
||||
|
||||
// Act: update the user, including collection access so we test this overloaded method
|
||||
orgUser.Type = OrganizationUserType.Admin;
|
||||
orgUser.AccessSecretsManager = true;
|
||||
var collection = await collectionRepository.CreateTestCollectionAsync(organization);
|
||||
|
||||
await organizationUserRepository.ReplaceAsync(orgUser, [
|
||||
new CollectionAccessSelection { Id = collection.Id, Manage = true }
|
||||
]);
|
||||
|
||||
// Assert
|
||||
var (actualOrgUser, actualCollections) = await organizationUserRepository.GetByIdWithCollectionsAsync(orgUser.Id);
|
||||
Assert.NotNull(actualOrgUser);
|
||||
Assert.Equal(OrganizationUserType.Admin, actualOrgUser.Type);
|
||||
Assert.True(actualOrgUser.AccessSecretsManager);
|
||||
|
||||
var collectionAccess = Assert.Single(actualCollections);
|
||||
Assert.Equal(collection.Id, collectionAccess.Id);
|
||||
Assert.True(collectionAccess.Manage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests OrganizationUsers in the Confirmed status, which is a stand-in for all other
|
||||
/// non-Invited statuses (which are all linked to a UserId).
|
||||
/// </summary>
|
||||
[DatabaseTheory, DatabaseData]
|
||||
public async Task ReplaceAsync_WithCollectionAccess_WhenUserIsConfirmed_Success(
|
||||
IUserRepository userRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
ICollectionRepository collectionRepository)
|
||||
{
|
||||
var organization = await organizationRepository.CreateTestOrganizationAsync();
|
||||
|
||||
var user = await userRepository.CreateTestUserAsync();
|
||||
// OrganizationUser is linked with the User in the Confirmed status
|
||||
var orgUser = await organizationUserRepository.CreateTestOrganizationUserAsync(organization, user);
|
||||
|
||||
// Act: update the user, including collection access so we test this overloaded method
|
||||
orgUser.Type = OrganizationUserType.Admin;
|
||||
orgUser.AccessSecretsManager = true;
|
||||
var collection = await collectionRepository.CreateTestCollectionAsync(organization);
|
||||
|
||||
await organizationUserRepository.ReplaceAsync(orgUser, [
|
||||
new CollectionAccessSelection { Id = collection.Id, Manage = true }
|
||||
]);
|
||||
|
||||
// Assert
|
||||
var (actualOrgUser, actualCollections) = await organizationUserRepository.GetByIdWithCollectionsAsync(orgUser.Id);
|
||||
Assert.NotNull(actualOrgUser);
|
||||
Assert.Equal(OrganizationUserType.Admin, actualOrgUser.Type);
|
||||
Assert.True(actualOrgUser.AccessSecretsManager);
|
||||
|
||||
var collectionAccess = Assert.Single(actualCollections);
|
||||
Assert.Equal(collection.Id, collectionAccess.Id);
|
||||
Assert.True(collectionAccess.Manage);
|
||||
|
||||
// Account revision date should be updated to a later date
|
||||
var actualUser = await userRepository.GetByIdAsync(user.Id);
|
||||
Assert.NotNull(actualUser);
|
||||
Assert.True(actualUser.AccountRevisionDate.CompareTo(user.AccountRevisionDate) > 0);
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ using Bit.Core.Repositories;
|
||||
using Bit.Core.Utilities;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Infrastructure.IntegrationTest.AdminConsole.Repositories;
|
||||
namespace Bit.Infrastructure.IntegrationTest.AdminConsole.Repositories.OrganizationUserRepository;
|
||||
|
||||
public class OrganizationUserRepositoryTests
|
||||
{
|
Reference in New Issue
Block a user