using System.ComponentModel.DataAnnotations; using System.Text.Json; using Bit.Core.Exceptions; using Bit.Core.Tools.Entities; using Bit.Core.Tools.Enums; using Bit.Core.Tools.Models.Data; using Bit.Core.Tools.Services; using Bit.Core.Utilities; namespace Bit.Api.Tools.Models.Request; public class SendRequestModel { public SendType Type { get; set; } public long? FileLength { get; set; } = null; [EncryptedString] [EncryptedStringLength(1000)] public string Name { get; set; } [EncryptedString] [EncryptedStringLength(1000)] public string Notes { get; set; } [Required] [EncryptedString] [EncryptedStringLength(1000)] public string Key { get; set; } [Range(1, int.MaxValue)] public int? MaxAccessCount { get; set; } public DateTime? ExpirationDate { get; set; } [Required] public DateTime? DeletionDate { get; set; } public SendFileModel File { get; set; } public SendTextModel Text { get; set; } [StringLength(1000)] public string Password { get; set; } [Required] public bool? Disabled { get; set; } public bool? HideEmail { get; set; } public Send ToSend(Guid userId, ISendAuthorizationService sendAuthorizationService) { var send = new Send { Type = Type, UserId = (Guid?)userId }; ToSend(send, sendAuthorizationService); return send; } public (Send, SendFileData) ToSend(Guid userId, string fileName, ISendAuthorizationService sendAuthorizationService) { var send = ToSendBase(new Send { Type = Type, UserId = (Guid?)userId }, sendAuthorizationService); var data = new SendFileData(Name, Notes, fileName); return (send, data); } public Send ToSend(Send existingSend, ISendAuthorizationService sendAuthorizationService) { existingSend = ToSendBase(existingSend, sendAuthorizationService); switch (existingSend.Type) { case SendType.File: var fileData = JsonSerializer.Deserialize(existingSend.Data); fileData.Name = Name; fileData.Notes = Notes; existingSend.Data = JsonSerializer.Serialize(fileData, JsonHelpers.IgnoreWritingNull); break; case SendType.Text: existingSend.Data = JsonSerializer.Serialize(ToSendTextData(), JsonHelpers.IgnoreWritingNull); break; default: throw new ArgumentException("Unsupported type: " + nameof(Type) + "."); } return existingSend; } public void ValidateCreation() { var now = DateTime.UtcNow; // Add 1 minute for a sane buffer and client clock float var nowPlus1Minute = now.AddMinutes(1); if (ExpirationDate.HasValue && ExpirationDate.Value <= nowPlus1Minute) { throw new BadRequestException("You cannot create a Send that is already expired. " + "Adjust the expiration date and try again."); } ValidateEdit(); } public void ValidateEdit() { var now = DateTime.UtcNow; // Add 1 minute for a sane buffer and client clock float var nowPlus1Minute = now.AddMinutes(1); if (DeletionDate.HasValue) { if (DeletionDate.Value <= nowPlus1Minute) { throw new BadRequestException("You cannot have a Send with a deletion date in the past. " + "Adjust the deletion date and try again."); } if (DeletionDate.Value > now.AddDays(31)) { throw new BadRequestException("You cannot have a Send with a deletion date that far " + "into the future. Adjust the Deletion Date to a value less than 31 days from now " + "and try again."); } } if (ExpirationDate.HasValue) { if (ExpirationDate.Value <= nowPlus1Minute) { throw new BadRequestException("You cannot have a Send with an expiration date in the past. " + "Adjust the expiration date and try again."); } if (ExpirationDate.Value > DeletionDate.Value) { throw new BadRequestException("You cannot have a Send with an expiration date greater than the deletion date. " + "Adjust the expiration date and try again."); } } } private Send ToSendBase(Send existingSend, ISendAuthorizationService authorizationService) { existingSend.Key = Key; existingSend.ExpirationDate = ExpirationDate; existingSend.DeletionDate = DeletionDate.Value; existingSend.MaxAccessCount = MaxAccessCount; if (!string.IsNullOrWhiteSpace(Password)) { existingSend.Password = authorizationService.HashPassword(Password); } existingSend.Disabled = Disabled.GetValueOrDefault(); existingSend.HideEmail = HideEmail.GetValueOrDefault(); return existingSend; } private SendTextData ToSendTextData() { return new SendTextData(Name, Notes, Text.Text, Text.Hidden); } } public class SendWithIdRequestModel : SendRequestModel { [Required] public Guid? Id { get; set; } }