1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-22 12:15:36 +01:00

local attachment storage & docker image

This commit is contained in:
Kyle Spearrin 2017-08-08 17:27:01 -04:00
parent e50b6240e4
commit fecd5b3a1a
13 changed files with 260 additions and 7 deletions

8
attachments/Dockerfile Normal file
View File

@ -0,0 +1,8 @@
FROM node
RUN npm install http-server -g
EXPOSE 80
COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

7
attachments/build.ps1 Normal file
View File

@ -0,0 +1,7 @@
$dir = Split-Path -Parent $MyInvocation.MyCommand.Path
echo "`n# Building Attachments"
echo "`nBuilding docker image"
docker --version
docker build -t bitwarden/attachments $dir\.

10
attachments/build.sh Normal file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -e
DIR="$(dirname $(readlink -f $0))"
echo -e "\n# Building Attachments"
echo -e "\nBuilding docker image"
docker --version
docker build -t bitwarden/attachments $DIR/.

View File

@ -0,0 +1,4 @@
#!/bin/sh
http-server /etc/bitwarden/core/attachments/. -p 80 -d false --utc

View File

@ -7,6 +7,9 @@ services:
web:
volumes:
- /etc/bitwarden/web:/etc/bitwarden/web
attachments:
volumes:
- /etc/bitwarden/core/attachments:/etc/bitwarden/core/attachments
api:
volumes:
- /etc/bitwarden/core:/etc/bitwarden/core

View File

@ -7,6 +7,9 @@ services:
web:
volumes:
- c:/bitwarden/web:/etc/bitwarden/web
attachments:
volumes:
- c:/bitwarden/core/attachments:/etc/bitwarden/core/attachments
api:
volumes:
- c:/bitwarden/core:/etc/bitwarden/core

View File

@ -7,6 +7,9 @@ services:
web:
volumes:
- c:/bitwarden/web:/etc/bitwarden/web
attachments:
volumes:
- c:/bitwarden/core/attachments:/etc/bitwarden/core/attachments
api:
volumes:
- c:/bitwarden/core:/etc/bitwarden/core

View File

@ -15,6 +15,11 @@ services:
image: bitwarden/web
container_name: web
restart: always
attachments:
image: bitwarden/attachments
container_name: attachments
restart: always
api:
image: bitwarden/api

View File

@ -39,6 +39,7 @@
public class AttachmentSettings
{
public string ConnectionString { get; set; }
public string BaseDirectory { get; set; }
public string BaseUrl { get; set; }
}

View File

@ -0,0 +1,155 @@
using System.Threading.Tasks;
using System.IO;
using System;
using Bit.Core.Models.Table;
namespace Bit.Core.Services
{
public class LocalAttachmentStorageService : IAttachmentStorageService
{
private readonly string _baseDirPath;
private readonly string _baseTempDirPath;
public LocalAttachmentStorageService(
GlobalSettings globalSettings)
{
_baseDirPath = globalSettings.Attachment.BaseDirectory;
_baseTempDirPath = $"{_baseDirPath}/temp";
}
public async Task UploadNewAttachmentAsync(Stream stream, Cipher cipher, string attachmentId)
{
await InitAsync();
var cipherDirPath = $"{_baseDirPath}/{cipher.Id}";
CreateDirectoryIfNotExists(cipherDirPath);
using(var fs = File.Create($"{cipherDirPath}/{attachmentId}"))
{
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(fs);
}
}
public async Task UploadShareAttachmentAsync(Stream stream, Guid cipherId, Guid organizationId, string attachmentId)
{
await InitAsync();
var tempCipherOrgDirPath = $"{_baseTempDirPath}/{cipherId}/{organizationId}";
CreateDirectoryIfNotExists(tempCipherOrgDirPath);
using(var fs = File.Create($"{tempCipherOrgDirPath}/{attachmentId}"))
{
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(fs);
}
}
public async Task StartShareAttachmentAsync(Guid cipherId, Guid organizationId, string attachmentId)
{
await InitAsync();
var sourceFilePath = $"{_baseTempDirPath}/{cipherId}/{organizationId}/{attachmentId}";
if(!File.Exists(sourceFilePath))
{
return;
}
var destFilePath = $"{_baseDirPath}/{cipherId}/{attachmentId}";
if(!File.Exists(destFilePath))
{
return;
}
var originalFilePath = $"{_baseTempDirPath}/{cipherId}/{attachmentId}";
DeleteFileIfExists(originalFilePath);
File.Move(destFilePath, originalFilePath);
DeleteFileIfExists(destFilePath);
File.Move(sourceFilePath, destFilePath);
}
public async Task RollbackShareAttachmentAsync(Guid cipherId, Guid organizationId, string attachmentId)
{
await InitAsync();
DeleteFileIfExists($"{_baseTempDirPath}/{cipherId}/{organizationId}/{attachmentId}");
var originalFilePath = $"{_baseTempDirPath}/{cipherId}/{attachmentId}";
if(!File.Exists(originalFilePath))
{
return;
}
var destFilePath = $"{_baseDirPath}/{cipherId}/{attachmentId}";
DeleteFileIfExists(destFilePath);
File.Move(originalFilePath, destFilePath);
DeleteFileIfExists(originalFilePath);
}
public async Task DeleteAttachmentAsync(Guid cipherId, string attachmentId)
{
await InitAsync();
DeleteFileIfExists($"{_baseDirPath}/{cipherId}/{attachmentId}");
}
public async Task CleanupAsync(Guid cipherId)
{
await InitAsync();
DeleteDirectoryIfExists($"{_baseTempDirPath}/{cipherId}");
}
public async Task DeleteAttachmentsForCipherAsync(Guid cipherId)
{
await InitAsync();
DeleteDirectoryIfExists($"{_baseDirPath}/{cipherId}");
}
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);
}
}
}

