mirror of
https://github.com/bitwarden/server.git
synced 2025-07-01 08:02:49 -05:00
[SG-998] Move files to Vault folders (#2724)
* Move Api files * Move Core files * Move Infrastructure files * Move Sql Files * Move Api Sync files to Vault * Move test vault files * Update Sql.sqlproj paths * Update Codeowners * Fix vault file paths in sqlproj * Update CipherDetails.sql path in sqlproj * Update Core models and entities namespaces * Update namespaces Core Services and Repositories * Missed service namespaces * Update Api namespaces * Update Infrastructure namespaces * Move infrastructure queries that were missed * Tests namespace updates * Admin and Events namespace updates * Remove unused usings * Remove extra CiphersController usings * Rename folder * Fix CipherDetails namespace * Sqlproj fixes * Move stored procs into folders by table * using order fix
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Bit.Core.Vault.Models.Data;
|
||||
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
|
@ -1,43 +0,0 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Models.Data;
|
||||
using Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
public interface ICipherService
|
||||
{
|
||||
Task SaveAsync(Cipher cipher, Guid savingUserId, DateTime? lastKnownRevisionDate, IEnumerable<Guid> collectionIds = null,
|
||||
bool skipPermissionCheck = false, bool limitCollectionScope = true);
|
||||
Task SaveDetailsAsync(CipherDetails cipher, Guid savingUserId, DateTime? lastKnownRevisionDate,
|
||||
IEnumerable<Guid> collectionIds = null, bool skipPermissionCheck = false);
|
||||
Task<(string attachmentId, string uploadUrl)> CreateAttachmentForDelayedUploadAsync(Cipher cipher,
|
||||
string key, string fileName, long fileSize, bool adminRequest, Guid savingUserId);
|
||||
Task CreateAttachmentAsync(Cipher cipher, Stream stream, string fileName, string key,
|
||||
long requestLength, Guid savingUserId, bool orgAdmin = false);
|
||||
Task CreateAttachmentShareAsync(Cipher cipher, Stream stream, long requestLength, string attachmentId,
|
||||
Guid organizationShareId);
|
||||
Task DeleteAsync(Cipher cipher, Guid deletingUserId, bool orgAdmin = false);
|
||||
Task DeleteManyAsync(IEnumerable<Guid> cipherIds, Guid deletingUserId, Guid? organizationId = null, bool orgAdmin = false);
|
||||
Task DeleteAttachmentAsync(Cipher cipher, string attachmentId, Guid deletingUserId, bool orgAdmin = false);
|
||||
Task PurgeAsync(Guid organizationId);
|
||||
Task MoveManyAsync(IEnumerable<Guid> cipherIds, Guid? destinationFolderId, Guid movingUserId);
|
||||
Task SaveFolderAsync(Folder folder);
|
||||
Task DeleteFolderAsync(Folder folder);
|
||||
Task ShareAsync(Cipher originalCipher, Cipher cipher, Guid organizationId, IEnumerable<Guid> collectionIds,
|
||||
Guid userId, DateTime? lastKnownRevisionDate);
|
||||
Task ShareManyAsync(IEnumerable<(Cipher cipher, DateTime? lastKnownRevisionDate)> ciphers, Guid organizationId,
|
||||
IEnumerable<Guid> collectionIds, Guid sharingUserId);
|
||||
Task SaveCollectionsAsync(Cipher cipher, IEnumerable<Guid> collectionIds, Guid savingUserId, bool orgAdmin);
|
||||
Task ImportCiphersAsync(List<Folder> folders, List<CipherDetails> ciphers,
|
||||
IEnumerable<KeyValuePair<int, int>> folderRelationships);
|
||||
Task ImportCiphersAsync(List<Collection> collections, List<CipherDetails> ciphers,
|
||||
IEnumerable<KeyValuePair<int, int>> collectionRelationships, Guid importingUserId);
|
||||
Task SoftDeleteAsync(Cipher cipher, Guid deletingUserId, bool orgAdmin = false);
|
||||
Task SoftDeleteManyAsync(IEnumerable<Guid> cipherIds, Guid deletingUserId, Guid? organizationId = null, bool orgAdmin = false);
|
||||
Task RestoreAsync(Cipher cipher, Guid restoringUserId, bool orgAdmin = false);
|
||||
Task RestoreManyAsync(IEnumerable<CipherDetails> ciphers, Guid restoringUserId);
|
||||
Task UploadFileForExistingAttachmentAsync(Stream stream, Cipher cipher, CipherAttachment.MetaData attachmentId);
|
||||
Task<AttachmentResponseData> GetAttachmentDownloadDataAsync(Cipher cipher, string attachmentId);
|
||||
Task<bool> ValidateCipherAttachmentFile(Cipher cipher, CipherAttachment.MetaData attachmentData);
|
||||
Task<(IEnumerable<CipherOrganizationDetails>, Dictionary<Guid, IGrouping<Guid, CollectionCipher>>)> GetOrganizationCiphers(Guid userId, Guid organizationId);
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Vault.Models.Data;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
using Bit.Core.Entities.Provider;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Vault.Entities;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
|
@ -3,6 +3,7 @@ using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Fido2NetLib;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
|
@ -1,266 +0,0 @@
|
||||
using Azure.Storage.Blobs;
|
||||
using Azure.Storage.Blobs.Models;
|
||||
using Azure.Storage.Sas;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
public class AzureAttachmentStorageService : IAttachmentStorageService
|
||||
{
|
||||
public FileUploadType FileUploadType => FileUploadType.Azure;
|
||||
public const string EventGridEnabledContainerName = "attachments-v2";
|
||||
private const string _defaultContainerName = "attachments";
|
||||
private readonly static string[] _attachmentContainerName = { "attachments", "attachments-v2" };
|
||||
private static readonly TimeSpan blobLinkLiveTime = TimeSpan.FromMinutes(1);
|
||||
private readonly BlobServiceClient _blobServiceClient;
|
||||
private readonly Dictionary<string, BlobContainerClient> _attachmentContainers = new Dictionary<string, BlobContainerClient>();
|
||||
private readonly ILogger<AzureAttachmentStorageService> _logger;
|
||||
|
||||
private string BlobName(Guid cipherId, CipherAttachment.MetaData attachmentData, Guid? organizationId = null, bool temp = false) =>
|
||||
string.Concat(
|
||||
temp ? "temp/" : "",
|
||||
$"{cipherId}/",
|
||||
organizationId != null ? $"{organizationId.Value}/" : "",
|
||||
attachmentData.AttachmentId
|
||||
);
|
||||
|
||||
public static (string cipherId, string organizationId, string attachmentId) IdentifiersFromBlobName(string blobName)
|
||||
{
|
||||
var parts = blobName.Split('/');
|
||||
switch (parts.Length)
|
||||
{
|
||||
case 4:
|
||||
return (parts[1], parts[2], parts[3]);
|
||||
case 3:
|
||||
if (parts[0] == "temp")
|
||||
{
|
||||
return (parts[1], null, parts[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (parts[0], parts[1], parts[2]);
|
||||
}
|
||||
case 2:
|
||||
return (parts[0], null, parts[1]);
|
||||
default:
|
||||
throw new Exception("Cannot determine cipher information from blob name");
|
||||
}
|
||||
}
|
||||
|
||||
public AzureAttachmentStorageService(
|
||||
GlobalSettings globalSettings,
|
||||
ILogger<AzureAttachmentStorageService> logger)
|
||||
{
|
||||
_blobServiceClient = new BlobServiceClient(globalSettings.Attachment.ConnectionString);
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<string> GetAttachmentDownloadUrlAsync(Cipher cipher, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
await InitAsync(attachmentData.ContainerName);
|
||||
var blobClient = _attachmentContainers[attachmentData.ContainerName].GetBlobClient(BlobName(cipher.Id, attachmentData));
|
||||
var sasUri = blobClient.GenerateSasUri(BlobSasPermissions.Read, DateTime.UtcNow.Add(blobLinkLiveTime));
|
||||
return sasUri.ToString();
|
||||
}
|
||||
|
||||
public async Task<string> GetAttachmentUploadUrlAsync(Cipher cipher, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
await InitAsync(EventGridEnabledContainerName);
|
||||
var blobClient = _attachmentContainers[EventGridEnabledContainerName].GetBlobClient(BlobName(cipher.Id, attachmentData));
|
||||
attachmentData.ContainerName = EventGridEnabledContainerName;
|
||||
var sasUri = blobClient.GenerateSasUri(BlobSasPermissions.Create | BlobSasPermissions.Write, DateTime.UtcNow.Add(blobLinkLiveTime));
|
||||
return sasUri.ToString();
|
||||
}
|
||||
|
||||
public async Task UploadNewAttachmentAsync(Stream stream, Cipher cipher, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
attachmentData.ContainerName = _defaultContainerName;
|
||||
await InitAsync(_defaultContainerName);
|
||||
var blobClient = _attachmentContainers[_defaultContainerName].GetBlobClient(BlobName(cipher.Id, attachmentData));
|
||||
|
||||
var metadata = new Dictionary<string, string>();
|
||||
metadata.Add("cipherId", cipher.Id.ToString());
|
||||
if (cipher.UserId.HasValue)
|
||||
{
|
||||
metadata.Add("userId", cipher.UserId.Value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
metadata.Add("organizationId", cipher.OrganizationId.Value.ToString());
|
||||
}
|
||||
|
||||
var headers = new BlobHttpHeaders
|
||||
{
|
||||
ContentDisposition = $"attachment; filename=\"{attachmentData.AttachmentId}\""
|
||||
};
|
||||
await blobClient.UploadAsync(stream, new BlobUploadOptions { Metadata = metadata, HttpHeaders = headers });
|
||||
}
|
||||
|
||||
public async Task UploadShareAttachmentAsync(Stream stream, Guid cipherId, Guid organizationId, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
attachmentData.ContainerName = _defaultContainerName;
|
||||
await InitAsync(_defaultContainerName);
|
||||
var blobClient = _attachmentContainers[_defaultContainerName].GetBlobClient(
|
||||
BlobName(cipherId, attachmentData, organizationId, temp: true));
|
||||
|
||||
var metadata = new Dictionary<string, string>();
|
||||
metadata.Add("cipherId", cipherId.ToString());
|
||||
metadata.Add("organizationId", organizationId.ToString());
|
||||
|
||||
var headers = new BlobHttpHeaders
|
||||
{
|
||||
ContentDisposition = $"attachment; filename=\"{attachmentData.AttachmentId}\""
|
||||
};
|
||||
await blobClient.UploadAsync(stream, new BlobUploadOptions { Metadata = metadata, HttpHeaders = headers });
|
||||
}
|
||||
|
||||
public async Task StartShareAttachmentAsync(Guid cipherId, Guid organizationId, CipherAttachment.MetaData data)
|
||||
{
|
||||
await InitAsync(data.ContainerName);
|
||||
var source = _attachmentContainers[data.ContainerName].GetBlobClient(
|
||||
BlobName(cipherId, data, organizationId, temp: true));
|
||||
if (!await source.ExistsAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await InitAsync(_defaultContainerName);
|
||||
var dest = _attachmentContainers[_defaultContainerName].GetBlobClient(BlobName(cipherId, data));
|
||||
if (!await dest.ExistsAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var original = _attachmentContainers[_defaultContainerName].GetBlobClient(
|
||||
BlobName(cipherId, data, temp: true));
|
||||
await original.DeleteIfExistsAsync();
|
||||
await original.StartCopyFromUriAsync(dest.Uri);
|
||||
|
||||
await dest.DeleteIfExistsAsync();
|
||||
await dest.StartCopyFromUriAsync(source.Uri);
|
||||
}
|
||||
|
||||
public async Task RollbackShareAttachmentAsync(Guid cipherId, Guid organizationId, CipherAttachment.MetaData attachmentData, string originalContainer)
|
||||
{
|
||||
await InitAsync(attachmentData.ContainerName);
|
||||
var source = _attachmentContainers[attachmentData.ContainerName].GetBlobClient(
|
||||
BlobName(cipherId, attachmentData, organizationId, temp: true));
|
||||
await source.DeleteIfExistsAsync();
|
||||
|
||||
await InitAsync(originalContainer);
|
||||
var original = _attachmentContainers[originalContainer].GetBlobClient(
|
||||
BlobName(cipherId, attachmentData, temp: true));
|
||||
if (!await original.ExistsAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dest = _attachmentContainers[originalContainer].GetBlobClient(
|
||||
BlobName(cipherId, attachmentData));
|
||||
await dest.DeleteIfExistsAsync();
|
||||
await dest.StartCopyFromUriAsync(original.Uri);
|
||||
await original.DeleteIfExistsAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAttachmentAsync(Guid cipherId, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
await InitAsync(attachmentData.ContainerName);
|
||||
var blobClient = _attachmentContainers[attachmentData.ContainerName].GetBlobClient(
|
||||
BlobName(cipherId, attachmentData));
|
||||
await blobClient.DeleteIfExistsAsync();
|
||||
}
|
||||
|
||||
public async Task CleanupAsync(Guid cipherId) => await DeleteAttachmentsForPathAsync($"temp/{cipherId}");
|
||||
|
||||
public async Task DeleteAttachmentsForCipherAsync(Guid cipherId) =>
|
||||
await DeleteAttachmentsForPathAsync(cipherId.ToString());
|
||||
|
||||
public async Task DeleteAttachmentsForOrganizationAsync(Guid organizationId)
|
||||
{
|
||||
await InitAsync(_defaultContainerName);
|
||||
}
|
||||
|
||||
public async Task DeleteAttachmentsForUserAsync(Guid userId)
|
||||
{
|
||||
await InitAsync(_defaultContainerName);
|
||||
}
|
||||
|
||||
public async Task<(bool, long?)> ValidateFileAsync(Cipher cipher, CipherAttachment.MetaData attachmentData, long leeway)
|
||||
{
|
||||
await InitAsync(attachmentData.ContainerName);
|
||||
|
||||
var blobClient = _attachmentContainers[attachmentData.ContainerName].GetBlobClient(BlobName(cipher.Id, attachmentData));
|
||||
|
||||
try
|
||||
{
|
||||
var blobProperties = await blobClient.GetPropertiesAsync();
|
||||
|
||||
var metadata = blobProperties.Value.Metadata;
|
||||
metadata["cipherId"] = cipher.Id.ToString();
|
||||
if (cipher.UserId.HasValue)
|
||||
{
|
||||
metadata["userId"] = cipher.UserId.Value.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
metadata["organizationId"] = cipher.OrganizationId.Value.ToString();
|
||||
}
|
||||
await blobClient.SetMetadataAsync(metadata);
|
||||
|
||||
var headers = new BlobHttpHeaders
|
||||
{
|
||||
ContentDisposition = $"attachment; filename=\"{attachmentData.AttachmentId}\""
|
||||
};
|
||||
await blobClient.SetHttpHeadersAsync(headers);
|
||||
|
||||
var length = blobProperties.Value.ContentLength;
|
||||
if (length < attachmentData.Size - leeway || length > attachmentData.Size + leeway)
|
||||
{
|
||||
return (false, length);
|
||||
}
|
||||
|
||||
return (true, length);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Unhandled error in ValidateFileAsync");
|
||||
return (false, null);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteAttachmentsForPathAsync(string path)
|
||||
{
|
||||
foreach (var container in _attachmentContainerName)
|
||||
{
|
||||
await InitAsync(container);
|
||||
var blobContainerClient = _attachmentContainers[container];
|
||||
|
||||
var blobItems = blobContainerClient.GetBlobsAsync(BlobTraits.None, BlobStates.None, prefix: path);
|
||||
await foreach (var blobItem in blobItems)
|
||||
{
|
||||
BlobClient blobClient = blobContainerClient.GetBlobClient(blobItem.Name);
|
||||
await blobClient.DeleteIfExistsAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InitAsync(string containerName)
|
||||
{
|
||||
if (!_attachmentContainers.ContainsKey(containerName) || _attachmentContainers[containerName] == null)
|
||||
{
|
||||
_attachmentContainers[containerName] = _blobServiceClient.GetBlobContainerClient(containerName);
|
||||
if (containerName == "attachments")
|
||||
{
|
||||
await _attachmentContainers[containerName].CreateIfNotExistsAsync(PublicAccessType.Blob, null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _attachmentContainers[containerName].CreateIfNotExistsAsync(PublicAccessType.None, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,9 @@ using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tokens;
|
||||
using Bit.Core.Vault.Models.Data;
|
||||
using Bit.Core.Vault.Repositories;
|
||||
using Bit.Core.Vault.Services;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
@ -7,6 +7,7 @@ using Bit.Core.Models.Data.Organizations;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Vault.Entities;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
|
@ -1,196 +0,0 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Settings;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
public class LocalAttachmentStorageService : IAttachmentStorageService
|
||||
{
|
||||
private readonly string _baseAttachmentUrl;
|
||||
private readonly string _baseDirPath;
|
||||
private readonly string _baseTempDirPath;
|
||||
|
||||
public FileUploadType FileUploadType => FileUploadType.Direct;
|
||||
|
||||
public LocalAttachmentStorageService(
|
||||
IGlobalSettings globalSettings)
|
||||
{
|
||||
_baseDirPath = globalSettings.Attachment.BaseDirectory;
|
||||
_baseTempDirPath = $"{_baseDirPath}/temp";
|
||||
_baseAttachmentUrl = globalSettings.Attachment.BaseUrl;
|
||||
}
|
||||
|
||||
public async Task<string> GetAttachmentDownloadUrlAsync(Cipher cipher, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
await InitAsync();
|
||||
return $"{_baseAttachmentUrl}/{cipher.Id}/{attachmentData.AttachmentId}";
|
||||
}
|
||||
|
||||
public async Task UploadNewAttachmentAsync(Stream stream, Cipher cipher, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
await InitAsync();
|
||||
var cipherDirPath = CipherDirectoryPath(cipher.Id, temp: false);
|
||||
CreateDirectoryIfNotExists(cipherDirPath);
|
||||
|
||||
using (var fs = File.Create(AttachmentFilePath(cipherDirPath, attachmentData.AttachmentId)))
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
await stream.CopyToAsync(fs);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UploadShareAttachmentAsync(Stream stream, Guid cipherId, Guid organizationId, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
await InitAsync();
|
||||
var tempCipherOrgDirPath = OrganizationDirectoryPath(cipherId, organizationId, temp: true);
|
||||
CreateDirectoryIfNotExists(tempCipherOrgDirPath);
|
||||
|
||||
using (var fs = File.Create(AttachmentFilePath(tempCipherOrgDirPath, attachmentData.AttachmentId)))
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
await stream.CopyToAsync(fs);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartShareAttachmentAsync(Guid cipherId, Guid organizationId, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
await InitAsync();
|
||||
var sourceFilePath = AttachmentFilePath(attachmentData.AttachmentId, cipherId, organizationId, temp: true);
|
||||
if (!File.Exists(sourceFilePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var destFilePath = AttachmentFilePath(attachmentData.AttachmentId, cipherId, temp: false);
|
||||
if (!File.Exists(destFilePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var originalFilePath = AttachmentFilePath(attachmentData.AttachmentId, cipherId, temp: true);
|
||||
DeleteFileIfExists(originalFilePath);
|
||||
|
||||
File.Move(destFilePath, originalFilePath);
|
||||
DeleteFileIfExists(destFilePath);
|
||||
|
||||
File.Move(sourceFilePath, destFilePath);
|
||||
}
|
||||
|
||||
public async Task RollbackShareAttachmentAsync(Guid cipherId, Guid organizationId, CipherAttachment.MetaData attachmentData, string originalContainer)
|
||||
{
|
||||
await InitAsync();
|
||||
DeleteFileIfExists(AttachmentFilePath(attachmentData.AttachmentId, cipherId, organizationId, temp: true));
|
||||
|
||||
var originalFilePath = AttachmentFilePath(attachmentData.AttachmentId, cipherId, temp: true);
|
||||
if (!File.Exists(originalFilePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var destFilePath = AttachmentFilePath(attachmentData.AttachmentId, cipherId, temp: false);
|
||||
DeleteFileIfExists(destFilePath);
|
||||
|
||||
File.Move(originalFilePath, destFilePath);
|
||||
DeleteFileIfExists(originalFilePath);
|
||||
}
|
||||
|
||||
public async Task DeleteAttachmentAsync(Guid cipherId, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
await InitAsync();
|
||||
DeleteFileIfExists(AttachmentFilePath(attachmentData.AttachmentId, cipherId, temp: false));
|
||||
}
|
||||
|
||||
public async Task CleanupAsync(Guid cipherId)
|
||||
{
|
||||
await InitAsync();
|
||||
DeleteDirectoryIfExists(CipherDirectoryPath(cipherId, temp: true));
|
||||
}
|
||||
|
||||
public async Task DeleteAttachmentsForCipherAsync(Guid cipherId)
|
||||
{
|
||||
await InitAsync();
|
||||
DeleteDirectoryIfExists(CipherDirectoryPath(cipherId, temp: false));
|
||||
}
|
||||
|
||||
public async Task DeleteAttachmentsForOrganizationAsync(Guid organizationId)
|
||||
{
|
||||
await InitAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAttachmentsForUserAsync(Guid userId)
|
||||
{
|
||||
await InitAsync();
|
||||
}
|
||||
|
||||
private void DeleteFileIfExists(string path)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteDirectoryIfExists(string path)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
Directory.Delete(path, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateDirectoryIfNotExists(string path)
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
}
|
||||
|
||||
private Task InitAsync()
|
||||
{
|
||||
if (!Directory.Exists(_baseDirPath))
|
||||
{
|
||||
Directory.CreateDirectory(_baseDirPath);
|
||||
}
|
||||
|
||||
if (!Directory.Exists(_baseTempDirPath))
|
||||
{
|
||||
Directory.CreateDirectory(_baseTempDirPath);
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
private string CipherDirectoryPath(Guid cipherId, bool temp = false) =>
|
||||
Path.Combine(temp ? _baseTempDirPath : _baseDirPath, cipherId.ToString());
|
||||
private string OrganizationDirectoryPath(Guid cipherId, Guid organizationId, bool temp = false) =>
|
||||
Path.Combine(temp ? _baseTempDirPath : _baseDirPath, cipherId.ToString(), organizationId.ToString());
|
||||
|
||||
private string AttachmentFilePath(string dir, string attachmentId) => Path.Combine(dir, attachmentId);
|
||||
private string AttachmentFilePath(string attachmentId, Guid cipherId, Guid? organizationId = null,
|
||||
bool temp = false) =>
|
||||
organizationId.HasValue ?
|
||||
AttachmentFilePath(OrganizationDirectoryPath(cipherId, organizationId.Value, temp), attachmentId) :
|
||||
AttachmentFilePath(CipherDirectoryPath(cipherId, temp), attachmentId);
|
||||
public Task<string> GetAttachmentUploadUrlAsync(Cipher cipher, CipherAttachment.MetaData attachmentData)
|
||||
=> Task.FromResult($"{cipher.Id}/attachment/{attachmentData.AttachmentId}");
|
||||
|
||||
public Task<(bool, long?)> ValidateFileAsync(Cipher cipher, CipherAttachment.MetaData attachmentData, long leeway)
|
||||
{
|
||||
long? length = null;
|
||||
var path = AttachmentFilePath(attachmentData.AttachmentId, cipher.Id, temp: false);
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return Task.FromResult((false, length));
|
||||
}
|
||||
|
||||
length = new FileInfo(path).Length;
|
||||
if (attachmentData.Size < length - leeway || attachmentData.Size > length + leeway)
|
||||
{
|
||||
return Task.FromResult((false, length));
|
||||
}
|
||||
|
||||
return Task.FromResult((true, length));
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ using Bit.Core.Enums;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
@ -7,6 +7,7 @@ using Bit.Core.Models;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Azure.NotificationHubs;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
@ -3,6 +3,7 @@ using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
@ -6,6 +6,7 @@ using Bit.Core.Models;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
@ -9,6 +9,8 @@ using Bit.Core.Models.Business;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Bit.Core.Vault.Repositories;
|
||||
using Fido2NetLib;
|
||||
using Fido2NetLib.Objects;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
|
@ -1,69 +0,0 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
public class NoopAttachmentStorageService : IAttachmentStorageService
|
||||
{
|
||||
public FileUploadType FileUploadType => FileUploadType.Direct;
|
||||
|
||||
public Task CleanupAsync(Guid cipherId)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task DeleteAttachmentAsync(Guid cipherId, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task DeleteAttachmentsForCipherAsync(Guid cipherId)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task DeleteAttachmentsForOrganizationAsync(Guid organizationId)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task DeleteAttachmentsForUserAsync(Guid userId)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task RollbackShareAttachmentAsync(Guid cipherId, Guid organizationId, CipherAttachment.MetaData attachmentData, string originalContainer)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task StartShareAttachmentAsync(Guid cipherId, Guid organizationId, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task UploadNewAttachmentAsync(Stream stream, Cipher cipher, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task UploadShareAttachmentAsync(Stream stream, Guid cipherId, Guid organizationId, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<string> GetAttachmentDownloadUrlAsync(Cipher cipher, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
return Task.FromResult((string)null);
|
||||
}
|
||||
|
||||
public Task<string> GetAttachmentUploadUrlAsync(Cipher cipher, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
return Task.FromResult(default(string));
|
||||
}
|
||||
public Task<(bool, long?)> ValidateFileAsync(Cipher cipher, CipherAttachment.MetaData attachmentData, long leeway)
|
||||
{
|
||||
return Task.FromResult((false, (long?)null));
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
using Bit.Core.Entities.Provider;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Vault.Entities;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
|
Reference in New Issue
Block a user