ASC.Data.Storage moved from release/11 4414f77b

This commit is contained in:
pavelbannov 2020-09-15 18:16:37 +03:00
parent dd80e300d0
commit dc6b0d2c6c
20 changed files with 1443 additions and 107 deletions

View File

@ -167,7 +167,7 @@ namespace ASC.Core.ChunkedUploader
if (uploadSession.BytesTotal == uploadSession.BytesUploaded)
{
return new FileStream(uploadSession.ChunksBuffer, FileMode.Open, FileAccess.Read, FileShare.ReadWrite,
return new FileStream(uploadSession.ChunksBuffer, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite,
4096, FileOptions.DeleteOnClose);
}
}

View File

@ -76,6 +76,7 @@ namespace ASC.Data.Storage.Configuration
public bool Public { get; set; }
public bool DisableMigrate { get; set; }
public bool Count { get; set; } = true;
public bool DisabledEncryption { get; set; }
public IEnumerable<Module> Domain { get; set; }
}

View File

@ -32,6 +32,7 @@ using System.Linq;
using ASC.Common.Logging;
using ASC.Core;
using ASC.Data.Storage.Configuration;
using ASC.Data.Storage.Encryption;
using ASC.Security.Cryptography;
using Microsoft.AspNetCore.Http;
@ -62,10 +63,16 @@ namespace ASC.Data.Storage.DiscStorage
ToDictionary(x => x.Name,
y => y.Expires);
_domainsExpires.Add(string.Empty, moduleConfig.Expires);
var settings = moduleConfig.DisabledEncryption ? new EncryptionSettings() : EncryptionSettings.Load();
Crypt = EncryptionFactory.GetCrypt(moduleConfig.Name, settings);
return this;
}
public DiscDataStore(
ICrypt crypt,
TenantManager tenantManager,
PathUtils pathUtils,
EmailValidationKeyProvider emailValidationKeyProvider,
@ -73,6 +80,7 @@ namespace ASC.Data.Storage.DiscStorage
IOptionsMonitor<ILog> options)
: base(tenantManager, pathUtils, emailValidationKeyProvider, httpContextAccessor, options)
{
Crypt = crypt;
}
public string GetPhysicalPath(string domain, string path)
@ -93,13 +101,18 @@ namespace ASC.Data.Storage.DiscStorage
}
public override Stream GetReadStream(string domain, string path)
{
return GetReadStream(domain, path, true);
}
public Stream GetReadStream(string domain, string path, bool withDecription)
{
if (path == null) throw new ArgumentNullException("path");
var target = GetTarget(domain, path);
if (File.Exists(target))
{
return File.OpenRead(target);
return withDecription ? Crypt.GetReadStream(target) : File.OpenRead(target);
}
throw new FileNotFoundException("File not found", Path.GetFullPath(target));
}
@ -112,8 +125,8 @@ namespace ASC.Data.Storage.DiscStorage
if (File.Exists(target))
{
var stream = File.OpenRead(target);
if (0 < offset) stream.Seek(offset, SeekOrigin.Begin);
var stream = Crypt.GetReadStream(target);
if (0 < offset && stream.CanSeek) stream.Seek(offset, SeekOrigin.Begin);
return stream;
}
throw new FileNotFoundException("File not found", Path.GetFullPath(target));
@ -173,6 +186,8 @@ namespace ASC.Data.Storage.DiscStorage
QuotaUsedAdd(domain, fslen);
Crypt.EncryptFile(target);
return GetUri(domain, path);
}
@ -211,11 +226,21 @@ namespace ASC.Data.Storage.DiscStorage
public override Uri FinalizeChunkedUpload(string domain, string path, string uploadId, Dictionary<int, string> eTags)
{
var target = GetTarget(domain, path);
if (QuotaController != null)
{
var size = GetFileSize(domain, path);
if (!File.Exists(target))
{
throw new FileNotFoundException("file not found " + target);
}
var size = new FileInfo(target).Length;
QuotaUsedAdd(domain, size);
}
Crypt.EncryptFile(target);
return GetUri(domain, path);
}
@ -233,6 +258,8 @@ namespace ASC.Data.Storage.DiscStorage
get { return true; }
}
private ICrypt Crypt { get; set; }
#endregion
public override void Delete(string domain, string path)
@ -242,7 +269,7 @@ namespace ASC.Data.Storage.DiscStorage
if (File.Exists(target))
{
var size = new FileInfo(target).Length;
var size = Crypt.GetFileSize(target);
File.Delete(target);
QuotaUsedDelete(domain, size);
@ -264,7 +291,7 @@ namespace ASC.Data.Storage.DiscStorage
if (!File.Exists(target))
continue;
var size = new FileInfo(target).Length;
var size = Crypt.GetFileSize(target);
File.Delete(target);
QuotaUsedDelete(domain, size);
@ -282,7 +309,7 @@ namespace ASC.Data.Storage.DiscStorage
var entries = Directory.GetFiles(targetDir, pattern, recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
foreach (var entry in entries)
{
var size = new FileInfo(entry).Length;
var size = Crypt.GetFileSize(entry);
File.Delete(entry);
QuotaUsedDelete(domain, size);
}
@ -307,8 +334,9 @@ namespace ASC.Data.Storage.DiscStorage
var fileInfo = new FileInfo(entry);
if (fileInfo.LastWriteTime >= fromDate && fileInfo.LastWriteTime <= toDate)
{
var size = Crypt.GetFileSize(entry);
File.Delete(entry);
QuotaUsedDelete(domain, fileInfo.Length);
QuotaUsedDelete(domain, size);
}
}
}
@ -345,7 +373,7 @@ namespace ASC.Data.Storage.DiscStorage
Directory.CreateDirectory(Path.GetDirectoryName(newtarget));
}
var flength = new FileInfo(target).Length;
var flength = Crypt.GetFileSize(target);
//Delete file if exists
if (File.Exists(newtarget))
@ -394,7 +422,7 @@ namespace ASC.Data.Storage.DiscStorage
if (!Directory.Exists(targetDir)) return;
var entries = Directory.GetFiles(targetDir, "*.*", SearchOption.AllDirectories);
var size = entries.Select(entry => new FileInfo(entry)).Select(info => info.Length).Sum();
var size = entries.Select(entry => Crypt.GetFileSize(entry)).Sum();
var subDirs = Directory.GetDirectories(targetDir, "*", SearchOption.AllDirectories).ToList();
subDirs.Reverse();
@ -411,7 +439,7 @@ namespace ASC.Data.Storage.DiscStorage
if (File.Exists(target))
{
return new FileInfo(target).Length;
return Crypt.GetFileSize(target);
}
throw new FileNotFoundException("file not found " + target);
}
@ -423,8 +451,8 @@ namespace ASC.Data.Storage.DiscStorage
if (Directory.Exists(target))
{
return Directory.GetFiles(target, "*.*", SearchOption.AllDirectories)
.Select(entry => new FileInfo(entry))
.Sum(info => info.Length);
.Select(entry => Crypt.GetFileSize(entry))
.Sum();
}
throw new FileNotFoundException("directory not found " + target);
@ -459,9 +487,10 @@ namespace ASC.Data.Storage.DiscStorage
var finfo = new FileInfo(entry);
if ((DateTime.UtcNow - finfo.CreationTimeUtc) > oldThreshold)
{
var size = Crypt.GetFileSize(entry);
File.Delete(entry);
QuotaUsedDelete(domain, finfo.Length);
QuotaUsedDelete(domain, size);
}
}
}
@ -548,7 +577,7 @@ namespace ASC.Data.Storage.DiscStorage
if (Directory.Exists(target))
{
var entries = Directory.GetFiles(target, "*.*", SearchOption.AllDirectories);
size = entries.Select(entry => new FileInfo(entry)).Select(info => info.Length).Sum();
size = entries.Select(entry => Crypt.GetFileSize(entry)).Sum();
}
return size;
}
@ -569,7 +598,7 @@ namespace ASC.Data.Storage.DiscStorage
File.Copy(target, newtarget, true);
var flength = new FileInfo(target).Length;
var flength = Crypt.GetFileSize(target); ;
QuotaUsedAdd(newdomain, flength);
}
else
@ -601,8 +630,10 @@ namespace ASC.Data.Storage.DiscStorage
// Copy each file into it's new directory.
foreach (var fi in source.GetFiles())
{
fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true);
QuotaUsedAdd(newdomain, fi.Length);
var fp = Path.Combine(target.ToString(), fi.Name);
fi.CopyTo(fp, true);
var size = Crypt.GetFileSize(fp);
QuotaUsedAdd(newdomain, size);
}
// Copy each subdirectory using recursion.
@ -625,11 +656,16 @@ namespace ASC.Data.Storage.DiscStorage
}
public Stream GetWriteStream(string domain, string path)
{
return GetWriteStream(domain, path, FileMode.Create);
}
public Stream GetWriteStream(string domain, string path, FileMode fileMode)
{
if (path == null) throw new ArgumentNullException("path");
var target = GetTarget(domain, path);
CreateDirectory(target);
return File.Open(target, FileMode.Create);
return File.Open(target, fileMode);
}
private static void CreateDirectory(string target)
@ -659,5 +695,38 @@ namespace ASC.Data.Storage.DiscStorage
throw new ArgumentException("bad path");
}
}
public void Encrypt(string domain, string path)
{
if (path == null) throw new ArgumentNullException("path");
var target = GetTarget(domain, path);
if (File.Exists(target))
{
Crypt.EncryptFile(target);
}
else
{
throw new FileNotFoundException("file not found", target);
}
}
public void Decrypt(string domain, string path)
{
if (path == null) throw new ArgumentNullException("path");
var target = GetTarget(domain, path);
if (File.Exists(target))
{
Crypt.DecryptFile(target);
}
else
{
throw new FileNotFoundException("file not found", target);
}
}
}
}
}

