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 ;
52using Azure . Messaging . EventGrid ;
63using Bit . Api . Models . Response ;
74using Bit . Api . Tools . Models . Request ;
1613using Bit . Core . Tools . Repositories ;
1714using Bit . Core . Tools . SendFeatures ;
1815using Bit . Core . Tools . SendFeatures . Commands . Interfaces ;
16+ using Bit . Core . Tools . SendFeatures . Queries . Interfaces ;
1917using Bit . Core . Tools . Services ;
2018using Bit . Core . Utilities ;
2119using 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