public async Task <bool> ValidateCipherAttachmentFile(Cipher cipher, CipherAttachment.MetaData attachmentData) { var(valid, realSize) = await _attachmentStorageService.ValidateFileAsync(cipher, attachmentData, _fileSizeLeeway); if (!valid || realSize > MAX_FILE_SIZE) { // File reported differs in size from that promised. Must be a rogue client. Delete Send await DeleteAttachmentAsync(cipher, attachmentData); return(false); } // Update Send data if necessary if (realSize != attachmentData.Size) { attachmentData.Size = realSize.Value; } attachmentData.Validated = true; var updatedAttachment = new CipherAttachment { Id = cipher.Id, UserId = cipher.UserId, OrganizationId = cipher.OrganizationId, AttachmentId = attachmentData.AttachmentId, AttachmentData = JsonConvert.SerializeObject(attachmentData) }; await _cipherRepository.UpdateAttachmentAsync(updatedAttachment); return(valid); }
public async Task UpdateAttachmentAsync(CipherAttachment attachment) { using (var scope = ServiceScopeFactory.CreateScope()) { var dbContext = GetDatabaseContext(scope); var cipher = await dbContext.Ciphers.FindAsync(attachment.Id); var attachmentsJson = string.IsNullOrWhiteSpace(cipher.Attachments) ? new JObject() : JObject.Parse(cipher.Attachments); attachmentsJson.Add(attachment.AttachmentId, attachment.AttachmentData); cipher.Attachments = JsonConvert.SerializeObject(attachmentsJson); await dbContext.SaveChangesAsync(); if (attachment.OrganizationId.HasValue) { await OrganizationUpdateStorage(cipher.OrganizationId.Value); await UserBumpAccountRevisionDateByCipherId(new List <Cipher> { cipher }); } else if (attachment.UserId.HasValue) { await UserUpdateStorage(attachment.UserId.Value); await UserBumpAccountRevisionDate(attachment.UserId.Value); } } }
public async Task CreateAttachmentAsync(Cipher cipher, Stream stream, string fileName, string key, long requestLength, Guid savingUserId, bool orgAdmin = false) { if (!orgAdmin && !(await UserCanEditAsync(cipher, savingUserId))) { throw new BadRequestException("You do not have permissions to edit this."); } if (requestLength < 1) { throw new BadRequestException("No data to attach."); } var storageBytesRemaining = 10240L; if (storageBytesRemaining < requestLength) { throw new BadRequestException("Not enough storage available."); } var attachmentId = Utilities.CoreHelpers.SecureRandomString(32, upper: false, special: false); await _attachmentStorageService.UploadNewAttachmentAsync(stream, cipher, attachmentId); try { var data = new CipherAttachment.MetaData { FileName = fileName, Key = key, Size = stream.Length }; var attachment = new CipherAttachment { Id = cipher.Id, UserId = cipher.UserId, OrganizationId = cipher.OrganizationId, AttachmentId = attachmentId, AttachmentData = JsonConvert.SerializeObject(data) }; await _cipherRepository.UpdateAttachmentAsync(attachment); await _eventService.LogCipherEventAsync(cipher, Enums.EventType.Cipher_AttachmentCreated); cipher.AddAttachment(attachmentId, data); } catch { // Clean up since this is not transactional await _attachmentStorageService.DeleteAttachmentAsync(cipher.Id, attachmentId); throw; } // push await _pushService.PushSyncCipherUpdateAsync(cipher, null); }
public async Task UpdateAttachmentAsync(CipherAttachment attachment) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.ExecuteAsync( $"[{Schema}].[Cipher_UpdateAttachment]", attachment, commandType : CommandType.StoredProcedure); } }
public async Task CreateAttachmentAsync(Cipher cipher, Stream stream, string fileName, string key, long requestLength, Guid savingUserId, bool orgAdmin = false) { await ValidateCipherEditForAttachmentAsync(cipher, savingUserId, orgAdmin, requestLength); var attachmentId = Utilities.CoreHelpers.SecureRandomString(32, upper: false, special: false); var data = new CipherAttachment.MetaData { AttachmentId = attachmentId, FileName = fileName, Key = key, }; await _attachmentStorageService.UploadNewAttachmentAsync(stream, cipher, data); // Must read stream length after it has been saved, otherwise it's 0 data.Size = stream.Length; try { var attachment = new CipherAttachment { Id = cipher.Id, UserId = cipher.UserId, OrganizationId = cipher.OrganizationId, AttachmentId = attachmentId, AttachmentData = JsonConvert.SerializeObject(data) }; await _cipherRepository.UpdateAttachmentAsync(attachment); await _eventService.LogCipherEventAsync(cipher, Enums.EventType.Cipher_AttachmentCreated); cipher.AddAttachment(attachmentId, data); if (!await ValidateCipherAttachmentFile(cipher, data)) { throw new Exception("Content-Length does not match uploaded file size"); } } catch { // Clean up since this is not transactional await _attachmentStorageService.DeleteAttachmentAsync(cipher.Id, data); throw; } // push await _pushService.PushSyncCipherUpdateAsync(cipher, null); }
public async Task CreateAttachmentAsync(Cipher cipher, Stream stream, string fileName, long requestLength, Guid savingUserId, bool orgAdmin = false) { if (!orgAdmin && !(await UserCanEditAsync(cipher, savingUserId))) { throw new BadRequestException("You do not have permissions to edit this."); } if (requestLength < 1) { throw new BadRequestException("No data to attach."); } var storageBytesRemaining = 0L; if (cipher.UserId.HasValue) { var user = await _userRepository.GetByIdAsync(cipher.UserId.Value); if (!(await _userService.CanAccessPremium(user))) { throw new BadRequestException("You must have premium status to use attachments."); } if (user.Premium) { storageBytesRemaining = user.StorageBytesRemaining(); } else { // Users that get access to file storage/premium from their organization get the default // 1 GB max storage. storageBytesRemaining = user.StorageBytesRemaining( _globalSettings.SelfHosted ? (short)1024 : (short)1); } } else if (cipher.OrganizationId.HasValue) { var org = await _organizationRepository.GetByIdAsync(cipher.OrganizationId.Value); if (!org.MaxStorageGb.HasValue) { throw new BadRequestException("This organization cannot use attachments."); } storageBytesRemaining = org.StorageBytesRemaining(); } if (storageBytesRemaining < requestLength) { throw new BadRequestException("Not enough storage available."); } var attachmentId = Utilities.CoreHelpers.SecureRandomString(32, upper: false, special: false); await _attachmentStorageService.UploadNewAttachmentAsync(stream, cipher, attachmentId); try { var data = new CipherAttachment.MetaData { FileName = fileName, Size = stream.Length }; var attachment = new CipherAttachment { Id = cipher.Id, UserId = cipher.UserId, OrganizationId = cipher.OrganizationId, AttachmentId = attachmentId, AttachmentData = JsonConvert.SerializeObject(data) }; await _cipherRepository.UpdateAttachmentAsync(attachment); await _eventService.LogCipherEventAsync(cipher, Enums.EventType.Cipher_AttachmentCreated); cipher.AddAttachment(attachmentId, data); } catch { // Clean up since this is not transactional await _attachmentStorageService.DeleteAttachmentAsync(cipher.Id, attachmentId); throw; } // push await _pushService.PushSyncCipherUpdateAsync(cipher, null); }
public async Task CreateAttachmentShareAsync(Cipher cipher, Stream stream, long requestLength, string attachmentId, Guid organizationId) { try { if (requestLength < 1) { throw new BadRequestException("No data to attach."); } if (cipher.Id == default(Guid)) { throw new BadRequestException(nameof(cipher.Id)); } if (cipher.OrganizationId.HasValue) { throw new BadRequestException("Cipher belongs to an organization already."); } var org = await _organizationRepository.GetByIdAsync(organizationId); if (org == null || !org.MaxStorageGb.HasValue) { throw new BadRequestException("This organization cannot use attachments."); } var storageBytesRemaining = org.StorageBytesRemaining(); if (storageBytesRemaining < requestLength) { throw new BadRequestException("Not enough storage available for this organization."); } var attachments = cipher.GetAttachments(); if (!attachments.ContainsKey(attachmentId)) { throw new BadRequestException($"Cipher does not own specified attachment"); } await _attachmentStorageService.UploadShareAttachmentAsync(stream, cipher.Id, organizationId, attachments[attachmentId]); // Previous call may alter metadata var updatedAttachment = new CipherAttachment { Id = cipher.Id, UserId = cipher.UserId, OrganizationId = cipher.OrganizationId, AttachmentId = attachmentId, AttachmentData = JsonConvert.SerializeObject(attachments[attachmentId]) }; await _cipherRepository.UpdateAttachmentAsync(updatedAttachment); } catch { await _attachmentStorageService.CleanupAsync(cipher.Id); throw; } }