Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 21 additions & 23 deletions src/Api/Tools/Controllers/SendsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
using Bit.Core;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Data;
using Bit.Core.Tools.Repositories;
using Bit.Core.Tools.SendFeatures;
using Bit.Core.Tools.SendFeatures.Commands.Interfaces;
using Bit.Core.Tools.SendFeatures.Queries.Interfaces;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
Expand All @@ -30,27 +30,29 @@ public class SendsController : Controller
private readonly ISendFileStorageService _sendFileStorageService;
private readonly IAnonymousSendCommand _anonymousSendCommand;
private readonly INonAnonymousSendCommand _nonAnonymousSendCommand;

private readonly ISendOwnerQuery _sendOwnerQuery;

private readonly ILogger<SendsController> _logger;
private readonly GlobalSettings _globalSettings;

public SendsController(
ISendRepository sendRepository,
IUserService userService,
ISendAuthorizationService sendAuthorizationService,
IAnonymousSendCommand anonymousSendCommand,
INonAnonymousSendCommand nonAnonymousSendCommand,
ISendOwnerQuery sendOwnerQuery,
ISendFileStorageService sendFileStorageService,
ILogger<SendsController> logger,
GlobalSettings globalSettings)
ILogger<SendsController> logger)
{
_sendRepository = sendRepository;
_userService = userService;
_sendAuthorizationService = sendAuthorizationService;
_anonymousSendCommand = anonymousSendCommand;
_nonAnonymousSendCommand = nonAnonymousSendCommand;
_sendOwnerQuery = sendOwnerQuery;
_sendFileStorageService = sendFileStorageService;
_logger = logger;
_globalSettings = globalSettings;
}

#region Anonymous endpoints
Expand Down Expand Up @@ -83,7 +85,7 @@ public async Task<IActionResult> Access(string id, [FromBody] SendAccessRequestM
throw new NotFoundException();
}

var sendResponse = new SendAccessResponseModel(send, _globalSettings);
var sendResponse = new SendAccessResponseModel(send);
if (send.UserId.HasValue && !send.HideEmail.GetValueOrDefault())
{
var creator = await _userService.GetUserByIdAsync(send.UserId.Value);
Expand Down Expand Up @@ -178,23 +180,19 @@ public async Task<ObjectResult> AzureValidateFile()
[HttpGet("{id}")]
public async Task<SendResponseModel> Get(string id)
{
var userId = _userService.GetProperUserId(User).Value;
var send = await _sendRepository.GetByIdAsync(new Guid(id));
if (send == null || send.UserId != userId)
{
throw new NotFoundException();
}

return new SendResponseModel(send, _globalSettings);
var sendId = new Guid(id);
var send = await _sendOwnerQuery.Get(sendId);
return new SendResponseModel(send);
}

[HttpGet("")]
public async Task<ListResponseModel<SendResponseModel>> Get()
{
var userId = _userService.GetProperUserId(User).Value;
var sends = await _sendRepository.GetManyByUserIdAsync(userId);
var responses = sends.Select(s => new SendResponseModel(s, _globalSettings));
return new ListResponseModel<SendResponseModel>(responses);
var sends = await _sendOwnerQuery.GetOwned();
var responses = sends.Select(s => new SendResponseModel(s));
var result = new ListResponseModel<SendResponseModel>(responses);

return result;
}

[HttpPost("")]
Expand All @@ -204,7 +202,7 @@ public async Task<SendResponseModel> Post([FromBody] SendRequestModel model)
var userId = _userService.GetProperUserId(User).Value;
var send = model.ToSend(userId, _sendAuthorizationService);
await _nonAnonymousSendCommand.SaveSendAsync(send);
return new SendResponseModel(send, _globalSettings);
return new SendResponseModel(send);
}

[HttpPost("file/v2")]
Expand Down Expand Up @@ -233,7 +231,7 @@ public async Task<SendFileUploadDataResponseModel> PostFile([FromBody] SendReque
{
Url = uploadUrl,
FileUploadType = _sendFileStorageService.FileUploadType,
SendResponse = new SendResponseModel(send, _globalSettings)
SendResponse = new SendResponseModel(send)
};
}

Expand All @@ -257,7 +255,7 @@ public async Task<SendFileUploadDataResponseModel> RenewFileUpload(string id, st
{
Url = await _sendFileStorageService.GetSendFileUploadUrlAsync(send, fileId),
FileUploadType = _sendFileStorageService.FileUploadType,
SendResponse = new SendResponseModel(send, _globalSettings),
SendResponse = new SendResponseModel(send),
};
}

Expand Down Expand Up @@ -291,7 +289,7 @@ public async Task<SendResponseModel> Put(string id, [FromBody] SendRequestModel
}

await _nonAnonymousSendCommand.SaveSendAsync(model.ToSend(send, _sendAuthorizationService));
return new SendResponseModel(send, _globalSettings);
return new SendResponseModel(send);
}

[HttpPut("{id}/remove-password")]
Expand All @@ -306,7 +304,7 @@ public async Task<SendResponseModel> PutRemovePassword(string id)

send.Password = null;
await _nonAnonymousSendCommand.SaveSendAsync(send);
return new SendResponseModel(send, _globalSettings);
return new SendResponseModel(send);
}

[HttpDelete("{id}")]
Expand Down
3 changes: 1 addition & 2 deletions src/Api/Tools/Models/Response/SendAccessResponseModel.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
๏ปฟusing System.Text.Json;
using Bit.Core.Models.Api;
using Bit.Core.Settings;
using Bit.Core.Tools.Entities;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Data;
Expand All @@ -10,7 +9,7 @@ namespace Bit.Api.Tools.Models.Response;

