mirror of
https://github.com/bitwarden/server.git
synced 2025-07-09 20:03:47 -05:00
post, upload, and save cipher attachment
This commit is contained in:
19
src/Core/Models/Data/Attachment.cs
Normal file
19
src/Core/Models/Data/Attachment.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Bit.Core.Models.Data
|
||||
{
|
||||
public class CipherAttachment
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public Guid? OrganizationId { get; set; }
|
||||
public string AttachmentId { get; set; }
|
||||
public string AttachmentData { get; set; }
|
||||
|
||||
public class MetaData
|
||||
{
|
||||
public long Size { get; set; }
|
||||
public string FileName { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ namespace Bit.Core.Models.Table
|
||||
public string Data { get; set; }
|
||||
public string Favorites { get; set; }
|
||||
public string Folders { get; set; }
|
||||
public string Attachments { get; set; }
|
||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;
|
||||
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Table;
|
||||
using Core.Models.Data;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Repositories
|
||||
{
|
||||
@ -19,6 +20,7 @@ namespace Bit.Core.Repositories
|
||||
Task UpsertAsync(CipherDetails cipher);
|
||||
Task ReplaceAsync(Cipher obj, IEnumerable<Guid> collectionIds);
|
||||
Task UpdatePartialAsync(Guid id, Guid userId, Guid? folderId, bool favorite);
|
||||
Task UpdateAttachmentAsync(CipherAttachment attachment);
|
||||
Task DeleteAsync(IEnumerable<Guid> ids, Guid userId);
|
||||
Task MoveAsync(IEnumerable<Guid> ids, Guid? folderId, Guid userId);
|
||||
Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders);
|
||||
|
@ -9,6 +9,7 @@ using Dapper;
|
||||
using Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Repositories.SqlServer
|
||||
{
|
||||
@ -176,6 +177,17 @@ namespace Bit.Core.Repositories.SqlServer
|
||||
}
|
||||
}
|
||||
|
||||
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 DeleteAsync(IEnumerable<Guid> ids, Guid userId)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
|
@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Table;
|
||||
using Core.Models.Data;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@ -10,6 +11,8 @@ namespace Bit.Core.Services
|
||||
{
|
||||
Task SaveAsync(Cipher cipher, Guid savingUserId, bool orgAdmin = false);
|
||||
Task SaveDetailsAsync(CipherDetails cipher, Guid savingUserId);
|
||||
Task AttachAsync(Cipher cipher, Stream stream, string fileName, long requestLength, Guid savingUserId,
|
||||
bool orgAdmin = false);
|
||||
Task DeleteAsync(Cipher cipher, Guid deletingUserId, bool orgAdmin = false);
|
||||
Task DeleteManyAsync(IEnumerable<Guid> cipherIds, Guid deletingUserId);
|
||||
Task MoveManyAsync(IEnumerable<Guid> cipherIds, Guid? destinationFolderId, Guid movingUserId);
|
||||
|
@ -6,6 +6,9 @@ using Bit.Core.Models.Table;
|
||||
using Bit.Core.Repositories;
|
||||
using Core.Models.Data;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@ -18,6 +21,7 @@ namespace Bit.Core.Services
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly ICollectionCipherRepository _collectionCipherRepository;
|
||||
private readonly IPushNotificationService _pushService;
|
||||
private readonly IAttachmentStorageService _attachmentStorageService;
|
||||
|
||||
public CipherService(
|
||||
ICipherRepository cipherRepository,
|
||||
@ -26,7 +30,8 @@ namespace Bit.Core.Services
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
ICollectionCipherRepository collectionCipherRepository,
|
||||
IPushNotificationService pushService)
|
||||
IPushNotificationService pushService,
|
||||
IAttachmentStorageService attachmentStorageService)
|
||||
{
|
||||
_cipherRepository = cipherRepository;
|
||||
_folderRepository = folderRepository;
|
||||
@ -35,6 +40,7 @@ namespace Bit.Core.Services
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_collectionCipherRepository = collectionCipherRepository;
|
||||
_pushService = pushService;
|
||||
_attachmentStorageService = attachmentStorageService;
|
||||
}
|
||||
|
||||
public async Task SaveAsync(Cipher cipher, Guid savingUserId, bool orgAdmin = false)
|
||||
@ -86,6 +92,45 @@ namespace Bit.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AttachAsync(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.");
|
||||
}
|
||||
|
||||
// TODO: check available space against requestLength
|
||||
|
||||
var attachmentId = Utilities.CoreHelpers.SecureRandomString(32, upper: false, special: false);
|
||||
await _attachmentStorageService.UploadAttachmentAsync(stream, $"{cipher.Id}/{attachmentId}");
|
||||
|
||||
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);
|
||||
|
||||
// push
|
||||
await _pushService.PushSyncCipherUpdateAsync(cipher);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(Cipher cipher, Guid deletingUserId, bool orgAdmin = false)
|
||||
{
|
||||
if(!orgAdmin && !(await UserCanEditAsync(cipher, deletingUserId)))
|
||||
|
@ -53,8 +53,7 @@ namespace Bit.Core.Utilities
|
||||
services.AddSingleton<IPushNotificationService, NotificationHubPushNotificationService>();
|
||||
services.AddSingleton<IBlockIpService, AzureQueueBlockIpService>();
|
||||
services.AddSingleton<IPushRegistrationService, NotificationHubPushRegistrationService>();
|
||||
// noop for now
|
||||
services.AddSingleton<IAttachmentStorageService, NoopAttachmentStorageService>();
|
||||
services.AddSingleton<IAttachmentStorageService, AzureAttachmentStorageService>();
|
||||
}
|
||||
|
||||
public static void AddNoopServices(this IServiceCollection services)
|
||||
|
Reference in New Issue
Block a user