ASC.Data.Storage moved from release/11 4414f77b
This commit is contained in:
parent
dd80e300d0
commit
dc6b0d2c6c
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
332
common/ASC.Data.Storage/Encryption/Crypt.cs
Normal file
332
common/ASC.Data.Storage/Encryption/Crypt.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
common/ASC.Data.Storage/Encryption/EncryprtionStatus.cs
Normal file
36
common/ASC.Data.Storage/Encryption/EncryprtionStatus.cs
Normal 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
|
||||
}
|
||||
}
|
41
common/ASC.Data.Storage/Encryption/EncryptionFactory.cs
Normal file
41
common/ASC.Data.Storage/Encryption/EncryptionFactory.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
178
common/ASC.Data.Storage/Encryption/EncryptionSettings.cs
Normal file
178
common/ASC.Data.Storage/Encryption/EncryptionSettings.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
34
common/ASC.Data.Storage/Encryption/ICrypt.cs
Normal file
34
common/ASC.Data.Storage/Encryption/ICrypt.cs
Normal 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);
|
||||
}
|
||||
}
|
34
common/ASC.Data.Storage/Encryption/IEncryptionService.cs
Normal file
34
common/ASC.Data.Storage/Encryption/IEncryptionService.cs
Normal 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();
|
||||
}
|
||||
}
|
45
common/ASC.Data.Storage/Encryption/IMetadata.cs
Normal file
45
common/ASC.Data.Storage/Encryption/IMetadata.cs
Normal 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();
|
||||
}
|
||||
}
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
301
common/ASC.Data.Storage/Encryption/Metadata.cs
Normal file
301
common/ASC.Data.Storage/Encryption/Metadata.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
110
common/ASC.Data.Storage/Encryption/StreamWrapper.cs
Normal file
110
common/ASC.Data.Storage/Encryption/StreamWrapper.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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())
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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))
|
||||
|
@ -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())
|
||||
|
Loading…
Reference in New Issue
Block a user