Skip to content

Commit 64fa791

Browse files
Merge branch 'main' into billing/pm-29598/create-subscription-upgrade-endpoint
2 parents bca4274 + 484a8e4 commit 64fa791

35 files changed

+22505
-55
lines changed

src/Api/KeyManagement/Validators/SendRotationValidator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public async Task<IReadOnlyList<Send>> ValidateAsync(User user, IEnumerable<Send
4444
throw new BadRequestException("All existing sends must be included in the rotation.");
4545
}
4646

47-
result.Add(send.ToSend(existing, _sendAuthorizationService));
47+
result.Add(send.UpdateSend(existing, _sendAuthorizationService));
4848
}
4949

5050
return result;

src/Api/Tools/Controllers/SendsController.cs

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
// FIXME: Update this file to be null safe and then delete the line below
2-
#nullable disable
3-
4-
using System.Text.Json;
1+
using System.Text.Json;
52
using Azure.Messaging.EventGrid;
63
using Bit.Api.Models.Response;
74
using Bit.Api.Tools.Models.Request;
@@ -16,6 +13,7 @@
1613
using Bit.Core.Tools.Repositories;
1714
using Bit.Core.Tools.SendFeatures;
1815
using Bit.Core.Tools.SendFeatures.Commands.Interfaces;
16+
using Bit.Core.Tools.SendFeatures.Queries.Interfaces;
1917
using Bit.Core.Tools.Services;
2018
using Bit.Core.Utilities;
2119
using Microsoft.AspNetCore.Authorization;
@@ -33,6 +31,9 @@ public class SendsController : Controller
3331
private readonly ISendFileStorageService _sendFileStorageService;
3432
private readonly IAnonymousSendCommand _anonymousSendCommand;
3533
private readonly INonAnonymousSendCommand _nonAnonymousSendCommand;
34+
35+
private readonly ISendOwnerQuery _sendOwnerQuery;
36+
3637
private readonly ILogger<SendsController> _logger;
3738
private readonly GlobalSettings _globalSettings;
3839