View File

@ -268,5 +268,24 @@ namespace Bit.Core.Utilities
{
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
}
public static bool FullFramework()
{
#if NET461
return true;
#else
return false;
#endif
}
public static bool SettingHasValue(string setting)
{
if(string.IsNullOrWhiteSpace(setting) || setting.Equals("SECRET"))
{
return false;
}
return true;
}
}
}

View File

@ -15,7 +15,9 @@ using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
#if NET461
using Microsoft.WindowsAzure.Storage;
#endif
using System;
using System.IO;
using SqlServerRepos = Bit.Core.Repositories.SqlServer;
@ -53,23 +55,38 @@ namespace Bit.Core.Utilities
{
services.AddSingleton<IMailService, RazorViewMailService>();
if(!string.IsNullOrWhiteSpace(globalSettings.Mail.SendGridApiKey))
if(CoreHelpers.SettingHasValue(globalSettings.Mail.SendGridApiKey))
{
services.AddSingleton<IMailDeliveryService, SendGridMailDeliveryService>();
}
else if(CoreHelpers.SettingHasValue(globalSettings.Mail?.Smtp?.Host) &&
CoreHelpers.SettingHasValue(globalSettings.Mail?.Smtp?.Username) &&
CoreHelpers.SettingHasValue(globalSettings.Mail?.Smtp?.Password))
{
services.AddSingleton<IMailDeliveryService, SmtpMailDeliveryService>();
}
else
{
services.AddSingleton<IMailDeliveryService, NoopMailDeliveryService>();
}
#if NET461
services.AddSingleton<IPushNotificationService, NotificationHubPushNotificationService>();
services.AddSingleton<IPushRegistrationService, NotificationHubPushRegistrationService>();
if(globalSettings.SelfHosted)
{
services.AddSingleton<IPushNotificationService, NoopPushNotificationService>();
services.AddSingleton<IPushRegistrationService, NoopPushRegistrationService>();
}
else
{
services.AddSingleton<IPushNotificationService, NotificationHubPushNotificationService>();
services.AddSingleton<IPushRegistrationService, NotificationHubPushRegistrationService>();
}
#else
services.AddSingleton<IPushNotificationService, NoopPushNotificationService>();
services.AddSingleton<IPushRegistrationService, NoopPushRegistrationService>();
#endif
if(!string.IsNullOrWhiteSpace(globalSettings.Storage.ConnectionString))
if(CoreHelpers.SettingHasValue(globalSettings.Storage.ConnectionString))
{
services.AddSingleton<IBlockIpService, AzureQueueBlockIpService>();
}
@ -78,10 +95,14 @@ namespace Bit.Core.Utilities
services.AddSingleton<IBlockIpService, NoopBlockIpService>();
}
if(!string.IsNullOrWhiteSpace(globalSettings.Attachment.ConnectionString))
if(CoreHelpers.SettingHasValue(globalSettings.Attachment.ConnectionString))
{
services.AddSingleton<IAttachmentStorageService, AzureAttachmentStorageService>();
}
else if(CoreHelpers.SettingHasValue(globalSettings.Attachment.BaseDirectory))
{
services.AddSingleton<IAttachmentStorageService, LocalAttachmentStorageService>();
}
else
{
services.AddSingleton<IAttachmentStorageService, NoopAttachmentStorageService>();
@ -169,8 +190,8 @@ namespace Bit.Core.Utilities
{
identityServerBuilder.AddTemporarySigningCredential();
}
else if(!string.IsNullOrWhiteSpace(globalSettings.IdentityServer.CertificatePassword) &&
System.IO.File.Exists("identity.pfx"))
else if(!string.IsNullOrWhiteSpace(globalSettings.IdentityServer.CertificatePassword)
&& File.Exists("identity.pfx"))
{
var identityServerCert = CoreHelpers.GetCertificate("identity.pfx",
globalSettings.IdentityServer.CertificatePassword);

View File

@ -204,6 +204,16 @@ server {{
proxy_redirect off;
}}
location /attachments/ {{
proxy_pass http://attachments/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Url-Scheme $scheme;
proxy_redirect off;
}}
location /api/ {{
proxy_pass http://api/;
proxy_set_header X-Real-IP $remote_addr;
@ -243,6 +253,8 @@ globalSettings:baseServiceUri:api={_url}/api
globalSettings:baseServiceUri:identity={_url}/identity
globalSettings:sqlServer:connectionString={dbConnectionString}
globalSettings:identityServer:certificatePassword={_identityCertPassword}
globalSettings:attachment:baseDirectory=/etc/bitwarden/core/attachments
globalSettings:attachment:baseUrl={_url}/attachments
globalSettings:duo:aKey={Helpers.SecureRandomString(32, alpha: true, numeric: true)}
globalSettings:yubico:clientId=REPLACE
globalSettings:yubico:REPLACE");
@ -265,6 +277,8 @@ SA_PASSWORD={dbPass}");
sw.Write($@"var bitwardenAppSettings = {{
apiUri: ""{_url}/api"",
identityUri: ""{_url}/identity"",
stripeKey: null,
braintreeKey: null,
whitelistDomains: [""{_domain}""]
}};");
}