View File

@ -0,0 +1,332 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
using System;
using System.IO;
using System.Security.Cryptography;
namespace ASC.Data.Storage.Encryption
{
public class Crypt : ICrypt
{
private readonly string storage;
private readonly EncryptionSettings settings;
private readonly string tempDir;
public Crypt(string storageName, EncryptionSettings encryptionSettings)
{
storage = storageName;
settings = encryptionSettings;
tempDir = ConfigurationManagerExtension.AppSettings["storage.encryption.tempdir"] ?? Path.GetTempPath();
}
public byte Version { get { return 1; } }
public void EncryptFile(string filePath)
{
var metadata = EncryptionFactory.GetMetadata();
metadata.Initialize(settings.Password);
using (var fileStream = File.OpenRead(filePath))
{
if (metadata.TryReadFromStream(fileStream, Version))
{
return;
}
}
EncryptFile(filePath, settings.Password);
}
public void DecryptFile(string filePath)
{
if (settings.Status == EncryprtionStatus.Decrypted)
{
return;
}
DecryptFile(filePath, settings.Password);
}
public Stream GetReadStream(string filePath)
{
if (settings.Status == EncryprtionStatus.Decrypted)
{
return File.OpenRead(filePath);
}
return GetReadStream(filePath, settings.Password);
}
public long GetFileSize(string filePath)
{
if (settings.Status == EncryprtionStatus.Decrypted)
{
return new FileInfo(filePath).Length;
}
return GetFileSize(filePath, settings.Password);
}
private void EncryptFile(string filePath, string password)
{
if (string.IsNullOrEmpty(password)) return;
var fileInfo = new FileInfo(filePath);
if (fileInfo.IsReadOnly)
{
fileInfo.IsReadOnly = false;
}
var ecryptedFilePath = GetUniqFileName(filePath, ".enc");
try
{
var metadata = EncryptionFactory.GetMetadata();
metadata.Initialize(Version, password, fileInfo.Length);
using (var ecryptedFileStream = new FileStream(ecryptedFilePath, FileMode.Create))
{
metadata.WriteToStream(ecryptedFileStream);
using (var algorithm = metadata.GetCryptographyAlgorithm())
{
using (var transform = algorithm.CreateEncryptor())
{
using (var cryptoStream = new CryptoStreamWrapper(ecryptedFileStream, transform, CryptoStreamMode.Write))
{
using (var fileStream = File.OpenRead(filePath))
{
fileStream.CopyTo(cryptoStream);
fileStream.Close();
}
cryptoStream.FlushFinalBlock();
metadata.ComputeAndWriteHmacHash(ecryptedFileStream);
cryptoStream.Close();
}
}
}
ecryptedFileStream.Close();
}
ReplaceFile(ecryptedFilePath, filePath);
}
catch (Exception exception)
{
if (File.Exists(ecryptedFilePath))
{
File.Delete(ecryptedFilePath);
}
throw exception;
}
}
private void DecryptFile(string filePath, string password)
{
var fileInfo = new FileInfo(filePath);
if (fileInfo.IsReadOnly)
{
fileInfo.IsReadOnly = false;
}
var decryptedFilePath = GetUniqFileName(filePath, ".dec");
try
{
var metadata = EncryptionFactory.GetMetadata();
metadata.Initialize(password);
using (var fileStream = File.OpenRead(filePath))
{
if (!metadata.TryReadFromStream(fileStream, Version)) return;
metadata.ComputeAndValidateHmacHash(fileStream);
using (var decryptedFileStream = new FileStream(decryptedFilePath, FileMode.Create))
{
using (var algorithm = metadata.GetCryptographyAlgorithm())
{
using (var transform = algorithm.CreateDecryptor())
{
using (var cryptoStream = new CryptoStreamWrapper(decryptedFileStream, transform, CryptoStreamMode.Write))
{
fileStream.CopyTo(cryptoStream);
cryptoStream.FlushFinalBlock();
cryptoStream.Close();
}
}
}
decryptedFileStream.Close();
}
fileStream.Close();
}
ReplaceFile(decryptedFilePath, filePath);
}
catch (Exception exception)
{
if (File.Exists(decryptedFilePath))
{
File.Delete(decryptedFilePath);
}
throw exception;
}
}
private Stream GetReadMemoryStream(string filePath, string password)
{
var decryptedMemoryStream = new MemoryStream(); //TODO: MemoryStream or temporary decrypted file on disk?
var metadata = EncryptionFactory.GetMetadata();
metadata.Initialize(password);
var fileStream = File.OpenRead(filePath);
if (!metadata.TryReadFromStream(fileStream, Version))
{
decryptedMemoryStream.Close();
fileStream.Seek(0, SeekOrigin.Begin);
return fileStream;
}
metadata.ComputeAndValidateHmacHash(fileStream);
using (var algorithm = metadata.GetCryptographyAlgorithm())
{
using (var transform = algorithm.CreateDecryptor())
{
using (var cryptoStream = new CryptoStreamWrapper(fileStream, transform, CryptoStreamMode.Read))
{
cryptoStream.CopyTo(decryptedMemoryStream);
cryptoStream.Close();
}
}
}
fileStream.Close();
decryptedMemoryStream.Seek(0, SeekOrigin.Begin);
return decryptedMemoryStream;
}
private Stream GetReadStream(string filePath, string password)
{
var metadata = EncryptionFactory.GetMetadata();
metadata.Initialize(password);
var fileStream = File.OpenRead(filePath);
if (!metadata.TryReadFromStream(fileStream, Version))
{
fileStream.Seek(0, SeekOrigin.Begin);
return fileStream;
}
metadata.ComputeAndValidateHmacHash(fileStream);
var wrapper = new StreamWrapper(fileStream, metadata);
return wrapper;
}
private long GetFileSize(string filePath, string password)
{
var metadata = EncryptionFactory.GetMetadata();
metadata.Initialize(password);
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, metadata.GetMetadataLength(), FileOptions.SequentialScan))
{
if (metadata.TryReadFromStream(fileStream, Version))
{
return metadata.GetFileSize();
}
else
{
return new FileInfo(filePath).Length;
}
}
}
private string GetUniqFileName(string filePath, string ext)
{
var dir = string.IsNullOrEmpty(tempDir) ? Path.GetDirectoryName(filePath) : tempDir;
var name = Path.GetFileNameWithoutExtension(filePath);
var result = Path.Combine(dir, string.Format("{0}_{1}{2}", storage, name, ext));
var index = 1;
while (File.Exists(result))
{
result = Path.Combine(dir, string.Format("{0}_{1}({2}){3}", storage, name, index++, ext));
}
return result;
}
private void ReplaceFile(string modifiedFilePath, string originalFilePath)
{
var tempFilePath = GetUniqFileName(originalFilePath, ".tmp");
File.Move(originalFilePath, tempFilePath);
try
{
File.Move(modifiedFilePath, originalFilePath);
}
catch (Exception exception)
{
File.Move(tempFilePath, originalFilePath);
throw exception;
}
finally
{
if (File.Exists(tempFilePath))
{
File.Delete(tempFilePath);
}
}
}
}
}

