mirror of
https://github.com/bitwarden/server
synced 2025-12-26 21:23:39 +00:00
Merge remote-tracking branch 'origin/main' into xunit-v3-full-upgrade
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
using System.Net;
|
||||
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
||||
using Bit.Api.IntegrationTest.Factories;
|
||||
using Bit.Api.IntegrationTest.Helpers;
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Api.IntegrationTest.AdminConsole.Controllers;
|
||||
|
||||
public class OrganizationUserControllerAutoConfirmTests : IClassFixture<ApiApplicationFactory>, IAsyncLifetime
|
||||
{
|
||||
private const string _mockEncryptedString = "2.AOs41Hd8OQiCPXjyJKCiDA==|O6OHgt2U2hJGBSNGnimJmg==|iD33s8B69C8JhYYhSa4V1tArjvLr8eEaGqOV7BRo5Jk=";
|
||||
|
||||
private readonly HttpClient _client;
|
||||
private readonly ApiApplicationFactory _factory;
|
||||
private readonly LoginHelper _loginHelper;
|
||||
|
||||
private string _ownerEmail = null!;
|
||||
|
||||
public OrganizationUserControllerAutoConfirmTests(ApiApplicationFactory apiFactory)
|
||||
{
|
||||
_factory = apiFactory;
|
||||
_factory.SubstituteService<IFeatureService>(featureService =>
|
||||
{
|
||||
featureService
|
||||
.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers)
|
||||
.Returns(true);
|
||||
});
|
||||
_client = _factory.CreateClient();
|
||||
_loginHelper = new LoginHelper(_factory, _client);
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_ownerEmail = $"org-owner-{Guid.NewGuid()}@example.com";
|
||||
await _factory.LoginWithNewAccount(_ownerEmail);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AutoConfirm_WhenUserCannotManageOtherUsers_ThenShouldReturnForbidden()
|
||||
{
|
||||
var (organization, _) = await OrganizationTestHelpers.SignUpAsync(_factory, plan: PlanType.EnterpriseAnnually2023,
|
||||
ownerEmail: _ownerEmail, passwordManagerSeats: 5, paymentMethod: PaymentMethodType.Card);
|
||||
|
||||
organization.UseAutomaticUserConfirmation = true;
|
||||
|
||||
await _factory.GetService<IOrganizationRepository>()
|
||||
.UpsertAsync(organization);
|
||||
|
||||
var testKey = $"test-key-{Guid.NewGuid()}";
|
||||
|
||||
var userToConfirmEmail = $"org-user-to-confirm-{Guid.NewGuid()}@example.com";
|
||||
await _factory.LoginWithNewAccount(userToConfirmEmail);
|
||||
|
||||
var (confirmingUserEmail, _) = await OrganizationTestHelpers.CreateNewUserWithAccountAsync(_factory, organization.Id, OrganizationUserType.User);
|
||||
await _loginHelper.LoginAsync(confirmingUserEmail);
|
||||
|
||||
var organizationUser = await OrganizationTestHelpers.CreateUserAsync(
|
||||
_factory,
|
||||
organization.Id,
|
||||
userToConfirmEmail,
|
||||
OrganizationUserType.User,
|
||||
false,
|
||||
new Permissions { ManageUsers = false },
|
||||
OrganizationUserStatusType.Accepted);
|
||||
|
||||
var result = await _client.PostAsJsonAsync($"organizations/{organization.Id}/users/{organizationUser.Id}/auto-confirm",
|
||||
new OrganizationUserConfirmRequestModel
|
||||
{
|
||||
Key = testKey,
|
||||
DefaultUserCollectionName = _mockEncryptedString
|
||||
});
|
||||
|
||||
Assert.Equal(HttpStatusCode.Forbidden, result.StatusCode);
|
||||
|
||||
await _factory.GetService<IOrganizationRepository>().DeleteAsync(organization);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AutoConfirm_WhenOwnerConfirmsValidUser_ThenShouldReturnNoContent()
|
||||
{
|
||||
var (organization, _) = await OrganizationTestHelpers.SignUpAsync(_factory, plan: PlanType.EnterpriseAnnually2023,
|
||||
ownerEmail: _ownerEmail, passwordManagerSeats: 5, paymentMethod: PaymentMethodType.Card);
|
||||
|
||||
organization.UseAutomaticUserConfirmation = true;
|
||||
|
||||
await _factory.GetService<IOrganizationRepository>()
|
||||
.UpsertAsync(organization);
|
||||
|
||||
var testKey = $"test-key-{Guid.NewGuid()}";
|
||||
|
||||
await _factory.GetService<IPolicyRepository>().CreateAsync(new Policy
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
Type = PolicyType.AutomaticUserConfirmation,
|
||||
Enabled = true
|
||||
});
|
||||
|
||||
await _factory.GetService<IPolicyRepository>().CreateAsync(new Policy
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
Type = PolicyType.OrganizationDataOwnership,
|
||||
Enabled = true
|
||||
});
|
||||
|
||||
var userToConfirmEmail = $"org-user-to-confirm-{Guid.NewGuid()}@example.com";
|
||||
await _factory.LoginWithNewAccount(userToConfirmEmail);
|
||||
|
||||
await _loginHelper.LoginAsync(_ownerEmail);
|
||||
var organizationUser = await OrganizationTestHelpers.CreateUserAsync(
|
||||
_factory,
|
||||
organization.Id,
|
||||
userToConfirmEmail,
|
||||
OrganizationUserType.User,
|
||||
false,
|
||||
new Permissions(),
|
||||
OrganizationUserStatusType.Accepted);
|
||||
|
||||
var result = await _client.PostAsJsonAsync($"organizations/{organization.Id}/users/{organizationUser.Id}/auto-confirm",
|
||||
new OrganizationUserConfirmRequestModel
|
||||
{
|
||||
Key = testKey,
|
||||
DefaultUserCollectionName = _mockEncryptedString
|
||||
});
|
||||
|
||||
Assert.Equal(HttpStatusCode.NoContent, result.StatusCode);
|
||||
|
||||
var orgUserRepository = _factory.GetService<IOrganizationUserRepository>();
|
||||
var confirmedUser = await orgUserRepository.GetByIdAsync(organizationUser.Id);
|
||||
Assert.NotNull(confirmedUser);
|
||||
Assert.Equal(OrganizationUserStatusType.Confirmed, confirmedUser.Status);
|
||||
Assert.Equal(testKey, confirmedUser.Key);
|
||||
|
||||
var collectionRepository = _factory.GetService<ICollectionRepository>();
|
||||
var collections = await collectionRepository.GetManyByUserIdAsync(organizationUser.UserId!.Value);
|
||||
|
||||
Assert.NotEmpty(collections);
|
||||
Assert.Single(collections.Where(c => c.Type == CollectionType.DefaultUserCollection));
|
||||
|
||||
await _factory.GetService<IOrganizationRepository>().DeleteAsync(organization);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AutoConfirm_WhenUserIsConfirmedMultipleTimes_ThenShouldSuccessAndOnlyConfirmOneUser()
|
||||
{
|
||||
var (organization, _) = await OrganizationTestHelpers.SignUpAsync(_factory, plan: PlanType.EnterpriseAnnually2023,
|
||||
ownerEmail: _ownerEmail, passwordManagerSeats: 5, paymentMethod: PaymentMethodType.Card);
|
||||
|
||||
organization.UseAutomaticUserConfirmation = true;
|
||||
|
||||
await _factory.GetService<IOrganizationRepository>()
|
||||
.UpsertAsync(organization);
|
||||
|
||||
var testKey = $"test-key-{Guid.NewGuid()}";
|
||||
|
||||
var userToConfirmEmail = $"org-user-to-confirm-{Guid.NewGuid()}@example.com";
|
||||
await _factory.LoginWithNewAccount(userToConfirmEmail);
|
||||
|
||||
await _factory.GetService<IPolicyRepository>().CreateAsync(new Policy
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
Type = PolicyType.AutomaticUserConfirmation,
|
||||
Enabled = true
|
||||
});
|
||||
|
||||
await _factory.GetService<IPolicyRepository>().CreateAsync(new Policy
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
Type = PolicyType.OrganizationDataOwnership,
|
||||
Enabled = true
|
||||
});
|
||||
|
||||
await _loginHelper.LoginAsync(_ownerEmail);
|
||||
|
||||
var organizationUser = await OrganizationTestHelpers.CreateUserAsync(
|
||||
_factory,
|
||||
organization.Id,
|
||||
userToConfirmEmail,
|
||||
OrganizationUserType.User,
|
||||
false,
|
||||
new Permissions(),
|
||||
OrganizationUserStatusType.Accepted);
|
||||
|
||||
var tenRequests = Enumerable.Range(0, 10)
|
||||
.Select(_ => _client.PostAsJsonAsync($"organizations/{organization.Id}/users/{organizationUser.Id}/auto-confirm",
|
||||
new OrganizationUserConfirmRequestModel
|
||||
{
|
||||
Key = testKey,
|
||||
DefaultUserCollectionName = _mockEncryptedString
|
||||
})).ToList();
|
||||
|
||||
var results = await Task.WhenAll(tenRequests);
|
||||
|
||||
Assert.Contains(results, r => r.StatusCode == HttpStatusCode.NoContent);
|
||||
|
||||
var orgUserRepository = _factory.GetService<IOrganizationUserRepository>();
|
||||
var confirmedUser = await orgUserRepository.GetByIdAsync(organizationUser.Id);
|
||||
Assert.NotNull(confirmedUser);
|
||||
Assert.Equal(OrganizationUserStatusType.Confirmed, confirmedUser.Status);
|
||||
Assert.Equal(testKey, confirmedUser.Key);
|
||||
|
||||
var collections = await _factory.GetService<ICollectionRepository>()
|
||||
.GetManyByUserIdAsync(organizationUser.UserId!.Value);
|
||||
Assert.NotEmpty(collections);
|
||||
// validates user only received one default collection
|
||||
Assert.Single(collections.Where(c => c.Type == CollectionType.DefaultUserCollection));
|
||||
|
||||
await _factory.GetService<IOrganizationRepository>().DeleteAsync(organization);
|
||||
}
|
||||
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
_client.Dispose();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user