1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 15:42:48 -05:00

[PM-14377] Add PATCH complete endpoint (#5100)

* Added CQRS pattern

* Added the GetManyByUserIdAsync signature to the repositiory

* Added sql sproc

Created user defined type to hold status

Created migration file

* Added ef core query

* Added absract and concrete implementation for GetManyByUserIdStatusAsync

* Added integration tests

* Updated params to status

* Implemented new query to utilize repository method

* Added controller for the security task endpoint

* Fixed lint issues

* Added documentation

* simplified to require single status

modified script to check for users with edit rights

* Updated ef core query

* Added new assertions

* simplified to require single status

* fixed formatting

* Fixed sql script

* Removed default null

* Added OperationAuthorizationRequirement for secruity task

* Added and registered MarkTaskAsCompletedCommand

* Added unit tests for the command

* Added complete endpoint

* removed false value
This commit is contained in:
SmithThe4th
2024-12-13 14:50:20 -05:00
committed by GitHub
parent c0a9c55891
commit 141a046a28
7 changed files with 207 additions and 2 deletions

View File

@ -3,6 +3,7 @@ using Bit.Api.Vault.Models.Response;
using Bit.Core;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Bit.Core.Vault.Commands.Interfaces;
using Bit.Core.Vault.Enums;
using Bit.Core.Vault.Queries;
using Microsoft.AspNetCore.Authorization;
@ -17,11 +18,16 @@ public class SecurityTaskController : Controller
{
private readonly IUserService _userService;
private readonly IGetTaskDetailsForUserQuery _getTaskDetailsForUserQuery;
private readonly IMarkTaskAsCompleteCommand _markTaskAsCompleteCommand;
public SecurityTaskController(IUserService userService, IGetTaskDetailsForUserQuery getTaskDetailsForUserQuery)
public SecurityTaskController(
IUserService userService,
IGetTaskDetailsForUserQuery getTaskDetailsForUserQuery,
IMarkTaskAsCompleteCommand markTaskAsCompleteCommand)
{
_userService = userService;
_getTaskDetailsForUserQuery = getTaskDetailsForUserQuery;
_markTaskAsCompleteCommand = markTaskAsCompleteCommand;
}
/// <summary>
@ -37,4 +43,15 @@ public class SecurityTaskController : Controller
var response = securityTasks.Select(x => new SecurityTasksResponseModel(x)).ToList();
return new ListResponseModel<SecurityTasksResponseModel>(response);
}
/// <summary>
/// Marks a task as complete. The user must have edit permission on the cipher associated with the task.
/// </summary>
/// <param name="taskId">The unique identifier of the task to complete</param>
[HttpPatch("{taskId:guid}/complete")]
public async Task<IActionResult> Complete(Guid taskId)
{
await _markTaskAsCompleteCommand.CompleteAsync(taskId);
return NoContent();
}
}

View File

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Authorization.Infrastructure;
namespace Bit.Core.Vault.Authorization;
public class SecurityTaskOperationRequirement : OperationAuthorizationRequirement
{
public SecurityTaskOperationRequirement(string name)
{
Name = name;
}
}
public static class SecurityTaskOperations
{
public static readonly SecurityTaskOperationRequirement Update = new(nameof(Update));
}

View File

@ -0,0 +1,11 @@
namespace Bit.Core.Vault.Commands.Interfaces;
public interface IMarkTaskAsCompleteCommand
{
/// <summary>
/// Marks a task as complete.
/// </summary>
/// <param name="taskId">The unique identifier of the task to complete</param>
/// <returns>A task representing the async operation</returns>
Task CompleteAsync(Guid taskId);
}

View File

@ -0,0 +1,50 @@
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Utilities;
using Bit.Core.Vault.Authorization;
using Bit.Core.Vault.Commands.Interfaces;
using Bit.Core.Vault.Enums;
using Bit.Core.Vault.Repositories;
using Microsoft.AspNetCore.Authorization;
namespace Bit.Core.Vault.Commands;
public class MarkTaskAsCompletedCommand : IMarkTaskAsCompleteCommand
{
private readonly ISecurityTaskRepository _securityTaskRepository;
private readonly IAuthorizationService _authorizationService;
private readonly ICurrentContext _currentContext;
public MarkTaskAsCompletedCommand(
ISecurityTaskRepository securityTaskRepository,
IAuthorizationService authorizationService,
ICurrentContext currentContext)
{
_securityTaskRepository = securityTaskRepository;
_authorizationService = authorizationService;
_currentContext = currentContext;
}
/// <inheritdoc />
public async Task CompleteAsync(Guid taskId)
{
if (!_currentContext.UserId.HasValue)
{
throw new NotFoundException();
}
var task = await _securityTaskRepository.GetByIdAsync(taskId);
if (task is null)
{
throw new NotFoundException();
}
await _authorizationService.AuthorizeOrThrowAsync(_currentContext.HttpContext.User, task,
SecurityTaskOperations.Update);
task.Status = SecurityTaskStatus.Completed;
task.RevisionDate = DateTime.UtcNow;
await _securityTaskRepository.ReplaceAsync(task);
}
}

View File

@ -1,4 +1,6 @@
using Bit.Core.Vault.Queries;
using Bit.Core.Vault.Commands;
using Bit.Core.Vault.Commands.Interfaces;
using Bit.Core.Vault.Queries;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.Core.Vault;
@ -16,5 +18,6 @@ public static class VaultServiceCollectionExtensions
{
services.AddScoped<IOrganizationCiphersQuery, OrganizationCiphersQuery>();
services.AddScoped<IGetTaskDetailsForUserQuery, GetTaskDetailsForUserQuery>();
services.AddScoped<IMarkTaskAsCompleteCommand, MarkTaskAsCompletedCommand>();
}
}