View File

@ -1,60 +1,59 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
namespace ASC.Data.Storage.Configuration
{
static class Schema
{
public const string SECTION_NAME = "storage";
public const string FILE_PATH = "file";
public const string APPENDERS = "appender";
public const string APPEND = "append";
public const string APPENDSECURE = "appendssl";
public const string EXTs = "exts";
public const string HANDLERS = "handler";
public const string PROPERTIES = "properties";
public const string PROPERTY = "property";
public const string MODULES = "module";
public const string TYPE = "type";
public const string NAME = "name";
public const string VALUE = "value";
public const string PATH = "path";
public const string DATA = "data";
public const string VIRTUALPATH = "virtualpath";
public const string VISIBLE = "visible";
public const string COUNT_QUOTA = "count";
public const string APPEND_TENANT_ID = "appendTenantId";
public const string ACL = "acl";
public const string EXPIRES = "expires";
public const string DOMAINS = "domain";
public const string PUBLIC = "public";
public const string DISABLEDMIGRATE = "disableMigrate";
}
}
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
using System.IO;
using System.Security.Cryptography;
namespace ASC.Data.Storage.Encryption
{
//https://stackoverflow.com/a/22072068
internal sealed class CryptoStreamWrapper : CryptoStream
{
private readonly Stream underlyingStream;
public CryptoStreamWrapper(Stream stream, ICryptoTransform transform, CryptoStreamMode mode)
: base(stream, transform, mode)
{
underlyingStream = stream;
}
protected override void Dispose(bool disposing)
{
try
{
base.Dispose(disposing);
}
catch (CryptographicException)
{
if (disposing)
{
underlyingStream.Dispose();
}
}
}
}
}

