1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-22 12:15:36 +01:00
bitwarden-server/test/Core.Test/AdminConsole/OrganizationAuth/UpdateOrganizationAuthRequestCommandTests.cs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

247 lines
11 KiB
C#
Raw Normal View History

using Bit.Core.AdminConsole.OrganizationAuth;
Allow for bulk processing new login device requests (#4064) * Define a model for updating many auth requests In order to facilitate a command method that can update many auth requests at one time a new model must be defined that accepts valid input for the command's needs. To achieve this a new file has been created at `Core/AdminConsole/OrganizationAuth/Models/OrganizationAuthRequestUpdateCommandModel.cs` that contains a class of the same name. It's properties match those that need to come from any calling API request models to fulfill the request. * Declare a new command interface method Calling API functions of the `UpdateOrganizationAuthRequestCommand` need a function that can accept many auth request response objects and process them as approved or denied. To achieve this a new function has been added to `IUpdateOrganizationAuthRequestCommand` called `UpdateManyAsync()` that accepts an `IEnumberable<OrganizationAuthRequest>` and returns a `Task`. Implementations of this interface method will be used to bulk process auth requests as approved or denied. * Stub out method implementation for unit testing To facilitate a bulk device login request approval workflow in the admin console `UpdateOrganizationAuthRequestCommand` needs to be updated to include an `UpdateMany()` method. It should accept a list of `OrganizationAuthRequestUpdateCommandModel` objects, perform some simple data validation checks, and then pass those along to `AuthRequestRepository` for updating in the database. This commit stubs out this method for the purpose of writing unit tests. At this stage the method throws a `NotImplementedException()`. It will be expand after writing assertions. * Inject `IAuthRequestRepository` into `UpdateOrganizationAuthCommand` The updates to `UpdateOrganizationAuthRequestCommand` require a new direct dependency on `IAuthRequestRepository`. This commit simply registers this dependency in the `UpdateOrganizationAuthRequest` constructor for use in unit tests and the `UpdateManyAsync()` implementation. * Write tests * Rename `UpdateManyAsync()` to `UpdateAsync` * Drop the `CommandModel` suffix * Invert business logic update filters * Rework everything to be more model-centric * Bulk send push notifications * Write tests that validate the command as a whole * Fix a test that I broke by mistake * Swap to using await instead of chained methods for processing * Seperate a function arguement into a variable declaration * Ungeneric-ify the processor * Adjust ternary formatting * Adjust naming of methods regarding logging organization events * Throw an exception if Process is called with no auth request loaded * Rename `_updates` -> `_update` * Rename email methods * Stop returning `this` * Allow callbacks to be null * Make some assertions about the state of a processed auth request * Be more terse about arguements in happy path test * Remove unneeded null check * Expose an endpoint for bulk processing of organization auth requests (#4077) --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
2024-05-27 03:56:52 +02:00
using Bit.Core.AdminConsole.OrganizationAuth.Models;
using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Models.Api.Request.AuthRequest;
Allow for bulk processing new login device requests (#4064) * Define a model for updating many auth requests In order to facilitate a command method that can update many auth requests at one time a new model must be defined that accepts valid input for the command's needs. To achieve this a new file has been created at `Core/AdminConsole/OrganizationAuth/Models/OrganizationAuthRequestUpdateCommandModel.cs` that contains a class of the same name. It's properties match those that need to come from any calling API request models to fulfill the request. * Declare a new command interface method Calling API functions of the `UpdateOrganizationAuthRequestCommand` need a function that can accept many auth request response objects and process them as approved or denied. To achieve this a new function has been added to `IUpdateOrganizationAuthRequestCommand` called `UpdateManyAsync()` that accepts an `IEnumberable<OrganizationAuthRequest>` and returns a `Task`. Implementations of this interface method will be used to bulk process auth requests as approved or denied. * Stub out method implementation for unit testing To facilitate a bulk device login request approval workflow in the admin console `UpdateOrganizationAuthRequestCommand` needs to be updated to include an `UpdateMany()` method. It should accept a list of `OrganizationAuthRequestUpdateCommandModel` objects, perform some simple data validation checks, and then pass those along to `AuthRequestRepository` for updating in the database. This commit stubs out this method for the purpose of writing unit tests. At this stage the method throws a `NotImplementedException()`. It will be expand after writing assertions. * Inject `IAuthRequestRepository` into `UpdateOrganizationAuthCommand` The updates to `UpdateOrganizationAuthRequestCommand` require a new direct dependency on `IAuthRequestRepository`. This commit simply registers this dependency in the `UpdateOrganizationAuthRequest` constructor for use in unit tests and the `UpdateManyAsync()` implementation. * Write tests * Rename `UpdateManyAsync()` to `UpdateAsync` * Drop the `CommandModel` suffix * Invert business logic update filters * Rework everything to be more model-centric * Bulk send push notifications * Write tests that validate the command as a whole * Fix a test that I broke by mistake * Swap to using await instead of chained methods for processing * Seperate a function arguement into a variable declaration * Ungeneric-ify the processor * Adjust ternary formatting * Adjust naming of methods regarding logging organization events * Throw an exception if Process is called with no auth request loaded * Rename `_updates` -> `_update` * Rename email methods * Stop returning `this` * Allow callbacks to be null * Make some assertions about the state of a processed auth request * Be more terse about arguements in happy path test * Remove unneeded null check * Expose an endpoint for bulk processing of organization auth requests (#4077) --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
2024-05-27 03:56:52 +02:00
using Bit.Core.Auth.Models.Data;
using Bit.Core.Auth.Services;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Repositories;
using Bit.Core.Services;
Allow for bulk processing new login device requests (#4064) * Define a model for updating many auth requests In order to facilitate a command method that can update many auth requests at one time a new model must be defined that accepts valid input for the command's needs. To achieve this a new file has been created at `Core/AdminConsole/OrganizationAuth/Models/OrganizationAuthRequestUpdateCommandModel.cs` that contains a class of the same name. It's properties match those that need to come from any calling API request models to fulfill the request. * Declare a new command interface method Calling API functions of the `UpdateOrganizationAuthRequestCommand` need a function that can accept many auth request response objects and process them as approved or denied. To achieve this a new function has been added to `IUpdateOrganizationAuthRequestCommand` called `UpdateManyAsync()` that accepts an `IEnumberable<OrganizationAuthRequest>` and returns a `Task`. Implementations of this interface method will be used to bulk process auth requests as approved or denied. * Stub out method implementation for unit testing To facilitate a bulk device login request approval workflow in the admin console `UpdateOrganizationAuthRequestCommand` needs to be updated to include an `UpdateMany()` method. It should accept a list of `OrganizationAuthRequestUpdateCommandModel` objects, perform some simple data validation checks, and then pass those along to `AuthRequestRepository` for updating in the database. This commit stubs out this method for the purpose of writing unit tests. At this stage the method throws a `NotImplementedException()`. It will be expand after writing assertions. * Inject `IAuthRequestRepository` into `UpdateOrganizationAuthCommand` The updates to `UpdateOrganizationAuthRequestCommand` require a new direct dependency on `IAuthRequestRepository`. This commit simply registers this dependency in the `UpdateOrganizationAuthRequest` constructor for use in unit tests and the `UpdateManyAsync()` implementation. * Write tests * Rename `UpdateManyAsync()` to `UpdateAsync` * Drop the `CommandModel` suffix * Invert business logic update filters * Rework everything to be more model-centric * Bulk send push notifications * Write tests that validate the command as a whole * Fix a test that I broke by mistake * Swap to using await instead of chained methods for processing * Seperate a function arguement into a variable declaration * Ungeneric-ify the processor * Adjust ternary formatting * Adjust naming of methods regarding logging organization events * Throw an exception if Process is called with no auth request loaded * Rename `_updates` -> `_update` * Rename email methods * Stop returning `this` * Allow callbacks to be null * Make some assertions about the state of a processed auth request * Be more terse about arguements in happy path test * Remove unneeded null check * Expose an endpoint for bulk processing of organization auth requests (#4077) --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
2024-05-27 03:56:52 +02:00
using Bit.Core.Settings;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.AdminConsole.OrganizationAuth;
[SutProviderCustomize]
public class UpdateOrganizationAuthRequestCommandTests
{
[Theory]
[BitAutoData]
public async Task UpdateOrgAuthRequest_Approved_SendEmail_Success(
DateTime responseDate, string email, DeviceType deviceType, string deviceIdentifier,
string requestIpAddress, Guid requestId, Guid userId, bool requestApproved,
string encryptedUserKey, SutProvider<UpdateOrganizationAuthRequestCommand> sutProvider)
{
var expectedDeviceTypeAndIdentifier = $"{deviceType} - {deviceIdentifier}";
sutProvider.GetDependency<IAuthRequestService>()
.UpdateAuthRequestAsync(requestId, userId,
Arg.Is<AuthRequestUpdateRequestModel>(x =>
x.RequestApproved == requestApproved && x.Key == encryptedUserKey))
.Returns(new AuthRequest()
{
UserId = userId,
Approved = true,
ResponseDate = responseDate,
RequestDeviceType = deviceType,
RequestDeviceIdentifier = deviceIdentifier,
RequestIpAddress = requestIpAddress,
});
sutProvider.GetDependency<IUserRepository>()
.GetByIdAsync(userId)
.Returns(new User()
{
Email = email
});
await sutProvider.Sut.UpdateAsync(requestId, userId, requestApproved, encryptedUserKey);
await sutProvider.GetDependency<IUserRepository>().Received(1).GetByIdAsync(userId);
await sutProvider.GetDependency<IMailService>().Received(1)
.SendTrustedDeviceAdminApprovalEmailAsync(email, responseDate, requestIpAddress, expectedDeviceTypeAndIdentifier);
}
[Theory]
[BitAutoData]
public async Task UpdateOrgAuthRequest_Denied_NonExecutes(
SutProvider<UpdateOrganizationAuthRequestCommand> sutProvider, Guid requestId, Guid userId,
bool requestApproved, string encryptedUserKey)
{
sutProvider.GetDependency<IAuthRequestService>()
.UpdateAuthRequestAsync(requestId, userId,
Arg.Is<AuthRequestUpdateRequestModel>(x =>
x.RequestApproved == requestApproved && x.Key == encryptedUserKey))
.Returns(new AuthRequest() { Approved = false });
await sutProvider.Sut.UpdateAsync(requestId, userId, requestApproved, encryptedUserKey);
await sutProvider.GetDependency<IUserRepository>().DidNotReceive().GetByIdAsync(userId);
await sutProvider.GetDependency<IMailService>().DidNotReceive()
.SendTrustedDeviceAdminApprovalEmailAsync(Arg.Any<string>(), Arg.Any<DateTime>(), Arg.Any<string>(),
Arg.Any<string>());
}
[Theory]
[BitAutoData]
Allow for bulk processing new login device requests (#4064) * Define a model for updating many auth requests In order to facilitate a command method that can update many auth requests at one time a new model must be defined that accepts valid input for the command's needs. To achieve this a new file has been created at `Core/AdminConsole/OrganizationAuth/Models/OrganizationAuthRequestUpdateCommandModel.cs` that contains a class of the same name. It's properties match those that need to come from any calling API request models to fulfill the request. * Declare a new command interface method Calling API functions of the `UpdateOrganizationAuthRequestCommand` need a function that can accept many auth request response objects and process them as approved or denied. To achieve this a new function has been added to `IUpdateOrganizationAuthRequestCommand` called `UpdateManyAsync()` that accepts an `IEnumberable<OrganizationAuthRequest>` and returns a `Task`. Implementations of this interface method will be used to bulk process auth requests as approved or denied. * Stub out method implementation for unit testing To facilitate a bulk device login request approval workflow in the admin console `UpdateOrganizationAuthRequestCommand` needs to be updated to include an `UpdateMany()` method. It should accept a list of `OrganizationAuthRequestUpdateCommandModel` objects, perform some simple data validation checks, and then pass those along to `AuthRequestRepository` for updating in the database. This commit stubs out this method for the purpose of writing unit tests. At this stage the method throws a `NotImplementedException()`. It will be expand after writing assertions. * Inject `IAuthRequestRepository` into `UpdateOrganizationAuthCommand` The updates to `UpdateOrganizationAuthRequestCommand` require a new direct dependency on `IAuthRequestRepository`. This commit simply registers this dependency in the `UpdateOrganizationAuthRequest` constructor for use in unit tests and the `UpdateManyAsync()` implementation. * Write tests * Rename `UpdateManyAsync()` to `UpdateAsync` * Drop the `CommandModel` suffix * Invert business logic update filters * Rework everything to be more model-centric * Bulk send push notifications * Write tests that validate the command as a whole * Fix a test that I broke by mistake * Swap to using await instead of chained methods for processing * Seperate a function arguement into a variable declaration * Ungeneric-ify the processor * Adjust ternary formatting * Adjust naming of methods regarding logging organization events * Throw an exception if Process is called with no auth request loaded * Rename `_updates` -> `_update` * Rename email methods * Stop returning `this` * Allow callbacks to be null * Make some assertions about the state of a processed auth request * Be more terse about arguements in happy path test * Remove unneeded null check * Expose an endpoint for bulk processing of organization auth requests (#4077) --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
2024-05-27 03:56:52 +02:00
public async Task UpdateAsync_BatchUpdate_AuthRequestForOrganizationNotFound_DoesNotExecute(
SutProvider<UpdateOrganizationAuthRequestCommand> sutProvider,
List<OrganizationAuthRequestUpdate> updates,
AuthRequestUpdateProcessorConfiguration configuration)
{
Allow for bulk processing new login device requests (#4064) * Define a model for updating many auth requests In order to facilitate a command method that can update many auth requests at one time a new model must be defined that accepts valid input for the command's needs. To achieve this a new file has been created at `Core/AdminConsole/OrganizationAuth/Models/OrganizationAuthRequestUpdateCommandModel.cs` that contains a class of the same name. It's properties match those that need to come from any calling API request models to fulfill the request. * Declare a new command interface method Calling API functions of the `UpdateOrganizationAuthRequestCommand` need a function that can accept many auth request response objects and process them as approved or denied. To achieve this a new function has been added to `IUpdateOrganizationAuthRequestCommand` called `UpdateManyAsync()` that accepts an `IEnumberable<OrganizationAuthRequest>` and returns a `Task`. Implementations of this interface method will be used to bulk process auth requests as approved or denied. * Stub out method implementation for unit testing To facilitate a bulk device login request approval workflow in the admin console `UpdateOrganizationAuthRequestCommand` needs to be updated to include an `UpdateMany()` method. It should accept a list of `OrganizationAuthRequestUpdateCommandModel` objects, perform some simple data validation checks, and then pass those along to `AuthRequestRepository` for updating in the database. This commit stubs out this method for the purpose of writing unit tests. At this stage the method throws a `NotImplementedException()`. It will be expand after writing assertions. * Inject `IAuthRequestRepository` into `UpdateOrganizationAuthCommand` The updates to `UpdateOrganizationAuthRequestCommand` require a new direct dependency on `IAuthRequestRepository`. This commit simply registers this dependency in the `UpdateOrganizationAuthRequest` constructor for use in unit tests and the `UpdateManyAsync()` implementation. * Write tests * Rename `UpdateManyAsync()` to `UpdateAsync` * Drop the `CommandModel` suffix * Invert business logic update filters * Rework everything to be more model-centric * Bulk send push notifications * Write tests that validate the command as a whole * Fix a test that I broke by mistake * Swap to using await instead of chained methods for processing * Seperate a function arguement into a variable declaration * Ungeneric-ify the processor * Adjust ternary formatting * Adjust naming of methods regarding logging organization events * Throw an exception if Process is called with no auth request loaded * Rename `_updates` -> `_update` * Rename email methods * Stop returning `this` * Allow callbacks to be null * Make some assertions about the state of a processed auth request * Be more terse about arguements in happy path test * Remove unneeded null check * Expose an endpoint for bulk processing of organization auth requests (#4077) --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
2024-05-27 03:56:52 +02:00
sutProvider.GetDependency<IAuthRequestRepository>().GetManyAdminApprovalRequestsByManyIdsAsync(
configuration.OrganizationId,
updates.Select(ar => ar.Id)
).ReturnsForAnyArgs((ICollection<OrganizationAdminAuthRequest>)null);
Allow for bulk processing new login device requests (#4064) * Define a model for updating many auth requests In order to facilitate a command method that can update many auth requests at one time a new model must be defined that accepts valid input for the command's needs. To achieve this a new file has been created at `Core/AdminConsole/OrganizationAuth/Models/OrganizationAuthRequestUpdateCommandModel.cs` that contains a class of the same name. It's properties match those that need to come from any calling API request models to fulfill the request. * Declare a new command interface method Calling API functions of the `UpdateOrganizationAuthRequestCommand` need a function that can accept many auth request response objects and process them as approved or denied. To achieve this a new function has been added to `IUpdateOrganizationAuthRequestCommand` called `UpdateManyAsync()` that accepts an `IEnumberable<OrganizationAuthRequest>` and returns a `Task`. Implementations of this interface method will be used to bulk process auth requests as approved or denied. * Stub out method implementation for unit testing To facilitate a bulk device login request approval workflow in the admin console `UpdateOrganizationAuthRequestCommand` needs to be updated to include an `UpdateMany()` method. It should accept a list of `OrganizationAuthRequestUpdateCommandModel` objects, perform some simple data validation checks, and then pass those along to `AuthRequestRepository` for updating in the database. This commit stubs out this method for the purpose of writing unit tests. At this stage the method throws a `NotImplementedException()`. It will be expand after writing assertions. * Inject `IAuthRequestRepository` into `UpdateOrganizationAuthCommand` The updates to `UpdateOrganizationAuthRequestCommand` require a new direct dependency on `IAuthRequestRepository`. This commit simply registers this dependency in the `UpdateOrganizationAuthRequest` constructor for use in unit tests and the `UpdateManyAsync()` implementation. * Write tests * Rename `UpdateManyAsync()` to `UpdateAsync` * Drop the `CommandModel` suffix * Invert business logic update filters * Rework everything to be more model-centric * Bulk send push notifications * Write tests that validate the command as a whole * Fix a test that I broke by mistake * Swap to using await instead of chained methods for processing * Seperate a function arguement into a variable declaration * Ungeneric-ify the processor * Adjust ternary formatting * Adjust naming of methods regarding logging organization events * Throw an exception if Process is called with no auth request loaded * Rename `_updates` -> `_update` * Rename email methods * Stop returning `this` * Allow callbacks to be null * Make some assertions about the state of a processed auth request * Be more terse about arguements in happy path test * Remove unneeded null check * Expose an endpoint for bulk processing of organization auth requests (#4077) --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
2024-05-27 03:56:52 +02:00
await sutProvider.Sut.UpdateAsync(configuration.OrganizationId, updates);
await sutProvider.GetDependency<IAuthRequestRepository>().DidNotReceiveWithAnyArgs().UpdateManyAsync(Arg.Any<IEnumerable<OrganizationAdminAuthRequest>>());
await sutProvider.GetDependency<IPushNotificationService>().DidNotReceiveWithAnyArgs().PushAuthRequestResponseAsync(Arg.Any<AuthRequest>());
await sutProvider.GetDependency<IMailService>().DidNotReceiveWithAnyArgs().SendTrustedDeviceAdminApprovalEmailAsync(
Arg.Any<string>(),
Arg.Any<DateTime>(),
Arg.Any<string>(),
Arg.Any<string>()
);
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogOrganizationUserEventsAsync(
Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>()
);
}
Allow for bulk processing new login device requests (#4064) * Define a model for updating many auth requests In order to facilitate a command method that can update many auth requests at one time a new model must be defined that accepts valid input for the command's needs. To achieve this a new file has been created at `Core/AdminConsole/OrganizationAuth/Models/OrganizationAuthRequestUpdateCommandModel.cs` that contains a class of the same name. It's properties match those that need to come from any calling API request models to fulfill the request. * Declare a new command interface method Calling API functions of the `UpdateOrganizationAuthRequestCommand` need a function that can accept many auth request response objects and process them as approved or denied. To achieve this a new function has been added to `IUpdateOrganizationAuthRequestCommand` called `UpdateManyAsync()` that accepts an `IEnumberable<OrganizationAuthRequest>` and returns a `Task`. Implementations of this interface method will be used to bulk process auth requests as approved or denied. * Stub out method implementation for unit testing To facilitate a bulk device login request approval workflow in the admin console `UpdateOrganizationAuthRequestCommand` needs to be updated to include an `UpdateMany()` method. It should accept a list of `OrganizationAuthRequestUpdateCommandModel` objects, perform some simple data validation checks, and then pass those along to `AuthRequestRepository` for updating in the database. This commit stubs out this method for the purpose of writing unit tests. At this stage the method throws a `NotImplementedException()`. It will be expand after writing assertions. * Inject `IAuthRequestRepository` into `UpdateOrganizationAuthCommand` The updates to `UpdateOrganizationAuthRequestCommand` require a new direct dependency on `IAuthRequestRepository`. This commit simply registers this dependency in the `UpdateOrganizationAuthRequest` constructor for use in unit tests and the `UpdateManyAsync()` implementation. * Write tests * Rename `UpdateManyAsync()` to `UpdateAsync` * Drop the `CommandModel` suffix * Invert business logic update filters * Rework everything to be more model-centric * Bulk send push notifications * Write tests that validate the command as a whole * Fix a test that I broke by mistake * Swap to using await instead of chained methods for processing * Seperate a function arguement into a variable declaration * Ungeneric-ify the processor * Adjust ternary formatting * Adjust naming of methods regarding logging organization events * Throw an exception if Process is called with no auth request loaded * Rename `_updates` -> `_update` * Rename email methods * Stop returning `this` * Allow callbacks to be null * Make some assertions about the state of a processed auth request * Be more terse about arguements in happy path test * Remove unneeded null check * Expose an endpoint for bulk processing of organization auth requests (#4077) --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
2024-05-27 03:56:52 +02:00
[Theory]
[BitAutoData]
public async Task UpdateAsync_BatchUpdate_ValidRequest_SavesAndFiresAllEvents(
SutProvider<UpdateOrganizationAuthRequestCommand> sutProvider,
List<OrganizationAuthRequestUpdate> updates,
List<OrganizationAdminAuthRequest> unprocessedAuthRequests,
AuthRequestUpdateProcessorConfiguration configuration,
List<OrganizationUser> organizationUsers,
List<User> users
)
{
// For this command to work we need the following from external
// classes:
// 1. A configured expiration timespan for organization auth requests
// 2. Some unresponded to auth requests that match the ids provided
// 3. A valid user to send emails to
// 4. A valid organization user to log events for
Allow for bulk processing new login device requests (#4064) * Define a model for updating many auth requests In order to facilitate a command method that can update many auth requests at one time a new model must be defined that accepts valid input for the command's needs. To achieve this a new file has been created at `Core/AdminConsole/OrganizationAuth/Models/OrganizationAuthRequestUpdateCommandModel.cs` that contains a class of the same name. It's properties match those that need to come from any calling API request models to fulfill the request. * Declare a new command interface method Calling API functions of the `UpdateOrganizationAuthRequestCommand` need a function that can accept many auth request response objects and process them as approved or denied. To achieve this a new function has been added to `IUpdateOrganizationAuthRequestCommand` called `UpdateManyAsync()` that accepts an `IEnumberable<OrganizationAuthRequest>` and returns a `Task`. Implementations of this interface method will be used to bulk process auth requests as approved or denied. * Stub out method implementation for unit testing To facilitate a bulk device login request approval workflow in the admin console `UpdateOrganizationAuthRequestCommand` needs to be updated to include an `UpdateMany()` method. It should accept a list of `OrganizationAuthRequestUpdateCommandModel` objects, perform some simple data validation checks, and then pass those along to `AuthRequestRepository` for updating in the database. This commit stubs out this method for the purpose of writing unit tests. At this stage the method throws a `NotImplementedException()`. It will be expand after writing assertions. * Inject `IAuthRequestRepository` into `UpdateOrganizationAuthCommand` The updates to `UpdateOrganizationAuthRequestCommand` require a new direct dependency on `IAuthRequestRepository`. This commit simply registers this dependency in the `UpdateOrganizationAuthRequest` constructor for use in unit tests and the `UpdateManyAsync()` implementation. * Write tests * Rename `UpdateManyAsync()` to `UpdateAsync` * Drop the `CommandModel` suffix * Invert business logic update filters * Rework everything to be more model-centric * Bulk send push notifications * Write tests that validate the command as a whole * Fix a test that I broke by mistake * Swap to using await instead of chained methods for processing * Seperate a function arguement into a variable declaration * Ungeneric-ify the processor * Adjust ternary formatting * Adjust naming of methods regarding logging organization events * Throw an exception if Process is called with no auth request loaded * Rename `_updates` -> `_update` * Rename email methods * Stop returning `this` * Allow callbacks to be null * Make some assertions about the state of a processed auth request * Be more terse about arguements in happy path test * Remove unneeded null check * Expose an endpoint for bulk processing of organization auth requests (#4077) --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
2024-05-27 03:56:52 +02:00
for (int i = 0; i < updates.Count(); i++)
{
unprocessedAuthRequests[i] = UnrespondAndEnsureValid(unprocessedAuthRequests[i], configuration.OrganizationId);
updates[i].Approved = true;
updates[i].Key = "key";
unprocessedAuthRequests[i].Id = updates[i].Id;
unprocessedAuthRequests[i].RequestDeviceType = DeviceType.iOS;
unprocessedAuthRequests[i].OrganizationUserId = organizationUsers[i].Id;
organizationUsers[i].OrganizationId = configuration.OrganizationId;
users[i].Id = unprocessedAuthRequests[i].UserId;
organizationUsers[i].UserId = unprocessedAuthRequests[i].UserId;
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(Arg.Is(users[i].Id)).Returns(users[i]);
};
sutProvider.GetDependency<IGlobalSettings>().PasswordlessAuth.AdminRequestExpiration.Returns(TimeSpan.FromDays(7));
sutProvider.GetDependency<IAuthRequestRepository>().GetManyAdminApprovalRequestsByManyIdsAsync(
configuration.OrganizationId,
updates.Select(ar => ar.Id)
).ReturnsForAnyArgs(unprocessedAuthRequests);
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(Arg.Is<IEnumerable<Guid>>(
list => list.All(x => organizationUsers.Select(y => y.Id).Contains(x)))).Returns(organizationUsers);
// Call the SUT
await sutProvider.Sut.UpdateAsync(configuration.OrganizationId, updates);
// Assert that because we passed in good data we call a save
// operation and raise all events
await sutProvider.GetDependency<IAuthRequestRepository>()
.Received()
.UpdateManyAsync(
Arg.Is<IEnumerable<OrganizationAdminAuthRequest>>(list =>
list.Any() &&
list.All(x =>
x.Approved.Value &&
x.Key == "key" &&
x.ResponseDate != null &&
unprocessedAuthRequests.Select(y => y.Id).Contains(x.Id))));
foreach (var authRequest in unprocessedAuthRequests)
{
await sutProvider.GetDependency<IPushNotificationService>().Received()
.PushAuthRequestResponseAsync(Arg.Is<OrganizationAdminAuthRequest>
(ar => ar.Id == authRequest.Id && ar.Approved == true && ar.Key == "key"));
await sutProvider.GetDependency<IMailService>().Received().SendTrustedDeviceAdminApprovalEmailAsync(
users.FirstOrDefault(x => x.Id == authRequest.UserId).Email,
Arg.Any<DateTime>(),
authRequest.RequestIpAddress,
$"iOS - {authRequest.RequestDeviceIdentifier}"
);
}
await sutProvider.GetDependency<IEventService>().Received().LogOrganizationUserEventsAsync(
Arg.Is<IEnumerable<(OrganizationUser o, EventType e, DateTime? d)>>(list =>
list.Any() && list.All(x => organizationUsers.Any(y => y.Id == x.o.Id) && x.e == EventType.OrganizationUser_ApprovedAuthRequest)
));
}
[Theory]
[BitAutoData]
public async Task UpdateAsync_BatchUpdate_AuthRequestIsDenied_DoesNotLeakRejection(
SutProvider<UpdateOrganizationAuthRequestCommand> sutProvider,
List<OrganizationAuthRequestUpdate> updates,
OrganizationAdminAuthRequest unprocessedAuthRequest,
AuthRequestUpdateProcessorConfiguration configuration,
User user
)
{
// For this command to work we need the following from external
// classes:
// 1. A configured expiration timespan for organization auth requests
// 2. Some unresponded to auth requests that match the ids provided
// 3. A valid user to send emails to
var unprocessedAuthRequests = new List<OrganizationAdminAuthRequest>();
unprocessedAuthRequest = UnrespondAndEnsureValid(unprocessedAuthRequest, configuration.OrganizationId);
foreach (var update in updates)
{
update.Approved = false;
unprocessedAuthRequest.Id = update.Id;
unprocessedAuthRequests.Add(unprocessedAuthRequest);
};
sutProvider.GetDependency<IGlobalSettings>().PasswordlessAuth.AdminRequestExpiration.Returns(TimeSpan.FromDays(7));
sutProvider.GetDependency<IAuthRequestRepository>().GetManyAdminApprovalRequestsByManyIdsAsync(
configuration.OrganizationId,
updates.Select(ar => ar.Id)
).ReturnsForAnyArgs(unprocessedAuthRequests);
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(Arg.Any<Guid>()).Returns(user);
// Call the SUT
await sutProvider.Sut.UpdateAsync(configuration.OrganizationId, updates);
// Assert that because we passed in good data we call a save
// operation and raise all events
await sutProvider.GetDependency<IAuthRequestRepository>().ReceivedWithAnyArgs().UpdateManyAsync(Arg.Any<IEnumerable<OrganizationAdminAuthRequest>>());
await sutProvider.GetDependency<IPushNotificationService>().DidNotReceiveWithAnyArgs().PushAuthRequestResponseAsync(Arg.Any<AuthRequest>());
await sutProvider.GetDependency<IMailService>().DidNotReceiveWithAnyArgs().SendTrustedDeviceAdminApprovalEmailAsync(
Arg.Any<string>(),
Arg.Any<DateTime>(),
Arg.Any<string>(),
Arg.Any<string>()
);
await sutProvider.GetDependency<IEventService>().ReceivedWithAnyArgs().LogOrganizationUserEventsAsync(
Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>()
);
}
private T UnrespondAndEnsureValid<T>(T authRequest, Guid organizationId) where T : AuthRequest
{
authRequest.OrganizationId = organizationId;
authRequest.Key = null;
authRequest.Approved = null;
authRequest.ResponseDate = null;
authRequest.AuthenticationDate = null;
authRequest.CreationDate = DateTime.UtcNow.AddMinutes(-10);
return authRequest;
}
}