public class SendAccessResponseModel : ResponseModel
{
public SendAccessResponseModel(Send send, GlobalSettings globalSettings)
public SendAccessResponseModel(Send send)
: base("send-access")
{
if (send == null)
Expand Down
3 changes: 1 addition & 2 deletions src/Api/Tools/Models/Response/SendResponseModel.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
๏ปฟusing System.Text.Json;
using Bit.Core.Models.Api;
using Bit.Core.Settings;
using Bit.Core.Tools.Entities;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Data;
Expand All @@ -10,7 +9,7 @@ namespace Bit.Api.Tools.Models.Response;

public class SendResponseModel : ResponseModel
{
public SendResponseModel(Send send, GlobalSettings globalSettings)
public SendResponseModel(Send send)
: base("send")
{
if (send == null)
Expand Down
2 changes: 1 addition & 1 deletion src/Api/Vault/Models/Response/SyncResponseModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public SyncResponseModel(
c => new CollectionDetailsResponseModel(c)) ?? new List<CollectionDetailsResponseModel>();
Domains = excludeDomains ? null : new DomainsResponseModel(user, false);
Policies = policies?.Select(p => new PolicyResponseModel(p)) ?? new List<PolicyResponseModel>();
Sends = sends.Select(s => new SendResponseModel(s, globalSettings));
Sends = sends.Select(s => new SendResponseModel(s));
}

public ProfileResponseModel Profile { get; set; }
Expand Down
14 changes: 14 additions & 0 deletions src/Core/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,22 @@ public static class FeatureFlagKeys
public const string IpcChannelFramework = "ipc-channel-framework";

/* Tools Team */
/// <summary>
/// Enable this flag to share the send view used by the web and browser clients
/// on the desktop client.
/// </summary>
public const string DesktopSendUIRefresh = "desktop-send-ui-refresh";

/// <summary>
/// Enable this flag to output email/OTP authenticated sends from the `GET sends` endpoint. When
/// this flag is disabled, the `GET sends` endpoint omits email/OTP authenticated sends.
/// </summary>
/// <remarks>
/// This flag is server-side only, and only inhibits the endpoint returning all sends.
/// Email/OTP sends can still be created and downloaded through other endpoints.
/// </remarks>
public const string PM19051_ListEmailOtpSends = "tools-send-email-otp-listing";

/* Vault Team */
public const string PM8851_BrowserOnboardingNudge = "pm-8851-browser-onboarding-nudge";
public const string PM9111ExtensionPersistAddEditForm = "pm-9111-extension-persist-add-edit-form";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
๏ปฟusing Bit.Core.Exceptions;
using Bit.Core.Tools.Entities;

#nullable enable

namespace Bit.Core.Tools.SendFeatures.Queries.Interfaces;

/// <summary>
/// Queries sends owned by the current user.
/// </summary>
public interface ISendOwnerQuery
{
/// <summary>
/// Gets a send.
/// </summary>
/// <param name="id">Identifies the send</param>
/// <returns>The send</returns>
/// <exception cref="NotFoundException">
/// Thrown when <paramref name="id"/> fails to identify a send
/// owned by the user.
/// </exception>
/// <exception cref="BadRequestException">
/// Thrown when the query cannot identify the current user.
/// </exception>
Task<Send> Get(Guid id);

/// <summary>
/// Gets all sends owned by the current user.
/// </summary>
/// <returns>
/// A sequence of all owned sends.
/// </returns>
/// <exception cref="BadRequestException">
/// Thrown when the query cannot identify the current user.
/// </exception>
Task<ICollection<Send>> GetOwned();
}
69 changes: 69 additions & 0 deletions src/Core/Tools/SendFeatures/Queries/SendOwnerQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
๏ปฟ
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.Tools.Entities;
using Bit.Core.Tools.Repositories;
using Bit.Core.Tools.SendFeatures.Queries.Interfaces;

#nullable enable

namespace Bit.Core.Tools.SendFeatures.Queries;

/// <inheritdoc cref="ISendOwnerQuery"/>
public class SendOwnerQuery : ISendOwnerQuery
{
private readonly ISendRepository _repository;
private readonly IFeatureService _features;
private readonly ICurrentContext _context;
private Guid CurrentUserId
{
get => _context.UserId ?? throw new BadRequestException("Invalid user.");
}

/// <summary>
/// Instantiates the command
/// </summary>
/// <param name="sendRepository">
/// Retrieves send records
/// </param>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="sendRepository"/> is <see langword="null"/>.
/// </exception>
public SendOwnerQuery(ISendRepository sendRepository, IFeatureService features, ICurrentContext context)
{
_repository = sendRepository;
_features = features ?? throw new ArgumentNullException(nameof(features));
_context = context ?? throw new ArgumentNullException(nameof(context));
}

/// <inheritdoc cref="ISendOwnerQuery.Get"/>
public async Task<Send> Get(Guid id)
{
var send = await _repository.GetByIdAsync(id);
if (send == null || send.UserId != CurrentUserId)
{
throw new NotFoundException();
}

return send;
}

/// <inheritdoc cref="ISendOwnerQuery.GetOwned"/>
public async Task<ICollection<Send>> GetOwned()
{
var sends = await _repository.GetManyByUserIdAsync(CurrentUserId);

var removeEmailOtp = !_features.IsEnabled(FeatureFlagKeys.PM19051_ListEmailOtpSends);
if (removeEmailOtp)
{
// reify list to avoid invalidating the enumerator
foreach (var s in sends.Where(s => s.Emails != null).ToList())
{
sends.Remove(s);
}
}

return sends;
}
}
Loading
Loading