View File

@ -0,0 +1,36 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
namespace ASC.Data.Storage.Encryption
{
public enum EncryprtionStatus
{
Decrypted,
EncryptionStarted,
Encrypted,
DecryptionStarted
}
}

View File

@ -0,0 +1,41 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
namespace ASC.Data.Storage.Encryption
{
class EncryptionFactory
{
public static ICrypt GetCrypt(string storageName, EncryptionSettings encryptionSettings)
{
return new Crypt(storageName, encryptionSettings);
}
public static IMetadata GetMetadata()
{
return new Metadata();
}
}
}

View File

@ -0,0 +1,48 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
using ASC.Common.Module;
namespace ASC.Data.Storage.Encryption
{
public class EncryptionServiceClient : BaseWcfClient<IEncryptionService>, IEncryptionService
{
public void Start(EncryptionSettings encryptionSettings, string serverRootPath)
{
Channel.Start(encryptionSettings, serverRootPath);
}
public double GetProgress()
{
return Channel.GetProgress();
}
public void Stop()
{
Channel.Stop();
}
}
}

View File

@ -0,0 +1,178 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
using System;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using ASC.Common.Caching;
using ASC.Security.Cryptography;
namespace ASC.Data.Storage.Encryption
{
[Serializable]
[DataContract]
public class EncryptionSettings
{
private const string key = "EncryptionSettings";
private string password;
[DataMember]
public string Password
{
get { return password; }
set { password = (value ?? string.Empty).Replace('#', '_'); }
}
[DataMember]
public EncryprtionStatus Status { get; set; }
[DataMember]
public bool NotifyUsers { get; set; }
public EncryptionSettings()
{
Password = string.Empty;
Status = EncryprtionStatus.Decrypted;
NotifyUsers = true;
}
public string Serialize()
{
return string.Join("#",
string.IsNullOrEmpty(password) ? string.Empty : InstanceCrypto.Encrypt(password),
(int)Status,
NotifyUsers
);
}
public static EncryptionSettings Deserialize(string value)
{
if (string.IsNullOrEmpty(value))
{
return new EncryptionSettings();
}
var parts = value.Split(new[] { '#' }, StringSplitOptions.None);
var password = string.IsNullOrEmpty(parts[0]) ? string.Empty : InstanceCrypto.Decrypt(parts[0]);
var status = int.Parse(parts[1]);
var notifyUsers = bool.Parse(parts[2]);
return new EncryptionSettings
{
Password = password,
Status = (EncryprtionStatus)status,
NotifyUsers = notifyUsers
};
}
// source System.Web.Security.Membership.GeneratePassword
public static string GeneratePassword(int length, int numberOfNonAlphanumericCharacters)
{
var punctuations = "!@#$%^&*()_-+=[{]};:>|./?".ToCharArray();
if (length < 1 || length > 128)
{
throw new ArgumentException("password_length_incorrect", "length");
}
if (numberOfNonAlphanumericCharacters > length || numberOfNonAlphanumericCharacters < 0)
{
throw new ArgumentException("min_required_non_alphanumeric_characters_incorrect", "numberOfNonAlphanumericCharacters");
}
byte[] array = new byte[length];
char[] array2 = new char[length];
int num = 0;
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(array);
}
for (int i = 0; i < length; i++)
{
int num2 = (int)array[i] % 87;
if (num2 < 10)
{
array2[i] = (char)(48 + num2);
continue;
}
if (num2 < 36)
{
array2[i] = (char)(65 + num2 - 10);
continue;
}
if (num2 < 62)
{
array2[i] = (char)(97 + num2 - 36);
continue;
}
array2[i] = punctuations[num2 - 62];
num++;
}
if (num < numberOfNonAlphanumericCharacters)
{
Random random = new Random();
for (int j = 0; j < numberOfNonAlphanumericCharacters - num; j++)
{
int num3;
do
{
num3 = random.Next(0, length);
}
while (!char.IsLetterOrDigit(array2[num3]));
array2[num3] = punctuations[random.Next(0, punctuations.Length)];
}
}
return new string(array2);
}
public static EncryptionSettings Load()
{
var settings = CoreContext.Configuration.GetSetting(key);
return Deserialize(settings);
}
public void Save()
{
var settings = Serialize();
CoreContext.Configuration.SaveSetting(key, settings);
AscCache.ClearCache();
}
}
}