@@ -42,6 +43,7 @@ public SendsController(
4243
ISendAuthorizationService sendAuthorizationService,
4344
IAnonymousSendCommand anonymousSendCommand,
4445
INonAnonymousSendCommand nonAnonymousSendCommand,
46+
ISendOwnerQuery sendOwnerQuery,
4547
ISendFileStorageService sendFileStorageService,
4648
ILogger<SendsController> logger,
4749
GlobalSettings globalSettings)
@@ -51,6 +53,7 @@ public SendsController(
5153
_sendAuthorizationService = sendAuthorizationService;
5254
_anonymousSendCommand = anonymousSendCommand;
5355
_nonAnonymousSendCommand = nonAnonymousSendCommand;
56+
_sendOwnerQuery = sendOwnerQuery;
5457
_sendFileStorageService = sendFileStorageService;
5558
_logger = logger;
5659
_globalSettings = globalSettings;
@@ -70,7 +73,11 @@ public async Task<IActionResult> Access(string id, [FromBody] SendAccessRequestM
7073

7174
var guid = new Guid(CoreHelpers.Base64UrlDecode(id));
7275
var send = await _sendRepository.GetByIdAsync(guid);
73-
SendAccessResult sendAuthResult =
76+
if (send == null)
77+
{
78+
throw new BadRequestException("Could not locate send");
79+
}
80+
var sendAuthResult =
7481
await _sendAuthorizationService.AccessAsync(send, model.Password);
7582
if (sendAuthResult.Equals(SendAccessResult.PasswordRequired))
7683
{
@@ -86,7 +93,7 @@ public async Task<IActionResult> Access(string id, [FromBody] SendAccessRequestM
8693
throw new NotFoundException();
8794
}
8895

89-
var sendResponse = new SendAccessResponseModel(send, _globalSettings);
96+
var sendResponse = new SendAccessResponseModel(send);
9097
if (send.UserId.HasValue && !send.HideEmail.GetValueOrDefault())
9198
{
9299
var creator = await _userService.GetUserByIdAsync(send.UserId.Value);
@@ -181,33 +188,29 @@ public async Task<ObjectResult> AzureValidateFile()
181188
[HttpGet("{id}")]
182189
public async Task<SendResponseModel> Get(string id)
183190
{
184-
var userId = _userService.GetProperUserId(User).Value;
185-
var send = await _sendRepository.GetByIdAsync(new Guid(id));
186-
if (send == null || send.UserId != userId)
187-
{
188-
throw new NotFoundException();
189-
}
190-
191-
return new SendResponseModel(send, _globalSettings);
191+
var sendId = new Guid(id);
192+
var send = await _sendOwnerQuery.Get(sendId, User);
193+
return new SendResponseModel(send);
192194
}
193195

194196
[HttpGet("")]
195197
public async Task<ListResponseModel<SendResponseModel>> GetAll()
196198
{
197-
var userId = _userService.GetProperUserId(User).Value;
198-
var sends = await _sendRepository.GetManyByUserIdAsync(userId);
199-
var responses = sends.Select(s => new SendResponseModel(s, _globalSettings));
200-
return new ListResponseModel<SendResponseModel>(responses);
199+
var sends = await _sendOwnerQuery.GetOwned(User);
200+
var responses = sends.Select(s => new SendResponseModel(s));
201+
var result = new ListResponseModel<SendResponseModel>(responses);
202+
203+
return result;
201204
}
202205

203206
[HttpPost("")]
204207
public async Task<SendResponseModel> Post([FromBody] SendRequestModel model)
205208
{
206209
model.ValidateCreation();
207-
var userId = _userService.GetProperUserId(User).Value;
210+
var userId = _userService.GetProperUserId(User) ?? throw new InvalidOperationException("User ID not found");
208211
var send = model.ToSend(userId, _sendAuthorizationService);
209212
await _nonAnonymousSendCommand.SaveSendAsync(send);
210-
return new SendResponseModel(send, _globalSettings);
213+
return new SendResponseModel(send);
211214
}
212215

213216
[HttpPost("file/v2")]
@@ -229,27 +232,27 @@ public async Task<SendFileUploadDataResponseModel> PostFile([FromBody] SendReque
229232
}
230233

231234
model.ValidateCreation();
232-
var userId = _userService.GetProperUserId(User).Value;
235+
var userId = _userService.GetProperUserId(User) ?? throw new InvalidOperationException("User ID not found");
233236
var (send, data) = model.ToSend(userId, model.File.FileName, _sendAuthorizationService);
234237
var uploadUrl = await _nonAnonymousSendCommand.SaveFileSendAsync(send, data, model.FileLength.Value);
235238
return new SendFileUploadDataResponseModel
236239
{
237240
Url = uploadUrl,
238241
FileUploadType = _sendFileStorageService.FileUploadType,
239-
SendResponse = new SendResponseModel(send, _globalSettings)
242+
SendResponse = new SendResponseModel(send)
240243
};
241244
}
242245

243246
[HttpGet("{id}/file/{fileId}")]
244247
public async Task<SendFileUploadDataResponseModel> RenewFileUpload(string id, string fileId)
245248
{
246-
var userId = _userService.GetProperUserId(User).Value;
249+
var userId = _userService.GetProperUserId(User) ?? throw new InvalidOperationException("User ID not found");
247250
var sendId = new Guid(id);
248251
var send = await _sendRepository.GetByIdAsync(sendId);
249-
var fileData = JsonSerializer.Deserialize<SendFileData>(send?.Data);
252+
var fileData = JsonSerializer.Deserialize<SendFileData>(send?.Data ?? string.Empty);
250253

251254
if (send == null || send.Type != SendType.File || (send.UserId.HasValue && send.UserId.Value != userId) ||
252-
!send.UserId.HasValue || fileData.Id != fileId || fileData.Validated)
255+
!send.UserId.HasValue || fileData?.Id != fileId || fileData.Validated)
253256
{
254257
// Not found if Send isn't found, user doesn't have access, request is faulty,
255258
// or we've already validated the file. This last is to emulate create-only blob permissions for Azure
@@ -260,7 +263,7 @@ public async Task<SendFileUploadDataResponseModel> RenewFileUpload(string id, st
260263
{
261264
Url = await _sendFileStorageService.GetSendFileUploadUrlAsync(send, fileId),
262265
FileUploadType = _sendFileStorageService.FileUploadType,
263-
SendResponse = new SendResponseModel(send, _globalSettings),
266+
SendResponse = new SendResponseModel(send),
264267
};
265268
}
266269

@@ -270,12 +273,16 @@ public async Task<SendFileUploadDataResponseModel> RenewFileUpload(string id, st
270273
[DisableFormValueModelBinding]
271274
public async Task PostFileForExistingSend(string id, string fileId)
272275
{
273-
if (!Request?.ContentType.Contains("multipart/") ?? true)
276+
if (!Request?.ContentType?.Contains("multipart/") ?? true)
274277
{
275278
throw new BadRequestException("Invalid content.");
276279
}
277280

278281
var send = await _sendRepository.GetByIdAsync(new Guid(id));
282+
if (send == null)
283+
{
284+
throw new BadRequestException("Could not locate send");
285+
}
279286
await Request.GetFileAsync(async (stream) =>
280287
{
281288
await _nonAnonymousSendCommand.UploadFileToExistingSendAsync(stream, send);
@@ -286,36 +293,39 @@ await Request.GetFileAsync(async (stream) =>
286293
public async Task<SendResponseModel> Put(string id, [FromBody] SendRequestModel model)
287294
{
288295
model.ValidateEdit();
289-
var userId = _userService.GetProperUserId(User).Value;
296+
var userId = _userService.GetProperUserId(User) ?? throw new InvalidOperationException("User ID not found");
290297
var send = await _sendRepository.GetByIdAsync(new Guid(id));
291298
if (send == null || send.UserId != userId)
292299
{
293300
throw new NotFoundException();
294301
}
295302

296-
await _nonAnonymousSendCommand.SaveSendAsync(model.ToSend(send, _sendAuthorizationService));
297-
return new SendResponseModel(send, _globalSettings);
303+
await _nonAnonymousSendCommand.SaveSendAsync(model.UpdateSend(send, _sendAuthorizationService));
304+
return new SendResponseModel(send);
298305
}
299306

300307
[HttpPut("{id}/remove-password")]
301308
public async Task<SendResponseModel> PutRemovePassword(string id)
302309
{
303-
var userId = _userService.GetProperUserId(User).Value;
310+
var userId = _userService.GetProperUserId(User) ?? throw new InvalidOperationException("User ID not found");
304311
var send = await _sendRepository.GetByIdAsync(new Guid(id));
305312
if (send == null || send.UserId != userId)
306313
{
307314
throw new NotFoundException();
308315
}
309316

317+
// This endpoint exists because PUT preserves existing Password/Emails when not provided.
318+
// This allows clients to update other fields without re-submitting sensitive auth data.
310319
send.Password = null;
320+
send.AuthType = AuthType.None;
311321
await _nonAnonymousSendCommand.SaveSendAsync(send);
312-
return new SendResponseModel(send, _globalSettings);
322+
return new SendResponseModel(send);
313323
}
314324

315325
[HttpDelete("{id}")]
316326
public async Task Delete(string id)
317327
{
318-
var userId = _userService.GetProperUserId(User).Value;
328+
var userId = _userService.GetProperUserId(User) ?? throw new InvalidOperationException("User ID not found");
319329
var send = await _sendRepository.GetByIdAsync(new Guid(id));
320330
if (send == null || send.UserId != userId)
321331
{

0 commit comments

Comments
 (0)