mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 15:42:48 -05:00
Revert filescoped (#2227)
* Revert "Add git blame entry (#2226)" This reverts commit239286737d
. * Revert "Turn on file scoped namespaces (#2225)" This reverts commit34fb4cca2a
.
This commit is contained in:
@ -13,412 +13,413 @@ using Microsoft.AspNetCore.Identity;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Api.Test.Controllers;
|
||||
|
||||
public class AccountsControllerTests : IDisposable
|
||||
namespace Bit.Api.Test.Controllers
|
||||
{
|
||||
|
||||
private readonly AccountsController _sut;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly IFolderRepository _folderRepository;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ISendRepository _sendRepository;
|
||||
private readonly ISendService _sendService;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
|
||||
public AccountsControllerTests()
|
||||
public class AccountsControllerTests : IDisposable
|
||||
{
|
||||
_userService = Substitute.For<IUserService>();
|
||||
_userRepository = Substitute.For<IUserRepository>();
|
||||
_cipherRepository = Substitute.For<ICipherRepository>();
|
||||
_folderRepository = Substitute.For<IFolderRepository>();
|
||||
_organizationService = Substitute.For<IOrganizationService>();
|
||||
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||
_providerUserRepository = Substitute.For<IProviderUserRepository>();
|
||||
_paymentService = Substitute.For<IPaymentService>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
_sendRepository = Substitute.For<ISendRepository>();
|
||||
_sendService = Substitute.For<ISendService>();
|
||||
_sut = new AccountsController(
|
||||
_globalSettings,
|
||||
_cipherRepository,
|
||||
_folderRepository,
|
||||
_organizationService,
|
||||
_organizationUserRepository,
|
||||
_providerUserRepository,
|
||||
_paymentService,
|
||||
_userRepository,
|
||||
_userService,
|
||||
_sendRepository,
|
||||
_sendService
|
||||
);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_sut?.Dispose();
|
||||
}
|
||||
private readonly AccountsController _sut;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ICipherRepository _cipherRepository;
|
||||
private readonly IFolderRepository _folderRepository;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ISendRepository _sendRepository;
|
||||
private readonly ISendService _sendService;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
|
||||
[Fact]
|
||||
public async Task PostPrelogin_WhenUserExists_ShouldReturnUserKdfInfo()
|
||||
{
|
||||
var userKdfInfo = new UserKdfInformation
|
||||
public AccountsControllerTests()
|
||||
{
|
||||
Kdf = KdfType.PBKDF2_SHA256,
|
||||
KdfIterations = 5000
|
||||
};
|
||||
_userRepository.GetKdfInformationByEmailAsync(Arg.Any<string>()).Returns(Task.FromResult(userKdfInfo));
|
||||
_userService = Substitute.For<IUserService>();
|
||||
_userRepository = Substitute.For<IUserRepository>();
|
||||
_cipherRepository = Substitute.For<ICipherRepository>();
|
||||
_folderRepository = Substitute.For<IFolderRepository>();
|
||||
_organizationService = Substitute.For<IOrganizationService>();
|
||||
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||
_providerUserRepository = Substitute.For<IProviderUserRepository>();
|
||||
_paymentService = Substitute.For<IPaymentService>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
_sendRepository = Substitute.For<ISendRepository>();
|
||||
_sendService = Substitute.For<ISendService>();
|
||||
_sut = new AccountsController(
|
||||
_globalSettings,
|
||||
_cipherRepository,
|
||||
_folderRepository,
|
||||
_organizationService,
|
||||
_organizationUserRepository,
|
||||
_providerUserRepository,
|
||||
_paymentService,
|
||||
_userRepository,
|
||||
_userService,
|
||||
_sendRepository,
|
||||
_sendService
|
||||
);
|
||||
}
|
||||
|
||||
var response = await _sut.PostPrelogin(new PreloginRequestModel { Email = "user@example.com" });
|
||||
|
||||
Assert.Equal(userKdfInfo.Kdf, response.Kdf);
|
||||
Assert.Equal(userKdfInfo.KdfIterations, response.KdfIterations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostPrelogin_WhenUserDoesNotExist_ShouldDefaultToSha256And100000Iterations()
|
||||
{
|
||||
_userRepository.GetKdfInformationByEmailAsync(Arg.Any<string>()).Returns(Task.FromResult((UserKdfInformation)null));
|
||||
|
||||
var response = await _sut.PostPrelogin(new PreloginRequestModel { Email = "user@example.com" });
|
||||
|
||||
Assert.Equal(KdfType.PBKDF2_SHA256, response.Kdf);
|
||||
Assert.Equal(100000, response.KdfIterations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostRegister_ShouldRegisterUser()
|
||||
{
|
||||
var passwordHash = "abcdef";
|
||||
var token = "123456";
|
||||
var userGuid = new Guid();
|
||||
_userService.RegisterUserAsync(Arg.Any<User>(), passwordHash, token, userGuid)
|
||||
.Returns(Task.FromResult(IdentityResult.Success));
|
||||
var request = new RegisterRequestModel
|
||||
public void Dispose()
|
||||
{
|
||||
Name = "Example User",
|
||||
Email = "user@example.com",
|
||||
MasterPasswordHash = passwordHash,
|
||||
MasterPasswordHint = "example",
|
||||
Token = token,
|
||||
OrganizationUserId = userGuid
|
||||
};
|
||||
_sut?.Dispose();
|
||||
}
|
||||
|
||||
await _sut.PostRegister(request);
|
||||
|
||||
await _userService.Received(1).RegisterUserAsync(Arg.Any<User>(), passwordHash, token, userGuid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostRegister_WhenUserServiceFails_ShouldThrowBadRequestException()
|
||||
{
|
||||
var passwordHash = "abcdef";
|
||||
var token = "123456";
|
||||
var userGuid = new Guid();
|
||||
_userService.RegisterUserAsync(Arg.Any<User>(), passwordHash, token, userGuid)
|
||||
.Returns(Task.FromResult(IdentityResult.Failed()));
|
||||
var request = new RegisterRequestModel
|
||||
[Fact]
|
||||
public async Task PostPrelogin_WhenUserExists_ShouldReturnUserKdfInfo()
|
||||
{
|
||||
Name = "Example User",
|
||||
Email = "user@example.com",
|
||||
MasterPasswordHash = passwordHash,
|
||||
MasterPasswordHint = "example",
|
||||
Token = token,
|
||||
OrganizationUserId = userGuid
|
||||
};
|
||||
var userKdfInfo = new UserKdfInformation
|
||||
{
|
||||
Kdf = KdfType.PBKDF2_SHA256,
|
||||
KdfIterations = 5000
|
||||
};
|
||||
_userRepository.GetKdfInformationByEmailAsync(Arg.Any<string>()).Returns(Task.FromResult(userKdfInfo));
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => _sut.PostRegister(request));
|
||||
}
|
||||
var response = await _sut.PostPrelogin(new PreloginRequestModel { Email = "user@example.com" });
|
||||
|
||||
[Fact]
|
||||
public async Task PostPasswordHint_ShouldNotifyUserService()
|
||||
{
|
||||
var email = "user@example.com";
|
||||
Assert.Equal(userKdfInfo.Kdf, response.Kdf);
|
||||
Assert.Equal(userKdfInfo.KdfIterations, response.KdfIterations);
|
||||
}
|
||||
|
||||
await _sut.PostPasswordHint(new PasswordHintRequestModel { Email = email });
|
||||
|
||||
await _userService.Received(1).SendMasterPasswordHintAsync(email);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostEmailToken_ShouldInitiateEmailChange()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
ConfigureUserServiceToAcceptPasswordFor(user);
|
||||
var newEmail = "example@user.com";
|
||||
|
||||
await _sut.PostEmailToken(new EmailTokenRequestModel { NewEmail = newEmail });
|
||||
|
||||
await _userService.Received(1).InitiateEmailChangeAsync(user, newEmail);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostEmailToken_WhenNotAuthorized_ShouldThrowUnauthorizedAccessException()
|
||||
{
|
||||
ConfigureUserServiceToReturnNullPrincipal();
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.PostEmailToken(new EmailTokenRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostEmailToken_WhenInvalidPasssword_ShouldThrowBadRequestException()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
ConfigureUserServiceToRejectPasswordFor(user);
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => _sut.PostEmailToken(new EmailTokenRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostEmail_ShouldChangeUserEmail()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
_userService.ChangeEmailAsync(user, default, default, default, default, default)
|
||||
.Returns(Task.FromResult(IdentityResult.Success));
|
||||
|
||||
await _sut.PostEmail(new EmailRequestModel());
|
||||
|
||||
await _userService.Received(1).ChangeEmailAsync(user, default, default, default, default, default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostEmail_WhenNotAuthorized_ShouldThrownUnauthorizedAccessException()
|
||||
{
|
||||
ConfigureUserServiceToReturnNullPrincipal();
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.PostEmail(new EmailRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostEmail_WhenEmailCannotBeChanged_ShouldThrowBadRequestException()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
_userService.ChangeEmailAsync(user, default, default, default, default, default)
|
||||
.Returns(Task.FromResult(IdentityResult.Failed()));
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => _sut.PostEmail(new EmailRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostVerifyEmail_ShouldSendEmailVerification()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
|
||||
await _sut.PostVerifyEmail();
|
||||
|
||||
await _userService.Received(1).SendEmailVerificationAsync(user);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostVerifyEmail_WhenNotAuthorized_ShouldThrownUnauthorizedAccessException()
|
||||
{
|
||||
ConfigureUserServiceToReturnNullPrincipal();
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.PostVerifyEmail()
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostVerifyEmailToken_ShouldConfirmEmail()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidIdFor(user);
|
||||
_userService.ConfirmEmailAsync(user, Arg.Any<string>())
|
||||
.Returns(Task.FromResult(IdentityResult.Success));
|
||||
|
||||
await _sut.PostVerifyEmailToken(new VerifyEmailRequestModel { UserId = "12345678-1234-1234-1234-123456789012" });
|
||||
|
||||
await _userService.Received(1).ConfirmEmailAsync(user, Arg.Any<string>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostVerifyEmailToken_WhenUserDoesNotExist_ShouldThrowUnauthorizedAccessException()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnNullUserId();
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.PostVerifyEmailToken(new VerifyEmailRequestModel { UserId = "12345678-1234-1234-1234-123456789012" })
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostVerifyEmailToken_WhenEmailConfirmationFails_ShouldThrowBadRequestException()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidIdFor(user);
|
||||
_userService.ConfirmEmailAsync(user, Arg.Any<string>())
|
||||
.Returns(Task.FromResult(IdentityResult.Failed()));
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => _sut.PostVerifyEmailToken(new VerifyEmailRequestModel { UserId = "12345678-1234-1234-1234-123456789012" })
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostPassword_ShouldChangePassword()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
_userService.ChangePasswordAsync(user, default, default, default, default)
|
||||
.Returns(Task.FromResult(IdentityResult.Success));
|
||||
|
||||
await _sut.PostPassword(new PasswordRequestModel());
|
||||
|
||||
await _userService.Received(1).ChangePasswordAsync(user, default, default, default, default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostPassword_WhenNotAuthorized_ShouldThrowUnauthorizedAccessException()
|
||||
{
|
||||
ConfigureUserServiceToReturnNullPrincipal();
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.PostPassword(new PasswordRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostPassword_WhenPasswordChangeFails_ShouldBadRequestException()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
_userService.ChangePasswordAsync(user, default, default, default, default)
|
||||
.Returns(Task.FromResult(IdentityResult.Failed()));
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => _sut.PostPassword(new PasswordRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetApiKey_ShouldReturnApiKeyResponse()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
ConfigureUserServiceToAcceptPasswordFor(user);
|
||||
await _sut.ApiKey(new SecretVerificationRequestModel());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetApiKey_WhenUserDoesNotExist_ShouldThrowUnauthorizedAccessException()
|
||||
{
|
||||
ConfigureUserServiceToReturnNullPrincipal();
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.ApiKey(new SecretVerificationRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetApiKey_WhenPasswordCheckFails_ShouldThrowBadRequestException()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
ConfigureUserServiceToRejectPasswordFor(user);
|
||||
await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => _sut.ApiKey(new SecretVerificationRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostRotateApiKey_ShouldRotateApiKey()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
ConfigureUserServiceToAcceptPasswordFor(user);
|
||||
await _sut.RotateApiKey(new SecretVerificationRequestModel());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostRotateApiKey_WhenUserDoesNotExist_ShouldThrowUnauthorizedAccessException()
|
||||
{
|
||||
ConfigureUserServiceToReturnNullPrincipal();
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.ApiKey(new SecretVerificationRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostRotateApiKey_WhenPasswordCheckFails_ShouldThrowBadRequestException()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
ConfigureUserServiceToRejectPasswordFor(user);
|
||||
await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => _sut.ApiKey(new SecretVerificationRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
// Below are helper functions that currently belong to this
|
||||
// test class, but ultimately may need to be split out into
|
||||
// something greater in order to share common test steps with
|
||||
// other test suites. They are included here for the time being
|
||||
// until that day comes.
|
||||
private User GenerateExampleUser()
|
||||
{
|
||||
return new User
|
||||
[Fact]
|
||||
public async Task PostPrelogin_WhenUserDoesNotExist_ShouldDefaultToSha256And100000Iterations()
|
||||
{
|
||||
Email = "user@example.com"
|
||||
};
|
||||
}
|
||||
_userRepository.GetKdfInformationByEmailAsync(Arg.Any<string>()).Returns(Task.FromResult((UserKdfInformation)null));
|
||||
|
||||
private void ConfigureUserServiceToReturnNullPrincipal()
|
||||
{
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
|
||||
.Returns(Task.FromResult((User)null));
|
||||
}
|
||||
var response = await _sut.PostPrelogin(new PreloginRequestModel { Email = "user@example.com" });
|
||||
|
||||
private void ConfigureUserServiceToReturnValidPrincipalFor(User user)
|
||||
{
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
|
||||
.Returns(Task.FromResult(user));
|
||||
}
|
||||
Assert.Equal(KdfType.PBKDF2_SHA256, response.Kdf);
|
||||
Assert.Equal(100000, response.KdfIterations);
|
||||
}
|
||||
|
||||
private void ConfigureUserServiceToRejectPasswordFor(User user)
|
||||
{
|
||||
_userService.CheckPasswordAsync(user, Arg.Any<string>())
|
||||
.Returns(Task.FromResult(false));
|
||||
}
|
||||
[Fact]
|
||||
public async Task PostRegister_ShouldRegisterUser()
|
||||
{
|
||||
var passwordHash = "abcdef";
|
||||
var token = "123456";
|
||||
var userGuid = new Guid();
|
||||
_userService.RegisterUserAsync(Arg.Any<User>(), passwordHash, token, userGuid)
|
||||
.Returns(Task.FromResult(IdentityResult.Success));
|
||||
var request = new RegisterRequestModel
|
||||
{
|
||||
Name = "Example User",
|
||||
Email = "user@example.com",
|
||||
MasterPasswordHash = passwordHash,
|
||||
MasterPasswordHint = "example",
|
||||
Token = token,
|
||||
OrganizationUserId = userGuid
|
||||
};
|
||||
|
||||
private void ConfigureUserServiceToAcceptPasswordFor(User user)
|
||||
{
|
||||
_userService.CheckPasswordAsync(user, Arg.Any<string>())
|
||||
.Returns(Task.FromResult(true));
|
||||
_userService.VerifySecretAsync(user, Arg.Any<string>())
|
||||
.Returns(Task.FromResult(true));
|
||||
}
|
||||
await _sut.PostRegister(request);
|
||||
|
||||
private void ConfigureUserServiceToReturnValidIdFor(User user)
|
||||
{
|
||||
_userService.GetUserByIdAsync(Arg.Any<Guid>())
|
||||
.Returns(Task.FromResult(user));
|
||||
}
|
||||
await _userService.Received(1).RegisterUserAsync(Arg.Any<User>(), passwordHash, token, userGuid);
|
||||
}
|
||||
|
||||
private void ConfigureUserServiceToReturnNullUserId()
|
||||
{
|
||||
_userService.GetUserByIdAsync(Arg.Any<Guid>())
|
||||
.Returns(Task.FromResult((User)null));
|
||||
[Fact]
|
||||
public async Task PostRegister_WhenUserServiceFails_ShouldThrowBadRequestException()
|
||||
{
|
||||
var passwordHash = "abcdef";
|
||||
var token = "123456";
|
||||
var userGuid = new Guid();
|
||||
_userService.RegisterUserAsync(Arg.Any<User>(), passwordHash, token, userGuid)
|
||||
.Returns(Task.FromResult(IdentityResult.Failed()));
|
||||
var request = new RegisterRequestModel
|
||||
{
|
||||
Name = "Example User",
|
||||
Email = "user@example.com",
|
||||
MasterPasswordHash = passwordHash,
|
||||
MasterPasswordHint = "example",
|
||||
Token = token,
|
||||
OrganizationUserId = userGuid
|
||||
};
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => _sut.PostRegister(request));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostPasswordHint_ShouldNotifyUserService()
|
||||
{
|
||||
var email = "user@example.com";
|
||||
|
||||
await _sut.PostPasswordHint(new PasswordHintRequestModel { Email = email });
|
||||
|
||||
await _userService.Received(1).SendMasterPasswordHintAsync(email);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostEmailToken_ShouldInitiateEmailChange()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
ConfigureUserServiceToAcceptPasswordFor(user);
|
||||
var newEmail = "example@user.com";
|
||||
|
||||
await _sut.PostEmailToken(new EmailTokenRequestModel { NewEmail = newEmail });
|
||||
|
||||
await _userService.Received(1).InitiateEmailChangeAsync(user, newEmail);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostEmailToken_WhenNotAuthorized_ShouldThrowUnauthorizedAccessException()
|
||||
{
|
||||
ConfigureUserServiceToReturnNullPrincipal();
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.PostEmailToken(new EmailTokenRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostEmailToken_WhenInvalidPasssword_ShouldThrowBadRequestException()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
ConfigureUserServiceToRejectPasswordFor(user);
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => _sut.PostEmailToken(new EmailTokenRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostEmail_ShouldChangeUserEmail()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
_userService.ChangeEmailAsync(user, default, default, default, default, default)
|
||||
.Returns(Task.FromResult(IdentityResult.Success));
|
||||
|
||||
await _sut.PostEmail(new EmailRequestModel());
|
||||
|
||||
await _userService.Received(1).ChangeEmailAsync(user, default, default, default, default, default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostEmail_WhenNotAuthorized_ShouldThrownUnauthorizedAccessException()
|
||||
{
|
||||
ConfigureUserServiceToReturnNullPrincipal();
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.PostEmail(new EmailRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostEmail_WhenEmailCannotBeChanged_ShouldThrowBadRequestException()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
_userService.ChangeEmailAsync(user, default, default, default, default, default)
|
||||
.Returns(Task.FromResult(IdentityResult.Failed()));
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => _sut.PostEmail(new EmailRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostVerifyEmail_ShouldSendEmailVerification()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
|
||||
await _sut.PostVerifyEmail();
|
||||
|
||||
await _userService.Received(1).SendEmailVerificationAsync(user);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostVerifyEmail_WhenNotAuthorized_ShouldThrownUnauthorizedAccessException()
|
||||
{
|
||||
ConfigureUserServiceToReturnNullPrincipal();
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.PostVerifyEmail()
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostVerifyEmailToken_ShouldConfirmEmail()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidIdFor(user);
|
||||
_userService.ConfirmEmailAsync(user, Arg.Any<string>())
|
||||
.Returns(Task.FromResult(IdentityResult.Success));
|
||||
|
||||
await _sut.PostVerifyEmailToken(new VerifyEmailRequestModel { UserId = "12345678-1234-1234-1234-123456789012" });
|
||||
|
||||
await _userService.Received(1).ConfirmEmailAsync(user, Arg.Any<string>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostVerifyEmailToken_WhenUserDoesNotExist_ShouldThrowUnauthorizedAccessException()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnNullUserId();
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.PostVerifyEmailToken(new VerifyEmailRequestModel { UserId = "12345678-1234-1234-1234-123456789012" })
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostVerifyEmailToken_WhenEmailConfirmationFails_ShouldThrowBadRequestException()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidIdFor(user);
|
||||
_userService.ConfirmEmailAsync(user, Arg.Any<string>())
|
||||
.Returns(Task.FromResult(IdentityResult.Failed()));
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => _sut.PostVerifyEmailToken(new VerifyEmailRequestModel { UserId = "12345678-1234-1234-1234-123456789012" })
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostPassword_ShouldChangePassword()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
_userService.ChangePasswordAsync(user, default, default, default, default)
|
||||
.Returns(Task.FromResult(IdentityResult.Success));
|
||||
|
||||
await _sut.PostPassword(new PasswordRequestModel());
|
||||
|
||||
await _userService.Received(1).ChangePasswordAsync(user, default, default, default, default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostPassword_WhenNotAuthorized_ShouldThrowUnauthorizedAccessException()
|
||||
{
|
||||
ConfigureUserServiceToReturnNullPrincipal();
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.PostPassword(new PasswordRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostPassword_WhenPasswordChangeFails_ShouldBadRequestException()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
_userService.ChangePasswordAsync(user, default, default, default, default)
|
||||
.Returns(Task.FromResult(IdentityResult.Failed()));
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => _sut.PostPassword(new PasswordRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetApiKey_ShouldReturnApiKeyResponse()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
ConfigureUserServiceToAcceptPasswordFor(user);
|
||||
await _sut.ApiKey(new SecretVerificationRequestModel());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetApiKey_WhenUserDoesNotExist_ShouldThrowUnauthorizedAccessException()
|
||||
{
|
||||
ConfigureUserServiceToReturnNullPrincipal();
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.ApiKey(new SecretVerificationRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetApiKey_WhenPasswordCheckFails_ShouldThrowBadRequestException()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
ConfigureUserServiceToRejectPasswordFor(user);
|
||||
await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => _sut.ApiKey(new SecretVerificationRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostRotateApiKey_ShouldRotateApiKey()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
ConfigureUserServiceToAcceptPasswordFor(user);
|
||||
await _sut.RotateApiKey(new SecretVerificationRequestModel());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostRotateApiKey_WhenUserDoesNotExist_ShouldThrowUnauthorizedAccessException()
|
||||
{
|
||||
ConfigureUserServiceToReturnNullPrincipal();
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
||||
() => _sut.ApiKey(new SecretVerificationRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostRotateApiKey_WhenPasswordCheckFails_ShouldThrowBadRequestException()
|
||||
{
|
||||
var user = GenerateExampleUser();
|
||||
ConfigureUserServiceToReturnValidPrincipalFor(user);
|
||||
ConfigureUserServiceToRejectPasswordFor(user);
|
||||
await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => _sut.ApiKey(new SecretVerificationRequestModel())
|
||||
);
|
||||
}
|
||||
|
||||
// Below are helper functions that currently belong to this
|
||||
// test class, but ultimately may need to be split out into
|
||||
// something greater in order to share common test steps with
|
||||
// other test suites. They are included here for the time being
|
||||
// until that day comes.
|
||||
private User GenerateExampleUser()
|
||||
{
|
||||
return new User
|
||||
{
|
||||
Email = "user@example.com"
|
||||
};
|
||||
}
|
||||
|
||||
private void ConfigureUserServiceToReturnNullPrincipal()
|
||||
{
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
|
||||
.Returns(Task.FromResult((User)null));
|
||||
}
|
||||
|
||||
private void ConfigureUserServiceToReturnValidPrincipalFor(User user)
|
||||
{
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
|
||||
.Returns(Task.FromResult(user));
|
||||
}
|
||||
|
||||
private void ConfigureUserServiceToRejectPasswordFor(User user)
|
||||
{
|
||||
_userService.CheckPasswordAsync(user, Arg.Any<string>())
|
||||
.Returns(Task.FromResult(false));
|
||||
}
|
||||
|
||||
private void ConfigureUserServiceToAcceptPasswordFor(User user)
|
||||
{
|
||||
_userService.CheckPasswordAsync(user, Arg.Any<string>())
|
||||
.Returns(Task.FromResult(true));
|
||||
_userService.VerifySecretAsync(user, Arg.Any<string>())
|
||||
.Returns(Task.FromResult(true));
|
||||
}
|
||||
|
||||
private void ConfigureUserServiceToReturnValidIdFor(User user)
|
||||
{
|
||||
_userService.GetUserByIdAsync(Arg.Any<Guid>())
|
||||
.Returns(Task.FromResult(user));
|
||||
}
|
||||
|
||||
private void ConfigureUserServiceToReturnNullUserId()
|
||||
{
|
||||
_userService.GetUserByIdAsync(Arg.Any<Guid>())
|
||||
.Returns(Task.FromResult((User)null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,78 +11,79 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Api.Test.Controllers;
|
||||
|
||||
[ControllerCustomize(typeof(CollectionsController))]
|
||||
[SutProviderCustomize]
|
||||
public class CollectionsControllerTests
|
||||
namespace Bit.Api.Test.Controllers
|
||||
{
|
||||
[Theory, BitAutoData]
|
||||
public async Task Post_Success(Guid orgId, SutProvider<CollectionsController> sutProvider)
|
||||
[ControllerCustomize(typeof(CollectionsController))]
|
||||
[SutProviderCustomize]
|
||||
public class CollectionsControllerTests
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.CreateNewCollections(orgId)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.EditAnyCollection(orgId)
|
||||
.Returns(false);
|
||||
|
||||
var collectionRequest = new CollectionRequestModel
|
||||
[Theory, BitAutoData]
|
||||
public async Task Post_Success(Guid orgId, SutProvider<CollectionsController> sutProvider)
|
||||
{
|
||||
Name = "encrypted_string",
|
||||
ExternalId = "my_external_id"
|
||||
};
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.CreateNewCollections(orgId)
|
||||
.Returns(true);
|
||||
|
||||
_ = await sutProvider.Sut.Post(orgId, collectionRequest);
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.EditAnyCollection(orgId)
|
||||
.Returns(false);
|
||||
|
||||
await sutProvider.GetDependency<ICollectionService>()
|
||||
.Received(1)
|
||||
.SaveAsync(Arg.Any<Collection>(), Arg.Any<IEnumerable<SelectionReadOnly>>(), null);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task Put_Success(Guid orgId, Guid collectionId, Guid userId, CollectionRequestModel collectionRequest,
|
||||
SutProvider<CollectionsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.ViewAssignedCollections(orgId)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.EditAssignedCollections(orgId)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.UserId
|
||||
.Returns(userId);
|
||||
|
||||
sutProvider.GetDependency<ICollectionRepository>()
|
||||
.GetByIdAsync(collectionId, userId)
|
||||
.Returns(new CollectionDetails
|
||||
var collectionRequest = new CollectionRequestModel
|
||||
{
|
||||
OrganizationId = orgId,
|
||||
});
|
||||
Name = "encrypted_string",
|
||||
ExternalId = "my_external_id"
|
||||
};
|
||||
|
||||
_ = await sutProvider.Sut.Put(orgId, collectionId, collectionRequest);
|
||||
}
|
||||
_ = await sutProvider.Sut.Post(orgId, collectionRequest);
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task Put_CanNotEditAssignedCollection_ThrowsNotFound(Guid orgId, Guid collectionId, Guid userId, CollectionRequestModel collectionRequest,
|
||||
SutProvider<CollectionsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.EditAssignedCollections(orgId)
|
||||
.Returns(true);
|
||||
await sutProvider.GetDependency<ICollectionService>()
|
||||
.Received(1)
|
||||
.SaveAsync(Arg.Any<Collection>(), Arg.Any<IEnumerable<SelectionReadOnly>>(), null);
|
||||
}
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.UserId
|
||||
.Returns(userId);
|
||||
[Theory, BitAutoData]
|
||||
public async Task Put_Success(Guid orgId, Guid collectionId, Guid userId, CollectionRequestModel collectionRequest,
|
||||
SutProvider<CollectionsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.ViewAssignedCollections(orgId)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<ICollectionRepository>()
|
||||
.GetByIdAsync(collectionId, userId)
|
||||
.Returns(Task.FromResult<CollectionDetails>(null));
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.EditAssignedCollections(orgId)
|
||||
.Returns(true);
|
||||
|
||||
_ = await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.Put(orgId, collectionId, collectionRequest));
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.UserId
|
||||
.Returns(userId);
|
||||
|
||||
sutProvider.GetDependency<ICollectionRepository>()
|
||||
.GetByIdAsync(collectionId, userId)
|
||||
.Returns(new CollectionDetails
|
||||
{
|
||||
OrganizationId = orgId,
|
||||
});
|
||||
|
||||
_ = await sutProvider.Sut.Put(orgId, collectionId, collectionRequest);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task Put_CanNotEditAssignedCollection_ThrowsNotFound(Guid orgId, Guid collectionId, Guid userId, CollectionRequestModel collectionRequest,
|
||||
SutProvider<CollectionsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.EditAssignedCollections(orgId)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.UserId
|
||||
.Returns(userId);
|
||||
|
||||
sutProvider.GetDependency<ICollectionRepository>()
|
||||
.GetByIdAsync(collectionId, userId)
|
||||
.Returns(Task.FromResult<CollectionDetails>(null));
|
||||
|
||||
_ = await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.Put(orgId, collectionId, collectionRequest));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,301 +18,302 @@ using Bit.Test.Common.Helpers;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Api.Test.Controllers;
|
||||
|
||||
[ControllerCustomize(typeof(OrganizationConnectionsController))]
|
||||
[SutProviderCustomize]
|
||||
[JsonDocumentCustomize]
|
||||
public class OrganizationConnectionsControllerTests
|
||||
namespace Bit.Api.Test.Controllers
|
||||
{
|
||||
public static IEnumerable<object[]> ConnectionTypes =>
|
||||
Enum.GetValues<OrganizationConnectionType>().Select(p => new object[] { p });
|
||||
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(true, true)]
|
||||
[BitAutoData(false, true)]
|
||||
[BitAutoData(true, false)]
|
||||
[BitAutoData(false, false)]
|
||||
public void ConnectionEnabled_RequiresBothSelfHostAndCommunications(bool selfHosted, bool enableCloudCommunication, SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
[ControllerCustomize(typeof(OrganizationConnectionsController))]
|
||||
[SutProviderCustomize]
|
||||
[JsonDocumentCustomize]
|
||||
public class OrganizationConnectionsControllerTests
|
||||
{
|
||||
var globalSettingsMock = sutProvider.GetDependency<IGlobalSettings>();
|
||||
globalSettingsMock.SelfHosted.Returns(selfHosted);
|
||||
globalSettingsMock.EnableCloudCommunication.Returns(enableCloudCommunication);
|
||||
public static IEnumerable<object[]> ConnectionTypes =>
|
||||
Enum.GetValues<OrganizationConnectionType>().Select(p => new object[] { p });
|
||||
|
||||
Action<bool> assert = selfHosted && enableCloudCommunication ? Assert.True : Assert.False;
|
||||
|
||||
var result = sutProvider.Sut.ConnectionsEnabled();
|
||||
|
||||
assert(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task CreateConnection_CloudBillingSync_RequiresOwnerPermissions(SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
var model = new OrganizationConnectionRequestModel
|
||||
[Theory]
|
||||
[BitAutoData(true, true)]
|
||||
[BitAutoData(false, true)]
|
||||
[BitAutoData(true, false)]
|
||||
[BitAutoData(false, false)]
|
||||
public void ConnectionEnabled_RequiresBothSelfHostAndCommunications(bool selfHosted, bool enableCloudCommunication, SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
Type = OrganizationConnectionType.CloudBillingSync,
|
||||
};
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.CreateConnection(model));
|
||||
var globalSettingsMock = sutProvider.GetDependency<IGlobalSettings>();
|
||||
globalSettingsMock.SelfHosted.Returns(selfHosted);
|
||||
globalSettingsMock.EnableCloudCommunication.Returns(enableCloudCommunication);
|
||||
|
||||
Assert.Contains($"You do not have permission to create a connection of type", exception.Message);
|
||||
}
|
||||
Action<bool> assert = selfHosted && enableCloudCommunication ? Assert.True : Assert.False;
|
||||
|
||||
[Theory]
|
||||
[BitMemberAutoData(nameof(ConnectionTypes))]
|
||||
public async Task CreateConnection_OnlyOneConnectionOfEachType(OrganizationConnectionType type,
|
||||
OrganizationConnectionRequestModel model, BillingSyncConfig config, Guid existingEntityId,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
model.Type = type;
|
||||
model.Config = JsonDocumentFromObject(config);
|
||||
var typedModel = new OrganizationConnectionRequestModel<BillingSyncConfig>(model);
|
||||
var existing = typedModel.ToData(existingEntityId).ToEntity();
|
||||
var result = sutProvider.Sut.ConnectionsEnabled();
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(model.OrganizationId).Returns(true);
|
||||
assert(result);
|
||||
}
|
||||
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>().GetByOrganizationIdTypeAsync(model.OrganizationId, type).Returns(new[] { existing });
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.CreateConnection(model));
|
||||
|
||||
Assert.Contains($"The requested organization already has a connection of type {model.Type}. Only one of each connection type may exist per organization.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task CreateConnection_BillingSyncType_InvalidLicense_Throws(OrganizationConnectionRequestModel model,
|
||||
BillingSyncConfig config, Guid cloudOrgId, OrganizationLicense organizationLicense,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
model.Type = OrganizationConnectionType.CloudBillingSync;
|
||||
organizationLicense.Id = cloudOrgId;
|
||||
|
||||
model.Config = JsonDocumentFromObject(config);
|
||||
var typedModel = new OrganizationConnectionRequestModel<BillingSyncConfig>(model);
|
||||
typedModel.ParsedConfig.CloudOrganizationId = cloudOrgId;
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.OrganizationOwner(model.OrganizationId)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<ILicensingService>()
|
||||
.ReadOrganizationLicenseAsync(model.OrganizationId)
|
||||
.Returns(organizationLicense);
|
||||
|
||||
sutProvider.GetDependency<ILicensingService>()
|
||||
.VerifyLicense(organizationLicense)
|
||||
.Returns(false);
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.CreateConnection(model));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task CreateConnection_Success(OrganizationConnectionRequestModel model, BillingSyncConfig config,
|
||||
Guid cloudOrgId, OrganizationLicense organizationLicense, SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
organizationLicense.Id = cloudOrgId;
|
||||
|
||||
model.Config = JsonDocumentFromObject(config);
|
||||
var typedModel = new OrganizationConnectionRequestModel<BillingSyncConfig>(model);
|
||||
typedModel.ParsedConfig.CloudOrganizationId = cloudOrgId;
|
||||
|
||||
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
|
||||
sutProvider.GetDependency<ICreateOrganizationConnectionCommand>().CreateAsync<BillingSyncConfig>(default)
|
||||
.ReturnsForAnyArgs(typedModel.ToData(Guid.NewGuid()).ToEntity());
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(model.OrganizationId).Returns(true);
|
||||
sutProvider.GetDependency<ILicensingService>()
|
||||
.ReadOrganizationLicenseAsync(Arg.Any<Guid>())
|
||||
.Returns(organizationLicense);
|
||||
|
||||
sutProvider.GetDependency<ILicensingService>()
|
||||
.VerifyLicense(organizationLicense)
|
||||
.Returns(true);
|
||||
|
||||
await sutProvider.Sut.CreateConnection(model);
|
||||
|
||||
await sutProvider.GetDependency<ICreateOrganizationConnectionCommand>().Received(1)
|
||||
.CreateAsync(Arg.Is(AssertHelper.AssertPropertyEqual(typedModel.ToData())));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateConnection_RequiresOwnerPermissions(SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>()
|
||||
.GetByIdAsync(Arg.Any<Guid>())
|
||||
.Returns(new OrganizationConnection());
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateConnection(default, null));
|
||||
|
||||
Assert.Contains("You do not have permission to update this connection.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(OrganizationConnectionType.CloudBillingSync)]
|
||||
public async Task UpdateConnection_BillingSync_OnlyOneConnectionOfEachType(OrganizationConnectionType type,
|
||||
OrganizationConnection existing1, OrganizationConnection existing2, BillingSyncConfig config,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
existing1.Type = existing2.Type = type;
|
||||
existing1.Config = JsonSerializer.Serialize(config);
|
||||
var typedModel = RequestModelFromEntity<BillingSyncConfig>(existing1);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(typedModel.OrganizationId).Returns(true);
|
||||
|
||||
var orgConnectionRepository = sutProvider.GetDependency<IOrganizationConnectionRepository>();
|
||||
orgConnectionRepository.GetByIdAsync(existing1.Id).Returns(existing1);
|
||||
orgConnectionRepository.GetByIdAsync(existing2.Id).Returns(existing2);
|
||||
orgConnectionRepository.GetByOrganizationIdTypeAsync(typedModel.OrganizationId, type).Returns(new[] { existing1, existing2 });
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateConnection(existing1.Id, typedModel));
|
||||
|
||||
Assert.Contains($"The requested organization already has a connection of type {typedModel.Type}. Only one of each connection type may exist per organization.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(OrganizationConnectionType.Scim)]
|
||||
public async Task UpdateConnection_Scim_OnlyOneConnectionOfEachType(OrganizationConnectionType type,
|
||||
OrganizationConnection existing1, OrganizationConnection existing2, ScimConfig config,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
existing1.Type = existing2.Type = type;
|
||||
existing1.Config = JsonSerializer.Serialize(config);
|
||||
var typedModel = RequestModelFromEntity<ScimConfig>(existing1);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(typedModel.OrganizationId).Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>()
|
||||
.GetByIdAsync(existing1.Id)
|
||||
.Returns(existing1);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageScim(typedModel.OrganizationId).Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>()
|
||||
.GetByOrganizationIdTypeAsync(typedModel.OrganizationId, type)
|
||||
.Returns(new[] { existing1, existing2 });
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateConnection(existing1.Id, typedModel));
|
||||
|
||||
Assert.Contains($"The requested organization already has a connection of type {typedModel.Type}. Only one of each connection type may exist per organization.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateConnection_Success(OrganizationConnection existing, BillingSyncConfig config,
|
||||
OrganizationConnection updated,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
existing.SetConfig(new BillingSyncConfig
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task CreateConnection_CloudBillingSync_RequiresOwnerPermissions(SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
CloudOrganizationId = config.CloudOrganizationId,
|
||||
});
|
||||
updated.Config = JsonSerializer.Serialize(config);
|
||||
updated.Id = existing.Id;
|
||||
updated.Type = OrganizationConnectionType.CloudBillingSync;
|
||||
var model = RequestModelFromEntity<BillingSyncConfig>(updated);
|
||||
var model = new OrganizationConnectionRequestModel
|
||||
{
|
||||
Type = OrganizationConnectionType.CloudBillingSync,
|
||||
};
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.CreateConnection(model));
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(model.OrganizationId).Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>()
|
||||
.GetByOrganizationIdTypeAsync(model.OrganizationId, model.Type)
|
||||
.Returns(new[] { existing });
|
||||
sutProvider.GetDependency<IUpdateOrganizationConnectionCommand>()
|
||||
.UpdateAsync<BillingSyncConfig>(default)
|
||||
.ReturnsForAnyArgs(updated);
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>()
|
||||
.GetByIdAsync(existing.Id)
|
||||
.Returns(existing);
|
||||
Assert.Contains($"You do not have permission to create a connection of type", exception.Message);
|
||||
}
|
||||
|
||||
var expected = new OrganizationConnectionResponseModel(updated, typeof(BillingSyncConfig));
|
||||
var result = await sutProvider.Sut.UpdateConnection(existing.Id, model);
|
||||
|
||||
AssertHelper.AssertPropertyEqual(expected, result);
|
||||
await sutProvider.GetDependency<IUpdateOrganizationConnectionCommand>().Received(1)
|
||||
.UpdateAsync(Arg.Is(AssertHelper.AssertPropertyEqual(model.ToData(updated.Id))));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateConnection_DoesNotExist_ThrowsNotFound(SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateConnection(Guid.NewGuid(), null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetConnection_RequiresOwnerPermissions(Guid connectionId, SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.GetConnection(connectionId, OrganizationConnectionType.CloudBillingSync));
|
||||
|
||||
Assert.Contains("You do not have permission to retrieve a connection of type", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetConnection_Success(OrganizationConnection connection, BillingSyncConfig config,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
connection.Config = JsonSerializer.Serialize(config);
|
||||
|
||||
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>()
|
||||
.GetByOrganizationIdTypeAsync(connection.OrganizationId, connection.Type)
|
||||
.Returns(new[] { connection });
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(connection.OrganizationId).Returns(true);
|
||||
|
||||
var expected = new OrganizationConnectionResponseModel(connection, typeof(BillingSyncConfig));
|
||||
var actual = await sutProvider.Sut.GetConnection(connection.OrganizationId, connection.Type);
|
||||
|
||||
AssertHelper.AssertPropertyEqual(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteConnection_NotFound(Guid connectionId,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteConnection(connectionId));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteConnection_RequiresOwnerPermissions(OrganizationConnection connection,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>().GetByIdAsync(connection.Id).Returns(connection);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.DeleteConnection(connection.Id));
|
||||
|
||||
Assert.Contains("You do not have permission to remove this connection of type", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteConnection_Success(OrganizationConnection connection,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>().GetByIdAsync(connection.Id).Returns(connection);
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(connection.OrganizationId).Returns(true);
|
||||
|
||||
await sutProvider.Sut.DeleteConnection(connection.Id);
|
||||
|
||||
await sutProvider.GetDependency<IDeleteOrganizationConnectionCommand>().DeleteAsync(connection);
|
||||
}
|
||||
|
||||
private static OrganizationConnectionRequestModel<T> RequestModelFromEntity<T>(OrganizationConnection entity)
|
||||
where T : new()
|
||||
{
|
||||
return new(new OrganizationConnectionRequestModel()
|
||||
[Theory]
|
||||
[BitMemberAutoData(nameof(ConnectionTypes))]
|
||||
public async Task CreateConnection_OnlyOneConnectionOfEachType(OrganizationConnectionType type,
|
||||
OrganizationConnectionRequestModel model, BillingSyncConfig config, Guid existingEntityId,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
Type = entity.Type,
|
||||
OrganizationId = entity.OrganizationId,
|
||||
Enabled = entity.Enabled,
|
||||
Config = JsonDocument.Parse(entity.Config),
|
||||
});
|
||||
}
|
||||
model.Type = type;
|
||||
model.Config = JsonDocumentFromObject(config);
|
||||
var typedModel = new OrganizationConnectionRequestModel<BillingSyncConfig>(model);
|
||||
var existing = typedModel.ToData(existingEntityId).ToEntity();
|
||||
|
||||
private static JsonDocument JsonDocumentFromObject<T>(T obj) => JsonDocument.Parse(JsonSerializer.Serialize(obj));
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(model.OrganizationId).Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>().GetByOrganizationIdTypeAsync(model.OrganizationId, type).Returns(new[] { existing });
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.CreateConnection(model));
|
||||
|
||||
Assert.Contains($"The requested organization already has a connection of type {model.Type}. Only one of each connection type may exist per organization.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task CreateConnection_BillingSyncType_InvalidLicense_Throws(OrganizationConnectionRequestModel model,
|
||||
BillingSyncConfig config, Guid cloudOrgId, OrganizationLicense organizationLicense,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
model.Type = OrganizationConnectionType.CloudBillingSync;
|
||||
organizationLicense.Id = cloudOrgId;
|
||||
|
||||
model.Config = JsonDocumentFromObject(config);
|
||||
var typedModel = new OrganizationConnectionRequestModel<BillingSyncConfig>(model);
|
||||
typedModel.ParsedConfig.CloudOrganizationId = cloudOrgId;
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.OrganizationOwner(model.OrganizationId)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.GetDependency<ILicensingService>()
|
||||
.ReadOrganizationLicenseAsync(model.OrganizationId)
|
||||
.Returns(organizationLicense);
|
||||
|
||||
sutProvider.GetDependency<ILicensingService>()
|
||||
.VerifyLicense(organizationLicense)
|
||||
.Returns(false);
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.CreateConnection(model));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task CreateConnection_Success(OrganizationConnectionRequestModel model, BillingSyncConfig config,
|
||||
Guid cloudOrgId, OrganizationLicense organizationLicense, SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
organizationLicense.Id = cloudOrgId;
|
||||
|
||||
model.Config = JsonDocumentFromObject(config);
|
||||
var typedModel = new OrganizationConnectionRequestModel<BillingSyncConfig>(model);
|
||||
typedModel.ParsedConfig.CloudOrganizationId = cloudOrgId;
|
||||
|
||||
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
|
||||
sutProvider.GetDependency<ICreateOrganizationConnectionCommand>().CreateAsync<BillingSyncConfig>(default)
|
||||
.ReturnsForAnyArgs(typedModel.ToData(Guid.NewGuid()).ToEntity());
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(model.OrganizationId).Returns(true);
|
||||
sutProvider.GetDependency<ILicensingService>()
|
||||
.ReadOrganizationLicenseAsync(Arg.Any<Guid>())
|
||||
.Returns(organizationLicense);
|
||||
|
||||
sutProvider.GetDependency<ILicensingService>()
|
||||
.VerifyLicense(organizationLicense)
|
||||
.Returns(true);
|
||||
|
||||
await sutProvider.Sut.CreateConnection(model);
|
||||
|
||||
await sutProvider.GetDependency<ICreateOrganizationConnectionCommand>().Received(1)
|
||||
.CreateAsync(Arg.Is(AssertHelper.AssertPropertyEqual(typedModel.ToData())));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateConnection_RequiresOwnerPermissions(SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>()
|
||||
.GetByIdAsync(Arg.Any<Guid>())
|
||||
.Returns(new OrganizationConnection());
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateConnection(default, null));
|
||||
|
||||
Assert.Contains("You do not have permission to update this connection.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(OrganizationConnectionType.CloudBillingSync)]
|
||||
public async Task UpdateConnection_BillingSync_OnlyOneConnectionOfEachType(OrganizationConnectionType type,
|
||||
OrganizationConnection existing1, OrganizationConnection existing2, BillingSyncConfig config,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
existing1.Type = existing2.Type = type;
|
||||
existing1.Config = JsonSerializer.Serialize(config);
|
||||
var typedModel = RequestModelFromEntity<BillingSyncConfig>(existing1);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(typedModel.OrganizationId).Returns(true);
|
||||
|
||||
var orgConnectionRepository = sutProvider.GetDependency<IOrganizationConnectionRepository>();
|
||||
orgConnectionRepository.GetByIdAsync(existing1.Id).Returns(existing1);
|
||||
orgConnectionRepository.GetByIdAsync(existing2.Id).Returns(existing2);
|
||||
orgConnectionRepository.GetByOrganizationIdTypeAsync(typedModel.OrganizationId, type).Returns(new[] { existing1, existing2 });
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateConnection(existing1.Id, typedModel));
|
||||
|
||||
Assert.Contains($"The requested organization already has a connection of type {typedModel.Type}. Only one of each connection type may exist per organization.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(OrganizationConnectionType.Scim)]
|
||||
public async Task UpdateConnection_Scim_OnlyOneConnectionOfEachType(OrganizationConnectionType type,
|
||||
OrganizationConnection existing1, OrganizationConnection existing2, ScimConfig config,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
existing1.Type = existing2.Type = type;
|
||||
existing1.Config = JsonSerializer.Serialize(config);
|
||||
var typedModel = RequestModelFromEntity<ScimConfig>(existing1);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(typedModel.OrganizationId).Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>()
|
||||
.GetByIdAsync(existing1.Id)
|
||||
.Returns(existing1);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageScim(typedModel.OrganizationId).Returns(true);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>()
|
||||
.GetByOrganizationIdTypeAsync(typedModel.OrganizationId, type)
|
||||
.Returns(new[] { existing1, existing2 });
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateConnection(existing1.Id, typedModel));
|
||||
|
||||
Assert.Contains($"The requested organization already has a connection of type {typedModel.Type}. Only one of each connection type may exist per organization.", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateConnection_Success(OrganizationConnection existing, BillingSyncConfig config,
|
||||
OrganizationConnection updated,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
existing.SetConfig(new BillingSyncConfig
|
||||
{
|
||||
CloudOrganizationId = config.CloudOrganizationId,
|
||||
});
|
||||
updated.Config = JsonSerializer.Serialize(config);
|
||||
updated.Id = existing.Id;
|
||||
updated.Type = OrganizationConnectionType.CloudBillingSync;
|
||||
var model = RequestModelFromEntity<BillingSyncConfig>(updated);
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(model.OrganizationId).Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>()
|
||||
.GetByOrganizationIdTypeAsync(model.OrganizationId, model.Type)
|
||||
.Returns(new[] { existing });
|
||||
sutProvider.GetDependency<IUpdateOrganizationConnectionCommand>()
|
||||
.UpdateAsync<BillingSyncConfig>(default)
|
||||
.ReturnsForAnyArgs(updated);
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>()
|
||||
.GetByIdAsync(existing.Id)
|
||||
.Returns(existing);
|
||||
|
||||
var expected = new OrganizationConnectionResponseModel(updated, typeof(BillingSyncConfig));
|
||||
var result = await sutProvider.Sut.UpdateConnection(existing.Id, model);
|
||||
|
||||
AssertHelper.AssertPropertyEqual(expected, result);
|
||||
await sutProvider.GetDependency<IUpdateOrganizationConnectionCommand>().Received(1)
|
||||
.UpdateAsync(Arg.Is(AssertHelper.AssertPropertyEqual(model.ToData(updated.Id))));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateConnection_DoesNotExist_ThrowsNotFound(SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateConnection(Guid.NewGuid(), null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetConnection_RequiresOwnerPermissions(Guid connectionId, SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.GetConnection(connectionId, OrganizationConnectionType.CloudBillingSync));
|
||||
|
||||
Assert.Contains("You do not have permission to retrieve a connection of type", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetConnection_Success(OrganizationConnection connection, BillingSyncConfig config,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
connection.Config = JsonSerializer.Serialize(config);
|
||||
|
||||
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>()
|
||||
.GetByOrganizationIdTypeAsync(connection.OrganizationId, connection.Type)
|
||||
.Returns(new[] { connection });
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(connection.OrganizationId).Returns(true);
|
||||
|
||||
var expected = new OrganizationConnectionResponseModel(connection, typeof(BillingSyncConfig));
|
||||
var actual = await sutProvider.Sut.GetConnection(connection.OrganizationId, connection.Type);
|
||||
|
||||
AssertHelper.AssertPropertyEqual(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteConnection_NotFound(Guid connectionId,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteConnection(connectionId));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteConnection_RequiresOwnerPermissions(OrganizationConnection connection,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>().GetByIdAsync(connection.Id).Returns(connection);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.DeleteConnection(connection.Id));
|
||||
|
||||
Assert.Contains("You do not have permission to remove this connection of type", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteConnection_Success(OrganizationConnection connection,
|
||||
SutProvider<OrganizationConnectionsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationConnectionRepository>().GetByIdAsync(connection.Id).Returns(connection);
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(connection.OrganizationId).Returns(true);
|
||||
|
||||
await sutProvider.Sut.DeleteConnection(connection.Id);
|
||||
|
||||
await sutProvider.GetDependency<IDeleteOrganizationConnectionCommand>().DeleteAsync(connection);
|
||||
}
|
||||
|
||||
private static OrganizationConnectionRequestModel<T> RequestModelFromEntity<T>(OrganizationConnection entity)
|
||||
where T : new()
|
||||
{
|
||||
return new(new OrganizationConnectionRequestModel()
|
||||
{
|
||||
Type = entity.Type,
|
||||
OrganizationId = entity.OrganizationId,
|
||||
Enabled = entity.Enabled,
|
||||
Config = JsonDocument.Parse(entity.Config),
|
||||
});
|
||||
}
|
||||
|
||||
private static JsonDocument JsonDocumentFromObject<T>(T obj) => JsonDocument.Parse(JsonSerializer.Serialize(obj));
|
||||
}
|
||||
}
|
||||
|
@ -13,135 +13,136 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Api.Test.Controllers;
|
||||
|
||||
[ControllerCustomize(typeof(OrganizationSponsorshipsController))]
|
||||
[SutProviderCustomize]
|
||||
public class OrganizationSponsorshipsControllerTests
|
||||
namespace Bit.Api.Test.Controllers
|
||||
{
|
||||
public static IEnumerable<object[]> EnterprisePlanTypes =>
|
||||
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p });
|
||||
public static IEnumerable<object[]> NonEnterprisePlanTypes =>
|
||||
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p });
|
||||
public static IEnumerable<object[]> NonFamiliesPlanTypes =>
|
||||
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Families).Select(p => new object[] { p });
|
||||
|
||||
public static IEnumerable<object[]> NonConfirmedOrganizationUsersStatuses =>
|
||||
Enum.GetValues<OrganizationUserStatusType>()
|
||||
.Where(s => s != OrganizationUserStatusType.Confirmed)
|
||||
.Select(s => new object[] { s });
|
||||
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RedeemSponsorship_BadToken_ThrowsBadRequest(string sponsorshipToken, User user,
|
||||
OrganizationSponsorshipRedeemRequestModel model, SutProvider<OrganizationSponsorshipsController> sutProvider)
|
||||
[ControllerCustomize(typeof(OrganizationSponsorshipsController))]
|
||||
[SutProviderCustomize]
|
||||
public class OrganizationSponsorshipsControllerTests
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(user.Id);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
sutProvider.GetDependency<IValidateRedemptionTokenCommand>().ValidateRedemptionTokenAsync(sponsorshipToken,
|
||||
user.Email).Returns((false, null));
|
||||
public static IEnumerable<object[]> EnterprisePlanTypes =>
|
||||
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p });
|
||||
public static IEnumerable<object[]> NonEnterprisePlanTypes =>
|
||||
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p });
|
||||
public static IEnumerable<object[]> NonFamiliesPlanTypes =>
|
||||
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Families).Select(p => new object[] { p });
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.RedeemSponsorship(sponsorshipToken, model));
|
||||
public static IEnumerable<object[]> NonConfirmedOrganizationUsersStatuses =>
|
||||
Enum.GetValues<OrganizationUserStatusType>()
|
||||
.Where(s => s != OrganizationUserStatusType.Confirmed)
|
||||
.Select(s => new object[] { s });
|
||||
|
||||
Assert.Contains("Failed to parse sponsorship token.", exception.Message);
|
||||
await sutProvider.GetDependency<ISetUpSponsorshipCommand>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.SetUpSponsorshipAsync(default, default);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RedeemSponsorship_NotSponsoredOrgOwner_ThrowsBadRequest(string sponsorshipToken, User user,
|
||||
OrganizationSponsorship sponsorship, OrganizationSponsorshipRedeemRequestModel model,
|
||||
SutProvider<OrganizationSponsorshipsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(user.Id);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
sutProvider.GetDependency<IValidateRedemptionTokenCommand>().ValidateRedemptionTokenAsync(sponsorshipToken,
|
||||
user.Email).Returns((true, sponsorship));
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(model.SponsoredOrganizationId).Returns(false);
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RedeemSponsorship_BadToken_ThrowsBadRequest(string sponsorshipToken, User user,
|
||||
OrganizationSponsorshipRedeemRequestModel model, SutProvider<OrganizationSponsorshipsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(user.Id);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
sutProvider.GetDependency<IValidateRedemptionTokenCommand>().ValidateRedemptionTokenAsync(sponsorshipToken,
|
||||
user.Email).Returns((false, null));
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.RedeemSponsorship(sponsorshipToken, model));
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.RedeemSponsorship(sponsorshipToken, model));
|
||||
|
||||
Assert.Contains("Can only redeem sponsorship for an organization you own.", exception.Message);
|
||||
await sutProvider.GetDependency<ISetUpSponsorshipCommand>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.SetUpSponsorshipAsync(default, default);
|
||||
}
|
||||
Assert.Contains("Failed to parse sponsorship token.", exception.Message);
|
||||
await sutProvider.GetDependency<ISetUpSponsorshipCommand>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.SetUpSponsorshipAsync(default, default);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RedeemSponsorship_NotSponsoredOrgOwner_Success(string sponsorshipToken, User user,
|
||||
OrganizationSponsorship sponsorship, Organization sponsoringOrganization,
|
||||
OrganizationSponsorshipRedeemRequestModel model, SutProvider<OrganizationSponsorshipsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(user.Id);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
sutProvider.GetDependency<IValidateRedemptionTokenCommand>().ValidateRedemptionTokenAsync(sponsorshipToken,
|
||||
user.Email).Returns((true, sponsorship));
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(model.SponsoredOrganizationId).Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(model.SponsoredOrganizationId).Returns(sponsoringOrganization);
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RedeemSponsorship_NotSponsoredOrgOwner_ThrowsBadRequest(string sponsorshipToken, User user,
|
||||
OrganizationSponsorship sponsorship, OrganizationSponsorshipRedeemRequestModel model,
|
||||
SutProvider<OrganizationSponsorshipsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(user.Id);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
sutProvider.GetDependency<IValidateRedemptionTokenCommand>().ValidateRedemptionTokenAsync(sponsorshipToken,
|
||||
user.Email).Returns((true, sponsorship));
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(model.SponsoredOrganizationId).Returns(false);
|
||||
|
||||
await sutProvider.Sut.RedeemSponsorship(sponsorshipToken, model);
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.RedeemSponsorship(sponsorshipToken, model));
|
||||
|
||||
await sutProvider.GetDependency<ISetUpSponsorshipCommand>().Received(1)
|
||||
.SetUpSponsorshipAsync(sponsorship, sponsoringOrganization);
|
||||
}
|
||||
Assert.Contains("Can only redeem sponsorship for an organization you own.", exception.Message);
|
||||
await sutProvider.GetDependency<ISetUpSponsorshipCommand>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.SetUpSponsorshipAsync(default, default);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task PreValidateSponsorshipToken_ValidatesToken_Success(string sponsorshipToken, User user,
|
||||
OrganizationSponsorship sponsorship, SutProvider<OrganizationSponsorshipsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(user.Id);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
sutProvider.GetDependency<IValidateRedemptionTokenCommand>()
|
||||
.ValidateRedemptionTokenAsync(sponsorshipToken, user.Email).Returns((true, sponsorship));
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RedeemSponsorship_NotSponsoredOrgOwner_Success(string sponsorshipToken, User user,
|
||||
OrganizationSponsorship sponsorship, Organization sponsoringOrganization,
|
||||
OrganizationSponsorshipRedeemRequestModel model, SutProvider<OrganizationSponsorshipsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(user.Id);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
sutProvider.GetDependency<IValidateRedemptionTokenCommand>().ValidateRedemptionTokenAsync(sponsorshipToken,
|
||||
user.Email).Returns((true, sponsorship));
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(model.SponsoredOrganizationId).Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(model.SponsoredOrganizationId).Returns(sponsoringOrganization);
|
||||
|
||||
await sutProvider.Sut.PreValidateSponsorshipToken(sponsorshipToken);
|
||||
await sutProvider.Sut.RedeemSponsorship(sponsorshipToken, model);
|
||||
|
||||
await sutProvider.GetDependency<IValidateRedemptionTokenCommand>().Received(1)
|
||||
.ValidateRedemptionTokenAsync(sponsorshipToken, user.Email);
|
||||
}
|
||||
await sutProvider.GetDependency<ISetUpSponsorshipCommand>().Received(1)
|
||||
.SetUpSponsorshipAsync(sponsorship, sponsoringOrganization);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RevokeSponsorship_WrongSponsoringUser_ThrowsBadRequest(OrganizationUser sponsoringOrgUser,
|
||||
Guid currentUserId, SutProvider<OrganizationSponsorshipsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(currentUserId);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetByIdAsync(sponsoringOrgUser.Id)
|
||||
.Returns(sponsoringOrgUser);
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task PreValidateSponsorshipToken_ValidatesToken_Success(string sponsorshipToken, User user,
|
||||
OrganizationSponsorship sponsorship, SutProvider<OrganizationSponsorshipsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(user.Id);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(user.Id)
|
||||
.Returns(user);
|
||||
sutProvider.GetDependency<IValidateRedemptionTokenCommand>()
|
||||
.ValidateRedemptionTokenAsync(sponsorshipToken, user.Email).Returns((true, sponsorship));
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.RevokeSponsorship(sponsoringOrgUser.Id));
|
||||
await sutProvider.Sut.PreValidateSponsorshipToken(sponsorshipToken);
|
||||
|
||||
Assert.Contains("Can only revoke a sponsorship you granted.", exception.Message);
|
||||
await sutProvider.GetDependency<IRemoveSponsorshipCommand>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.RemoveSponsorshipAsync(default);
|
||||
}
|
||||
await sutProvider.GetDependency<IValidateRedemptionTokenCommand>().Received(1)
|
||||
.ValidateRedemptionTokenAsync(sponsorshipToken, user.Email);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RemoveSponsorship_WrongOrgUserType_ThrowsBadRequest(Organization sponsoredOrg,
|
||||
SutProvider<OrganizationSponsorshipsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(Arg.Any<Guid>()).Returns(false);
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RevokeSponsorship_WrongSponsoringUser_ThrowsBadRequest(OrganizationUser sponsoringOrgUser,
|
||||
Guid currentUserId, SutProvider<OrganizationSponsorshipsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(currentUserId);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetByIdAsync(sponsoringOrgUser.Id)
|
||||
.Returns(sponsoringOrgUser);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.RemoveSponsorship(sponsoredOrg.Id));
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.RevokeSponsorship(sponsoringOrgUser.Id));
|
||||
|
||||
Assert.Contains("Only the owner of an organization can remove sponsorship.", exception.Message);
|
||||
await sutProvider.GetDependency<IRemoveSponsorshipCommand>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.RemoveSponsorshipAsync(default);
|
||||
Assert.Contains("Can only revoke a sponsorship you granted.", exception.Message);
|
||||
await sutProvider.GetDependency<IRemoveSponsorshipCommand>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.RemoveSponsorshipAsync(default);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RemoveSponsorship_WrongOrgUserType_ThrowsBadRequest(Organization sponsoredOrg,
|
||||
SutProvider<OrganizationSponsorshipsController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(Arg.Any<Guid>()).Returns(false);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.RemoveSponsorship(sponsoredOrg.Id));
|
||||
|
||||
Assert.Contains("Only the owner of an organization can remove sponsorship.", exception.Message);
|
||||
await sutProvider.GetDependency<IRemoveSponsorshipCommand>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.RemoveSponsorshipAsync(default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,56 +10,57 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Api.Test.Controllers;
|
||||
|
||||
[ControllerCustomize(typeof(OrganizationUsersController))]
|
||||
[SutProviderCustomize]
|
||||
public class OrganizationUsersControllerTests
|
||||
namespace Bit.Api.Test.Controllers
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Accept_RequiresKnownUser(Guid orgId, Guid orgUserId, OrganizationUserAcceptRequestModel model,
|
||||
SutProvider<OrganizationUsersController> sutProvider)
|
||||
[ControllerCustomize(typeof(OrganizationUsersController))]
|
||||
[SutProviderCustomize]
|
||||
public class OrganizationUsersControllerTests
|
||||
{
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs((User)null);
|
||||
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => sutProvider.Sut.Accept(orgId, orgUserId, model));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Accept_NoMasterPasswordReset(Guid orgId, Guid orgUserId,
|
||||
OrganizationUserAcceptRequestModel model, User user, SutProvider<OrganizationUsersController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
|
||||
|
||||
await sutProvider.Sut.Accept(orgId, orgUserId, model);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1)
|
||||
.AcceptUserAsync(orgUserId, user, model.Token, sutProvider.GetDependency<IUserService>());
|
||||
await sutProvider.GetDependency<IOrganizationService>().DidNotReceiveWithAnyArgs()
|
||||
.UpdateUserResetPasswordEnrollmentAsync(default, default, default, default);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Accept_RequireMasterPasswordReset(Guid orgId, Guid orgUserId,
|
||||
OrganizationUserAcceptRequestModel model, User user, SutProvider<OrganizationUsersController> sutProvider)
|
||||
{
|
||||
var policy = new Policy
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Accept_RequiresKnownUser(Guid orgId, Guid orgUserId, OrganizationUserAcceptRequestModel model,
|
||||
SutProvider<OrganizationUsersController> sutProvider)
|
||||
{
|
||||
Enabled = true,
|
||||
Data = CoreHelpers.ClassToJsonData(new ResetPasswordDataModel { AutoEnrollEnabled = true, }),
|
||||
};
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
|
||||
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(orgId,
|
||||
Core.Enums.PolicyType.ResetPassword).Returns(policy);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs((User)null);
|
||||
|
||||
await sutProvider.Sut.Accept(orgId, orgUserId, model);
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => sutProvider.Sut.Accept(orgId, orgUserId, model));
|
||||
}
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1)
|
||||
.AcceptUserAsync(orgUserId, user, model.Token, sutProvider.GetDependency<IUserService>());
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1)
|
||||
.UpdateUserResetPasswordEnrollmentAsync(orgId, user.Id, model.ResetPasswordKey, user.Id);
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Accept_NoMasterPasswordReset(Guid orgId, Guid orgUserId,
|
||||
OrganizationUserAcceptRequestModel model, User user, SutProvider<OrganizationUsersController> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
|
||||
|
||||
await sutProvider.Sut.Accept(orgId, orgUserId, model);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1)
|
||||
.AcceptUserAsync(orgUserId, user, model.Token, sutProvider.GetDependency<IUserService>());
|
||||
await sutProvider.GetDependency<IOrganizationService>().DidNotReceiveWithAnyArgs()
|
||||
.UpdateUserResetPasswordEnrollmentAsync(default, default, default, default);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Accept_RequireMasterPasswordReset(Guid orgId, Guid orgUserId,
|
||||
OrganizationUserAcceptRequestModel model, User user, SutProvider<OrganizationUsersController> sutProvider)
|
||||
{
|
||||
var policy = new Policy
|
||||
{
|
||||
Enabled = true,
|
||||
Data = CoreHelpers.ClassToJsonData(new ResetPasswordDataModel { AutoEnrollEnabled = true, }),
|
||||
};
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
|
||||
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(orgId,
|
||||
Core.Enums.PolicyType.ResetPassword).Returns(policy);
|
||||
|
||||
await sutProvider.Sut.Accept(orgId, orgUserId, model);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1)
|
||||
.AcceptUserAsync(orgUserId, user, model.Token, sutProvider.GetDependency<IUserService>());
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1)
|
||||
.UpdateUserResetPasswordEnrollmentAsync(orgId, user.Id, model.ResetPasswordKey, user.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,108 +12,109 @@ using Bit.Core.Settings;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Api.Test.Controllers;
|
||||
|
||||
public class OrganizationsControllerTests : IDisposable
|
||||
namespace Bit.Api.Test.Controllers
|
||||
{
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly IPolicyRepository _policyRepository;
|
||||
private readonly ISsoConfigRepository _ssoConfigRepository;
|
||||
private readonly ISsoConfigService _ssoConfigService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IGetOrganizationApiKeyCommand _getOrganizationApiKeyCommand;
|
||||
private readonly IRotateOrganizationApiKeyCommand _rotateOrganizationApiKeyCommand;
|
||||
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
|
||||
|
||||
private readonly OrganizationsController _sut;
|
||||
|
||||
public OrganizationsControllerTests()
|
||||
public class OrganizationsControllerTests : IDisposable
|
||||
{
|
||||
_currentContext = Substitute.For<ICurrentContext>();
|
||||
_globalSettings = Substitute.For<GlobalSettings>();
|
||||
_organizationRepository = Substitute.For<IOrganizationRepository>();
|
||||
_organizationService = Substitute.For<IOrganizationService>();
|
||||
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||
_paymentService = Substitute.For<IPaymentService>();
|
||||
_policyRepository = Substitute.For<IPolicyRepository>();
|
||||
_ssoConfigRepository = Substitute.For<ISsoConfigRepository>();
|
||||
_ssoConfigService = Substitute.For<ISsoConfigService>();
|
||||
_getOrganizationApiKeyCommand = Substitute.For<IGetOrganizationApiKeyCommand>();
|
||||
_rotateOrganizationApiKeyCommand = Substitute.For<IRotateOrganizationApiKeyCommand>();
|
||||
_organizationApiKeyRepository = Substitute.For<IOrganizationApiKeyRepository>();
|
||||
_userService = Substitute.For<IUserService>();
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly IPolicyRepository _policyRepository;
|
||||
private readonly ISsoConfigRepository _ssoConfigRepository;
|
||||
private readonly ISsoConfigService _ssoConfigService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IGetOrganizationApiKeyCommand _getOrganizationApiKeyCommand;
|
||||
private readonly IRotateOrganizationApiKeyCommand _rotateOrganizationApiKeyCommand;
|
||||
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
|
||||
|
||||
_sut = new OrganizationsController(_organizationRepository, _organizationUserRepository,
|
||||
_policyRepository, _organizationService, _userService, _paymentService, _currentContext,
|
||||
_ssoConfigRepository, _ssoConfigService, _getOrganizationApiKeyCommand, _rotateOrganizationApiKeyCommand,
|
||||
_organizationApiKeyRepository, _globalSettings);
|
||||
}
|
||||
private readonly OrganizationsController _sut;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_sut?.Dispose();
|
||||
}
|
||||
|
||||
[Theory, AutoData]
|
||||
public async Task OrganizationsController_UserCannotLeaveOrganizationThatProvidesKeyConnector(
|
||||
Guid orgId, User user)
|
||||
{
|
||||
var ssoConfig = new SsoConfig
|
||||
public OrganizationsControllerTests()
|
||||
{
|
||||
Id = default,
|
||||
Data = new SsoConfigurationData
|
||||
{
|
||||
KeyConnectorEnabled = true,
|
||||
}.Serialize(),
|
||||
Enabled = true,
|
||||
OrganizationId = orgId,
|
||||
};
|
||||
_currentContext = Substitute.For<ICurrentContext>();
|
||||
_globalSettings = Substitute.For<GlobalSettings>();
|
||||
_organizationRepository = Substitute.For<IOrganizationRepository>();
|
||||
_organizationService = Substitute.For<IOrganizationService>();
|
||||
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||
_paymentService = Substitute.For<IPaymentService>();
|
||||
_policyRepository = Substitute.For<IPolicyRepository>();
|
||||
_ssoConfigRepository = Substitute.For<ISsoConfigRepository>();
|
||||
_ssoConfigService = Substitute.For<ISsoConfigService>();
|
||||
_getOrganizationApiKeyCommand = Substitute.For<IGetOrganizationApiKeyCommand>();
|
||||
_rotateOrganizationApiKeyCommand = Substitute.For<IRotateOrganizationApiKeyCommand>();
|
||||
_organizationApiKeyRepository = Substitute.For<IOrganizationApiKeyRepository>();
|
||||
_userService = Substitute.For<IUserService>();
|
||||
|
||||
user.UsesKeyConnector = true;
|
||||
_sut = new OrganizationsController(_organizationRepository, _organizationUserRepository,
|
||||
_policyRepository, _organizationService, _userService, _paymentService, _currentContext,
|
||||
_ssoConfigRepository, _ssoConfigService, _getOrganizationApiKeyCommand, _rotateOrganizationApiKeyCommand,
|
||||
_organizationApiKeyRepository, _globalSettings);
|
||||
}
|
||||
|
||||
_currentContext.OrganizationUser(orgId).Returns(true);
|
||||
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => _sut.Leave(orgId.ToString()));
|
||||
|
||||
Assert.Contains("Your organization's Single Sign-On settings prevent you from leaving.",
|
||||
exception.Message);
|
||||
|
||||
await _organizationService.DidNotReceiveWithAnyArgs().DeleteUserAsync(default, default);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineAutoData(true, false)]
|
||||
[InlineAutoData(false, true)]
|
||||
[InlineAutoData(false, false)]
|
||||
public async Task OrganizationsController_UserCanLeaveOrganizationThatDoesntProvideKeyConnector(
|
||||
bool keyConnectorEnabled, bool userUsesKeyConnector, Guid orgId, User user)
|
||||
{
|
||||
var ssoConfig = new SsoConfig
|
||||
public void Dispose()
|
||||
{
|
||||
Id = default,
|
||||
Data = new SsoConfigurationData
|
||||
_sut?.Dispose();
|
||||
}
|
||||
|
||||
[Theory, AutoData]
|
||||
public async Task OrganizationsController_UserCannotLeaveOrganizationThatProvidesKeyConnector(
|
||||
Guid orgId, User user)
|
||||
{
|
||||
var ssoConfig = new SsoConfig
|
||||
{
|
||||
KeyConnectorEnabled = keyConnectorEnabled,
|
||||
}.Serialize(),
|
||||
Enabled = true,
|
||||
OrganizationId = orgId,
|
||||
};
|
||||
Id = default,
|
||||
Data = new SsoConfigurationData
|
||||
{
|
||||
KeyConnectorEnabled = true,
|
||||
}.Serialize(),
|
||||
Enabled = true,
|
||||
OrganizationId = orgId,
|
||||
};
|
||||
|
||||
user.UsesKeyConnector = userUsesKeyConnector;
|
||||
user.UsesKeyConnector = true;
|
||||
|
||||
_currentContext.OrganizationUser(orgId).Returns(true);
|
||||
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
||||
_currentContext.OrganizationUser(orgId).Returns(true);
|
||||
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
||||
|
||||
await _organizationService.DeleteUserAsync(orgId, user.Id);
|
||||
await _organizationService.Received(1).DeleteUserAsync(orgId, user.Id);
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => _sut.Leave(orgId.ToString()));
|
||||
|
||||
Assert.Contains("Your organization's Single Sign-On settings prevent you from leaving.",
|
||||
exception.Message);
|
||||
|
||||
await _organizationService.DidNotReceiveWithAnyArgs().DeleteUserAsync(default, default);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineAutoData(true, false)]
|
||||
[InlineAutoData(false, true)]
|
||||
[InlineAutoData(false, false)]
|
||||
public async Task OrganizationsController_UserCanLeaveOrganizationThatDoesntProvideKeyConnector(
|
||||
bool keyConnectorEnabled, bool userUsesKeyConnector, Guid orgId, User user)
|
||||
{
|
||||
var ssoConfig = new SsoConfig
|
||||
{
|
||||
Id = default,
|
||||
Data = new SsoConfigurationData
|
||||
{
|
||||
KeyConnectorEnabled = keyConnectorEnabled,
|
||||
}.Serialize(),
|
||||
Enabled = true,
|
||||
OrganizationId = orgId,
|
||||
};
|
||||
|
||||
user.UsesKeyConnector = userUsesKeyConnector;
|
||||
|
||||
_currentContext.OrganizationUser(orgId).Returns(true);
|
||||
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
|
||||
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
|
||||
|
||||
await _organizationService.DeleteUserAsync(orgId, user.Id);
|
||||
await _organizationService.Received(1).DeleteUserAsync(orgId, user.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,65 +15,66 @@ using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Api.Test.Controllers;
|
||||
|
||||
public class SendsControllerTests : IDisposable
|
||||
namespace Bit.Api.Test.Controllers
|
||||
{
|
||||
|
||||
private readonly SendsController _sut;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ISendRepository _sendRepository;
|
||||
private readonly ISendService _sendService;
|
||||
private readonly ISendFileStorageService _sendFileStorageService;
|
||||
private readonly ILogger<SendsController> _logger;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public SendsControllerTests()
|
||||
public class SendsControllerTests : IDisposable
|
||||
{
|
||||
_userService = Substitute.For<IUserService>();
|
||||
_sendRepository = Substitute.For<ISendRepository>();
|
||||
_sendService = Substitute.For<ISendService>();
|
||||
_sendFileStorageService = Substitute.For<ISendFileStorageService>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
_logger = Substitute.For<ILogger<SendsController>>();
|
||||
_currentContext = Substitute.For<ICurrentContext>();
|
||||
|
||||
_sut = new SendsController(
|
||||
_sendRepository,
|
||||
_userService,
|
||||
_sendService,
|
||||
_sendFileStorageService,
|
||||
_logger,
|
||||
_globalSettings,
|
||||
_currentContext
|
||||
);
|
||||
}
|
||||
private readonly SendsController _sut;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ISendRepository _sendRepository;
|
||||
private readonly ISendService _sendService;
|
||||
private readonly ISendFileStorageService _sendFileStorageService;
|
||||
private readonly ILogger<SendsController> _logger;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_sut?.Dispose();
|
||||
}
|
||||
public SendsControllerTests()
|
||||
{
|
||||
_userService = Substitute.For<IUserService>();
|
||||
_sendRepository = Substitute.For<ISendRepository>();
|
||||
_sendService = Substitute.For<ISendService>();
|
||||
_sendFileStorageService = Substitute.For<ISendFileStorageService>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
_logger = Substitute.For<ILogger<SendsController>>();
|
||||
_currentContext = Substitute.For<ICurrentContext>();
|
||||
|
||||
[Theory, AutoData]
|
||||
public async Task SendsController_WhenSendHidesEmail_CreatorIdentifierShouldBeNull(
|
||||
Guid id, Send send, User user)
|
||||
{
|
||||
var accessId = CoreHelpers.Base64UrlEncode(id.ToByteArray());
|
||||
_sut = new SendsController(
|
||||
_sendRepository,
|
||||
_userService,
|
||||
_sendService,
|
||||
_sendFileStorageService,
|
||||
_logger,
|
||||
_globalSettings,
|
||||
_currentContext
|
||||
);
|
||||
}
|
||||
|
||||
send.Id = default;
|
||||
send.Type = SendType.Text;
|
||||
send.Data = JsonSerializer.Serialize(new Dictionary<string, string>());
|
||||
send.HideEmail = true;
|
||||
public void Dispose()
|
||||
{
|
||||
_sut?.Dispose();
|
||||
}
|
||||
|
||||
_sendService.AccessAsync(id, null).Returns((send, false, false));
|
||||
_userService.GetUserByIdAsync(Arg.Any<Guid>()).Returns(user);
|
||||
[Theory, AutoData]
|
||||
public async Task SendsController_WhenSendHidesEmail_CreatorIdentifierShouldBeNull(
|
||||
Guid id, Send send, User user)
|
||||
{
|
||||
var accessId = CoreHelpers.Base64UrlEncode(id.ToByteArray());
|
||||
|
||||
var request = new SendAccessRequestModel();
|
||||
var actionResult = await _sut.Access(accessId, request);
|
||||
var response = (actionResult as ObjectResult)?.Value as SendAccessResponseModel;
|
||||
send.Id = default;
|
||||
send.Type = SendType.Text;
|
||||
send.Data = JsonSerializer.Serialize(new Dictionary<string, string>());
|
||||
send.HideEmail = true;
|
||||
|
||||
Assert.NotNull(response);
|
||||
Assert.Null(response.CreatorIdentifier);
|
||||
_sendService.AccessAsync(id, null).Returns((send, false, false));
|
||||
_userService.GetUserByIdAsync(Arg.Any<Guid>()).Returns(user);
|
||||
|
||||
var request = new SendAccessRequestModel();
|
||||
var actionResult = await _sut.Access(accessId, request);
|
||||
var response = (actionResult as ObjectResult)?.Value as SendAccessResponseModel;
|
||||
|
||||
Assert.NotNull(response);
|
||||
Assert.Null(response.CreatorIdentifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user