View File

@ -0,0 +1,34 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System.IO;
namespace ASC.Data.Storage.Encryption
{
public interface ICrypt
{
byte Version { get; }
void EncryptFile(string filePath);
void DecryptFile(string filePath);
Stream GetReadStream(string filePath);
long GetFileSize(string filePath);
}
}

View File

@ -0,0 +1,34 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System.ServiceModel;
namespace ASC.Data.Storage.Encryption
{
[ServiceContract]
public interface IEncryptionService
{
[OperationContract]
void Start(EncryptionSettings encryptionSettings, string serverRootPath);
[OperationContract]
double GetProgress();
[OperationContract]
void Stop();
}
}

View File

@ -0,0 +1,45 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System.IO;
using System.Security.Cryptography;
namespace ASC.Data.Storage.Encryption
{
public interface IMetadata
{
void Initialize(string password);
void Initialize(byte version, string password, long fileSize);
bool TryReadFromStream(Stream stream, byte cryptVersion);
void WriteToStream(Stream stream);
SymmetricAlgorithm GetCryptographyAlgorithm();
void ComputeAndWriteHmacHash(Stream stream);
void ComputeAndValidateHmacHash(Stream stream);
byte GetCryptoVersion();
long GetFileSize();
int GetMetadataLength();
}
}

View File

@ -0,0 +1,32 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
namespace ASC.Data.Storage.Encryption
{
public class IntegrityProtectionException : Exception
{
public IntegrityProtectionException()
{
}
public IntegrityProtectionException(string message) : base(message)
{
}
}
}

View File

@ -0,0 +1,301 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace ASC.Data.Storage.Encryption
{
public class Metadata : IMetadata
{
private const string prefixString = "AscEncrypted";
private const int prefixLength = 12; // prefixString length
private const int versionLength = 1; // byte
private const int sizeLength = 8; // long (int64)
private const int saltLength = 32; // The salt size must be 8 bytes or larger
private const int keySize = 256; // key size, in bits, of the secret key used for the symmetric algorithm. AES-256
private const int blockSize = 128; // block size, in bits, of the cryptographic operation. default is 128 bits
private const int keyLength = keySize / 8; // secret key used for the symmetric algorithm. 32 bytes
private const int ivLength = blockSize / 8; // The initialization vector (IV) to use for the symmetric algorithm. 16 bytes
private const int hmacKeyLength = 64; // HMACSHA256 64-byte private key is recommended.
private const int hmacHashLength = 32; // HMACSHA256 The output hash is 256 bits (32 bytes) in length
private const int metadataLength = prefixLength + versionLength + sizeLength + saltLength + hmacHashLength + ivLength;
private static int? iterations; // Rfc2898DeriveBytes: The minimum recommended number of iterations is 1000.
private int Iterations
{
get
{
if (iterations.HasValue)
{
return iterations.Value;
}
int iterationsCount;
if (!int.TryParse(ConfigurationManagerExtension.AppSettings["storage.encryption.iterations"], out iterationsCount))
{
iterationsCount = 4096;
}
iterations = iterationsCount;
return iterations.Value;
}
}
private string Password;
private byte[] Prefix;
private byte[] Version;
private byte[] Size;
private byte[] Salt;
private byte[] Key;
private byte[] IV;
private byte[] HmacKey;
private byte[] HmacHash;
public void Initialize(string password)
{
Password = password;
Prefix = Encoding.UTF8.GetBytes(prefixString);
Version = new byte[versionLength];
Size = new byte[sizeLength];
Salt = new byte[saltLength];
Key = new byte[keyLength];
IV = new byte[ivLength];
HmacKey = new byte[hmacKeyLength];
HmacHash = new byte[hmacHashLength];
}
public void Initialize(byte version, string password, long fileSize)
{
Password = password;
Prefix = Encoding.UTF8.GetBytes(prefixString);
Version = new byte[versionLength] { version };
Size = LongToByteArray(fileSize);
Salt = GenerateRandom(saltLength);
Key = GenerateKey();
IV = GenerateRandom(ivLength);
HmacKey = GenerateHmacKey();
HmacHash = new byte[hmacHashLength]; // Empty byte array. The real hmac will be computed after encryption
}
public bool TryReadFromStream(Stream stream, byte cryptVersion)
{
try
{
var readed = stream.Read(Prefix, 0, prefixLength);
if (readed < prefixLength) return false;
if (Encoding.UTF8.GetString(Prefix) != prefixString) return false;
readed = stream.Read(Version, 0, versionLength);
if (readed < versionLength) return false;
if (Version[0] != cryptVersion) return false;
readed = stream.Read(Size, 0, sizeLength);
if (readed < sizeLength) return false;
if (ByteArrayToLong(Size) < 0) return false;
readed = stream.Read(Salt, 0, saltLength);
if (readed < saltLength) return false;
readed = stream.Read(HmacHash, 0, hmacHashLength);
if (readed < hmacHashLength) return false;
readed = stream.Read(IV, 0, ivLength);
if (readed < ivLength) return false;
return true;
}
catch (Exception)
{
return false;
}
}
public void WriteToStream(Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
stream.Write(Prefix, 0, prefixLength);
stream.Write(Version, 0, versionLength);
stream.Write(Size, 0, sizeLength);
stream.Write(Salt, 0, saltLength);
stream.Write(HmacHash, 0, hmacHashLength);
stream.Write(IV, 0, ivLength);
}
public SymmetricAlgorithm GetCryptographyAlgorithm()
{
return new RijndaelManaged
{
KeySize = keySize,
BlockSize = blockSize,
Key = Key,
IV = IV,
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC
};
}
public void ComputeAndWriteHmacHash(Stream stream)
{
HmacHash = ComputeHmacHash(stream);
stream.Seek(metadataLength - ivLength - hmacHashLength, SeekOrigin.Begin); // Move position to hmac
stream.Write(HmacHash, 0, hmacHashLength); // Replace empty hmac with computed
}
public void ComputeAndValidateHmacHash(Stream stream)
{
Key = GenerateKey();
HmacKey = GenerateHmacKey();
var computedHash = ComputeHmacHash(stream);
if (!HmacHash.SequenceEqual(computedHash))
{
stream.Close();
throw new IntegrityProtectionException("Invalid signature");
}
stream.Seek(metadataLength, SeekOrigin.Begin); // Move position to encrypted data
}
public byte GetCryptoVersion()
{
return Version[0];
}
public long GetFileSize()
{
return ByteArrayToLong(Size);
}
public int GetMetadataLength()
{
return metadataLength;
}
private byte[] GenerateRandom(int length)
{
var random = new byte[length];
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(random);
}
return random;
}
private byte[] GenerateKey()
{
var key = new byte[keyLength];
using (var deriveBytes = new Rfc2898DeriveBytes(Password, Salt, Iterations, HashAlgorithmName.SHA256))
{
key = deriveBytes.GetBytes(keyLength);
}
return key;
}
private byte[] GenerateHmacKey()
{
var hmacKey = new byte[hmacKeyLength];
using (var sha512 = new SHA512Managed())
{
hmacKey = sha512.ComputeHash(Key);
}
return hmacKey;
}
private byte[] ComputeHmacHash(Stream stream)
{
var hmacHash = new byte[hmacHashLength];
stream.Seek(metadataLength - ivLength, SeekOrigin.Begin); // Move position to (IV + encrypted data)
using (var hmac = new HMACSHA256(HmacKey))
{
hmacHash = hmac.ComputeHash(stream); // IV needs to be part of the MAC calculation
}
return hmacHash;
}
private byte[] LongToByteArray(long value)
{
var result = BitConverter.GetBytes(value);
if (!BitConverter.IsLittleEndian)
Array.Reverse(result);
return result;
}
private long ByteArrayToLong(byte[] value)
{
if (!BitConverter.IsLittleEndian)
Array.Reverse(value);
try
{
return BitConverter.ToInt64(value, 0);
}
catch (Exception)
{
return -1;
}
}
}
}

View File

@ -0,0 +1,110 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.IO;
using System.Security.Cryptography;
namespace ASC.Data.Storage.Encryption
{
internal sealed class StreamWrapper : Stream
{
private readonly Stream stream;
private readonly CryptoStream cryptoStream;
private readonly SymmetricAlgorithm symmetricAlgorithm;
private readonly ICryptoTransform cryptoTransform;
private readonly long fileSize;
private readonly long metadataLength;
public StreamWrapper(Stream fileStream, IMetadata metadata)
{
stream = fileStream;
symmetricAlgorithm = metadata.GetCryptographyAlgorithm();
cryptoTransform = symmetricAlgorithm.CreateDecryptor();
cryptoStream = new CryptoStreamWrapper(stream, cryptoTransform, CryptoStreamMode.Read);
fileSize = metadata.GetFileSize();
metadataLength = metadata.GetMetadataLength();
}
public override bool CanRead
{
get { return stream.CanRead; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Length
{
get { return fileSize; }
}
public override long Position
{
get
{
return stream.Position - metadataLength;
}
set
{
if (value < 0 || value > fileSize)
throw new ArgumentOutOfRangeException();
stream.Position = value + metadataLength;
}
}
public override void Flush()
{
}
public override int Read(byte[] buffer, int offset, int count)
{
return cryptoStream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override void Close()
{
cryptoStream.Dispose();
stream.Dispose();
symmetricAlgorithm.Dispose();
cryptoTransform.Dispose();
}
}
}

View File

@ -1016,27 +1016,33 @@ namespace ASC.Data.Storage.S3
_recycleDir = props["recycleDir"];
}
if (props.ContainsKey("region"))
if (props.ContainsKey("region") && !string.IsNullOrEmpty(props["region"]))
{
_region = props["region"];
}
if (props.ContainsKey("serviceurl"))
if (props.ContainsKey("serviceurl") && !string.IsNullOrEmpty(props["serviceurl"]))
{
_serviceurl = props["serviceurl"];
}
if (props.ContainsKey("forcepathstyle"))
{
_forcepathstyle = bool.Parse(props["forcepathstyle"]);
if (bool.TryParse(props["forcepathstyle"], out var fps))
{
_forcepathstyle = fps;
}
}
if (props.ContainsKey("usehttp"))
{
_useHttp = bool.Parse(props["usehttp"]);
if (bool.TryParse(props["usehttp"], out var uh))
{
_useHttp = uh;
}
}
if (props.ContainsKey("sse"))
if (props.ContainsKey("sse") && !string.IsNullOrEmpty(props["sse"]))
{
switch (props["sse"].ToLower())
{

View File

@ -370,10 +370,12 @@ namespace ASC.Data.Backup.Service
using var scope = ServiceProvider.CreateScope();
var scopeClass = scope.ServiceProvider.GetService<BackupWorkerScope>();
var (tenantManager, backupStorageFactory, notifyHelper, backupRepository, backupWorker, backupPortalTask, _, _) = scopeClass;
var (tenantManager, backupStorageFactory, notifyHelper, backupRepository, backupWorker, backupPortalTask, _, _, coreBaseSettings) = scopeClass;
var tenant = tenantManager.GetTenant(TenantId);
var backupName = string.Format("{0}_{1:yyyy-MM-dd_HH-mm-ss}.{2}", tenant.TenantAlias, DateTime.UtcNow, ArchiveFormat);
var dateTime = coreBaseSettings.Standalone ? DateTime.Now : DateTime.UtcNow;
var backupName = string.Format("{0}_{1:yyyy-MM-dd_HH-mm-ss}.{2}", tenantManager.GetTenant(TenantId).TenantAlias, dateTime, ArchiveFormat);
var tempFile = Path.Combine(TempFolder, backupName);
var storagePath = tempFile;
try
@ -502,7 +504,7 @@ namespace ASC.Data.Backup.Service
{
using var scope = ServiceProvider.CreateScope();
var scopeClass = scope.ServiceProvider.GetService<BackupWorkerScope>();
var (tenantManager, backupStorageFactory, notifyHelper, _, backupWorker, _, restorePortalTask, _) = scopeClass;
var (tenantManager, backupStorageFactory, notifyHelper, _, backupWorker, _, restorePortalTask, _, _) = scopeClass;
Tenant tenant = null;
var tempFile = PathHelper.GetTempFileName(TempFolder);
try
@ -661,7 +663,7 @@ namespace ASC.Data.Backup.Service
{
using var scope = ServiceProvider.CreateScope();
var scopeClass = scope.ServiceProvider.GetService<BackupWorkerScope>();
var (tenantManager, _, notifyHelper, _, backupWorker, _, _, transferPortalTask) = scopeClass;
var (tenantManager, _, notifyHelper, _, backupWorker, _, _, transferPortalTask, _) = scopeClass;
var tempFile = PathHelper.GetTempFileName(TempFolder);
var tenant = tenantManager.GetTenant(TenantId);
var alias = tenant.TenantAlias;
@ -800,6 +802,7 @@ namespace ASC.Data.Backup.Service
private BackupPortalTask BackupPortalTask { get; }
private RestorePortalTask RestorePortalTask { get; }
private TransferPortalTask TransferPortalTask { get; }
public CoreBaseSettings CoreBaseSettings { get; }
public BackupWorkerScope(TenantManager tenantManager,
BackupStorageFactory backupStorageFactory,
@ -808,7 +811,8 @@ namespace ASC.Data.Backup.Service
BackupWorker backupWorker,
BackupPortalTask backupPortalTask,
RestorePortalTask restorePortalTask,
TransferPortalTask transferPortalTask)
TransferPortalTask transferPortalTask,
CoreBaseSettings coreBaseSettings)
{
TenantManager = tenantManager;
BackupStorageFactory = backupStorageFactory;
@ -818,6 +822,7 @@ namespace ASC.Data.Backup.Service
BackupPortalTask = backupPortalTask;
RestorePortalTask = restorePortalTask;
TransferPortalTask = transferPortalTask;
CoreBaseSettings = coreBaseSettings;
}
public void Deconstruct(out TenantManager tenantManager,
@ -827,7 +832,8 @@ namespace ASC.Data.Backup.Service
out BackupWorker backupWorker,
out BackupPortalTask backupPortalTask,
out RestorePortalTask restorePortalTask,
out TransferPortalTask transferPortalTask)
out TransferPortalTask transferPortalTask,
out CoreBaseSettings coreBaseSettings)
{
tenantManager = TenantManager;
backupStorageFactory = BackupStorageFactory;
@ -837,6 +843,7 @@ namespace ASC.Data.Backup.Service
backupPortalTask = BackupPortalTask;
restorePortalTask = RestorePortalTask;
transferPortalTask = TransferPortalTask;
coreBaseSettings = CoreBaseSettings;
}
}

View File

@ -261,7 +261,7 @@ namespace ASC.Data.Backup.Tasks.Modules
{
value = Regex.Replace(
Convert.ToString(value),
@"(?<=""message_id"":|/products/crm/httphandlers/filehandler\.ashx\?action=mailmessage&message_id=)\d+",
@"(?<=""message_id"":|/Products/CRM/HttpHandlers/filehandler\.ashx\?action=mailmessage&message_id=)\d+",
match =>
{
var mappedMessageId = Convert.ToString(columnMapper.GetMapping(relation.ParentTable, relation.ParentColumn, match.Value));

View File

@ -129,7 +129,7 @@ namespace ASC.Data.Backup.Tasks
return files.Distinct();
}
protected virtual bool IsStorageModuleAllowed(string storageModuleName)
protected bool IsStorageModuleAllowed(string storageModuleName)
{
var allowedStorageModules = new List<string>
{
@ -144,7 +144,9 @@ namespace ASC.Data.Backup.Tasks
"fckuploaders",
"talk",
"mailaggregator",
"whitelabel"
"whitelabel",
"customnavigation",
"userPhotos"
};
if (!allowedStorageModules.Contains(storageModuleName))

View File

@ -55,15 +55,26 @@ namespace ASC.Data.Backup.Tasks
public bool ReplaceDate { get; set; }
public bool Dump { get; set; }
private CoreBaseSettings CoreBaseSettings { get; set; }
private LicenseReader LicenseReader { get; set; }
private LicenseReader LicenseReader { get; set; }
public TenantManager TenantManager { get; }
private AscCacheNotify AscCacheNotify { get; set; }
private IOptionsMonitor<ILog> Options { get; set; }
public RestorePortalTask(DbFactory dbFactory, IOptionsMonitor<ILog> options, StorageFactory storageFactory, StorageFactoryConfig storageFactoryConfig, CoreBaseSettings coreBaseSettings, LicenseReader licenseReader, AscCacheNotify ascCacheNotify, ModuleProvider moduleProvider)
public RestorePortalTask(
DbFactory dbFactory,
IOptionsMonitor<ILog> options,
StorageFactory storageFactory,
StorageFactoryConfig storageFactoryConfig,
CoreBaseSettings coreBaseSettings,
LicenseReader licenseReader,
TenantManager tenantManager,
AscCacheNotify ascCacheNotify,
ModuleProvider moduleProvider)
: base(dbFactory, options, storageFactory, storageFactoryConfig, moduleProvider)
{
CoreBaseSettings = coreBaseSettings;
LicenseReader = licenseReader;
LicenseReader = licenseReader;
TenantManager = tenantManager;
AscCacheNotify = ascCacheNotify;
Options = options;
}
@ -119,7 +130,13 @@ namespace ASC.Data.Backup.Tasks
Logger.Debug("end restore data");
if (ProcessStorage)
{
{
if (CoreBaseSettings.Standalone)
{
Logger.Debug("clear cache");
AscCacheNotify.ClearCache();
}
DoRestoreStorage(dataReader);
}
if (UnblockPortalAfterCompleted)
@ -160,7 +177,23 @@ namespace ASC.Data.Backup.Tasks
var stepscount = keys.Count * 2 + upgrades.Count;
SetStepsCount(ProcessStorage ? stepscount + 1 : stepscount);
SetStepsCount(ProcessStorage ? stepscount + 1 : stepscount);
if (ProcessStorage)
{
var storageModules = StorageFactoryConfig.GetModuleList(ConfigPath).Where(IsStorageModuleAllowed);
var tenants = TenantManager.GetTenants(false);
stepscount += storageModules.Count() * tenants.Count;
SetStepsCount(stepscount + 1);
DoDeleteStorage(storageModules, tenants);
}
else
{
SetStepsCount(stepscount);
}
for (var i = 0; i < keys.Count; i += TasksLimit)
{
@ -284,6 +317,42 @@ namespace ASC.Data.Backup.Tasks
SetStepCompleted();
}
Logger.Debug("end restore storage");
}
private void DoDeleteStorage(IEnumerable<string> storageModules, IEnumerable<Tenant> tenants)
{
Logger.Debug("begin delete storage");
foreach (var tenant in tenants)
{
foreach (var module in storageModules)
{
var storage = StorageFactory.GetStorage(ConfigPath, tenant.TenantId.ToString(), module);
var domains = StorageFactoryConfig.GetDomainList(ConfigPath, module).ToList();
domains.Add(string.Empty); //instead storage.DeleteFiles("\\", "*.*", true);
foreach (var domain in domains)
{
ActionInvoker.Try(
state =>
{
if (storage.IsDirectory((string)state))
{
storage.DeleteFiles((string)state, "\\", "*.*", true);
}
},
domain,
5,
onFailure: error => Logger.WarnFormat("Can't delete files for domain {0}: \r\n{1}", domain, error)
);
}
SetStepCompleted();
}
}
Logger.Debug("end delete storage");
}
private IEnumerable<BackupFileInfo> GetFilesToProcess(IDataReadOperator dataReader)
@ -299,14 +368,6 @@ namespace ASC.Data.Backup.Tasks
}
}
protected override bool IsStorageModuleAllowed(string storageModuleName)
{
if (storageModuleName == "fckuploaders")
return false;
return base.IsStorageModuleAllowed(storageModuleName);
}
private void SetTenantActive(int tenantId)
{
using (var connection = DbFactory.OpenConnection())