Merge branch 'master' into feature/release-11.0.0

# Conflicts:
#	common/ASC.Core.Common/Context/WorkContext.cs
#	common/ASC.Core.Common/Tenants/TenantControlPanelSettings.cs
#	common/ASC.Core.Common/Tenants/TenantQuota.cs
#	common/ASC.Data.Storage/DiscStorage/DiscDataStore.cs
#	common/ASC.Data.Storage/Encryption/Crypt.cs
#	common/ASC.Data.Storage/Encryption/CryptoStreamWrapper.cs
#	common/ASC.Data.Storage/Encryption/EncryptionFactory.cs
#	common/ASC.Data.Storage/Encryption/EncryptionServiceClient.cs
#	common/ASC.Data.Storage/Encryption/EncryptionSettings.cs
#	common/ASC.Data.Storage/Encryption/ICrypt.cs
#	common/ASC.Data.Storage/Encryption/IEncryptionService.cs
#	common/ASC.Data.Storage/Encryption/IMetadata.cs
#	common/ASC.Data.Storage/Encryption/IntegrityProtectionException.cs
#	common/ASC.Data.Storage/Encryption/Metadata.cs
#	common/ASC.Data.Storage/Encryption/StreamWrapper.cs
#	config/storage.json
#	web/ASC.Web.Api/Controllers/SettingsController.cs
#	web/ASC.Web.Core/PublicResources/webstudio_patterns.xml
This commit is contained in:
pavelbannov 2020-09-20 20:17:12 +03:00
commit 0000129b3b
54 changed files with 2757 additions and 1174 deletions

View File

@ -64,6 +64,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Socket.IO.Svc", "common
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Data.Storage.Migration", "common\services\ASC.Data.Storage.Migration\ASC.Data.Storage.Migration.csproj", "{02356BD7-7E99-457B-BEFF-090CE4DF067D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Data.Storage.Encryption", "common\services\ASC.Data.Storage.Encryption\ASC.Data.Storage.Encryption.csproj", "{17A05AE2-21C8-4C1C-B422-6AB80F13F63E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -186,6 +188,10 @@ Global
{02356BD7-7E99-457B-BEFF-090CE4DF067D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{02356BD7-7E99-457B-BEFF-090CE4DF067D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{02356BD7-7E99-457B-BEFF-090CE4DF067D}.Release|Any CPU.Build.0 = Release|Any CPU
{17A05AE2-21C8-4C1C-B422-6AB80F13F63E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17A05AE2-21C8-4C1C-B422-6AB80F13F63E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17A05AE2-21C8-4C1C-B422-6AB80F13F63E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17A05AE2-21C8-4C1C-B422-6AB80F13F63E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,2 @@
echo "RUN ASC.Data.Storage.Encryption"
call dotnet run --project ..\..\common\services\ASC.Data.Storage.Encryption\ASC.Data.Storage.Encryption.csproj --no-build --$STORAGE_ROOT=..\..\..\Data --log__dir=..\..\..\Logs --log__name=encryption

View File

@ -31,6 +31,7 @@
<None Remove="protos\AzRecordCache.proto" />
<None Remove="protos\ConsumerCacheItem.proto" />
<None Remove="protos\GroupCacheItem.proto" />
<None Remove="protos\NotifyInvoke.proto" />
<None Remove="protos\NotifyMessage.proto" />
<None Remove="protos\QuotaCacheItem.proto" />
<None Remove="protos\SettingsCacheItem.proto" />
@ -58,6 +59,7 @@
<PackageReference Include="System.Text.Json" Version="4.7.2" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="protos\NotifyInvoke.proto" />
<Protobuf Include="protos\NotifyMessage.proto" />
<Protobuf Include="protos\SettingsCacheItem.proto" />
<Protobuf Include="protos\ConsumerCacheItem.proto" />

View File

@ -102,13 +102,14 @@ namespace ASC.Core
var configuration = serviceProvider.GetService<IConfiguration>();
var cacheNotify = serviceProvider.GetService<ICacheNotify<NotifyMessage>>();
var cacheInvoke = serviceProvider.GetService<ICacheNotify<NotifyInvoke>>();
var options = serviceProvider.GetService<IOptionsMonitor<ILog>>();
var telegramHelper = serviceProvider.GetService<TelegramHelper>();
NotifyContext = new NotifyContext(serviceProvider);
INotifySender jabberSender = new NotifyServiceSender(cacheNotify);
INotifySender emailSender = new NotifyServiceSender(cacheNotify);
INotifySender jabberSender = new NotifyServiceSender(cacheNotify, cacheInvoke);
INotifySender emailSender = new NotifyServiceSender(cacheNotify, cacheInvoke);
INotifySender telegramSender = new TelegramSender(options, telegramHelper);
var postman = configuration["core:notify:postman"];

View File

@ -32,6 +32,6 @@ namespace ASC.Notify
{
void SendNotifyMessage(NotifyMessage notifyMessage);
void InvokeSendMethod(string service, string method, int tenant, params object[] parameters);
void InvokeSendMethod(NotifyInvoke notifyInvoke);
}
}

View File

@ -32,21 +32,22 @@ namespace ASC.Core.Notify
{
public class NotifyServiceClient : INotifyService
{
private readonly ICacheNotify<NotifyMessage> cacheNotify;
public NotifyServiceClient(ICacheNotify<NotifyMessage> cacheNotify)
private ICacheNotify<NotifyMessage> СacheNotify;
private ICacheNotify<NotifyInvoke> NotifyInvoke;
public NotifyServiceClient(ICacheNotify<NotifyMessage> cacheNotify, ICacheNotify<NotifyInvoke> notifyInvoke)
{
this.cacheNotify = cacheNotify;
СacheNotify = cacheNotify;
NotifyInvoke = notifyInvoke;
}
public void SendNotifyMessage(NotifyMessage m)
{
cacheNotify.Publish(m, CacheNotifyAction.InsertOrUpdate);
СacheNotify.Publish(m, CacheNotifyAction.InsertOrUpdate);
}
public void InvokeSendMethod(string service, string method, int tenant, params object[] parameters)
public void InvokeSendMethod(NotifyInvoke notifyInvoke)
{
//TODO
//Channel.InvokeSendMethod(service, method, tenant, parameters);
NotifyInvoke.Publish(notifyInvoke, CacheNotifyAction.InsertOrUpdate);
}
}
}

View File

@ -34,9 +34,9 @@ namespace ASC.Core.Notify.Senders
public class NotifyServiceSender : INotifySender
{
public NotifyServiceClient NotifyServiceClient { get; }
public NotifyServiceSender(ICacheNotify<NotifyMessage> cacheNotify)
public NotifyServiceSender(ICacheNotify<NotifyMessage> cacheNotify, ICacheNotify<NotifyInvoke> notifyInvoke)
{
NotifyServiceClient = new NotifyServiceClient(cacheNotify);
NotifyServiceClient = new NotifyServiceClient(cacheNotify, notifyInvoke);
}
public void Init(IDictionary<string, string> properties)

View File

@ -1,54 +1,54 @@
/*
*
* (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 ASC.Core.Common.Settings;
namespace ASC.Core.Tenants
{
[Serializable]
[DataContract]
public class TenantControlPanelSettings : ISettings
{
[DataMember(Name = "LimitedAccess")]
public bool LimitedAccess { get; set; }
public Guid ID
{
get { return new Guid("{880585C4-52CD-4AE2-8DA4-3B8E2772753B}"); }
}
public ISettings GetDefault(IServiceProvider serviceProvider)
{
return new TenantControlPanelSettings
{
LimitedAccess = false
};
}
}
/*
*
* (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 ASC.Core.Common.Settings;
namespace ASC.Core.Tenants
{
[Serializable]
[DataContract]
public class TenantControlPanelSettings : ISettings
{
[DataMember(Name = "LimitedAccess")]
public bool LimitedAccess { get; set; }
public Guid ID
{
get { return new Guid("{880585C4-52CD-4AE2-8DA4-3B8E2772753B}"); }
}
public ISettings GetDefault(IServiceProvider serviceProvider)
{
return new TenantControlPanelSettings
{
LimitedAccess = false
};
}
}
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
package ASC.Notify.Messages;
message NotifyInvoke {
string Service = 1;
string Method = 2;
int32 Tenant = 3;
repeated string Parameters = 4;
}

View File

@ -20,9 +20,12 @@
</ItemGroup>
<ItemGroup>
<None Remove="protos\EncryptionSettingsProto.proto" />
<None Remove="protos\EncryptionStop.proto" />
<None Remove="protos\MigrationCache.proto" />
<None Remove="protos\MigrationProgress.proto" />
<None Remove="protos\MigrationUploadCdn.proto" />
<None Remove="protos\ProgressEncryption.proto" />
</ItemGroup>
<ItemGroup>
@ -48,9 +51,12 @@
<ItemGroup>
<Protobuf Include="protos\DataStoreCacheItem.proto" />
<Protobuf Include="protos\EncryptionSettingsProto.proto" />
<Protobuf Include="protos\EncryptionStop.proto" />
<Protobuf Include="protos\MigrationCache.proto" />
<Protobuf Include="protos\MigrationProgress.proto" />
<Protobuf Include="protos\MigrationUploadCdn.proto" />
<Protobuf Include="protos\ProgressEncryption.proto" />
</ItemGroup>
</Project>

View File

@ -1,6 +1,6 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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).
@ -43,6 +43,9 @@ namespace ASC.Data.Storage.DiscStorage
public class DiscDataStore : BaseStorage
{
private readonly Dictionary<string, MappedPath> _mappedPaths = new Dictionary<string, MappedPath>();
private ICrypt Crypt { get; set; }
private EncryptionSettingsHelper EncryptionSettingsHelper { get; set; }
private EncryptionFactory EncryptionFactory { get; set; }
public override IDataStore Configure(string tenant, Handler handlerConfig, Module moduleConfig, IDictionary<string, string> props)
{
@ -63,24 +66,24 @@ 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();
var settings = moduleConfig.DisabledEncryption ? new EncryptionSettings() : EncryptionSettingsHelper.Load();
Crypt = EncryptionFactory.GetCrypt(moduleConfig.Name, settings);
return this;
}
public DiscDataStore(
ICrypt crypt,
TenantManager tenantManager,
PathUtils pathUtils,
EmailValidationKeyProvider emailValidationKeyProvider,
IHttpContextAccessor httpContextAccessor,
IOptionsMonitor<ILog> options)
IOptionsMonitor<ILog> options,
EncryptionSettingsHelper encryptionSettingsHelper,
EncryptionFactory encryptionFactory)
: base(tenantManager, pathUtils, emailValidationKeyProvider, httpContextAccessor, options)
{
Crypt = crypt;
EncryptionSettingsHelper = encryptionSettingsHelper;
EncryptionFactory = encryptionFactory;
}
public string GetPhysicalPath(string domain, string path)
@ -235,7 +238,7 @@ namespace ASC.Data.Storage.DiscStorage
throw new FileNotFoundException("file not found " + target);
}
var size = new FileInfo(target).Length;
var size = Crypt.GetFileSize(target);
QuotaUsedAdd(domain, size);
}
@ -258,8 +261,6 @@ namespace ASC.Data.Storage.DiscStorage
get { return true; }
}
private ICrypt Crypt { get; set; }
#endregion
public override void Delete(string domain, string path)
@ -598,7 +599,7 @@ namespace ASC.Data.Storage.DiscStorage
File.Copy(target, newtarget, true);
var flength = Crypt.GetFileSize(target); ;
var flength = Crypt.GetFileSize(target);
QuotaUsedAdd(newdomain, flength);
}
else
@ -728,5 +729,4 @@ namespace ASC.Data.Storage.DiscStorage
}
}
}
}
}

View File

@ -1,332 +1,343 @@
/*
*
* (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);
}
}
}
}
}
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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;
using Microsoft.Extensions.Configuration;
namespace ASC.Data.Storage.Encryption
{
public class Crypt : ICrypt
{
private string Storage { get; set; }
private EncryptionSettings Settings { get; set; }
private string TempDir { get; set; }
private IConfiguration Configuration { get; set; }
private EncryptionFactory EncryptionFactory { get; set; }
public void Init(string storageName, EncryptionSettings encryptionSettings)
{
Storage = storageName;
Settings = encryptionSettings;
TempDir = Configuration["storage:encryption:tempdir"] ?? Path.GetTempPath();
}
public Crypt(IConfiguration configuration, EncryptionFactory encryptionFactory)
{
Configuration = configuration;
EncryptionFactory = encryptionFactory;
}
public byte Version { get { return 1; } }
public void EncryptFile(string filePath)
{
if (string.IsNullOrEmpty(Settings.Password)) return;
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)
{
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,6 +1,6 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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).

View File

@ -1,6 +1,6 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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).
@ -24,18 +24,44 @@
*/
using System;
using ASC.Common;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Data.Storage.Encryption
{
class EncryptionFactory
public class EncryptionFactory
{
public static ICrypt GetCrypt(string storageName, EncryptionSettings encryptionSettings)
private IServiceProvider ServiceProvider { get; }
public EncryptionFactory(IServiceProvider serviceProvider)
{
return new Crypt(storageName, encryptionSettings);
ServiceProvider = serviceProvider;
}
public static IMetadata GetMetadata()
public ICrypt GetCrypt(string storageName, EncryptionSettings encryptionSettings)
{
return new Metadata();
var crypt = ServiceProvider.GetService<Crypt>();
crypt.Init(storageName, encryptionSettings);
return crypt;
}
public IMetadata GetMetadata()
{
return ServiceProvider.GetService<Metadata>();
}
}
public static class EncryptionFactoryExtension
{
public static DIHelper AddEncryptionFactoryService(this DIHelper services)
{
services.TryAddSingleton<EncryptionFactory>();
services.TryAddTransient<Crypt>();
services.TryAddTransient<Metadata>();
return services;
}
}
}

View File

@ -1,48 +1,65 @@
/*
*
* (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();
}
}
}
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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;
using ASC.Common.Caching;
namespace ASC.Data.Storage.Encryption
{
public class EncryptionServiceClient : IEncryptionService
{
private ICacheNotify<EncryptionSettingsProto> NotifySetting { get; }
private ICacheNotify<EncryptionStop> NotifyStop { get; }
public EncryptionServiceClient(
ICacheNotify<EncryptionSettingsProto> notifySetting, ICacheNotify<EncryptionStop> notifyStop)
{
NotifySetting = notifySetting;
NotifyStop = notifyStop;
}
public void Start(EncryptionSettingsProto encryptionSettingsProto)
{
NotifySetting.Publish(encryptionSettingsProto, CacheNotifyAction.Insert);
}
public void Stop()
{
NotifyStop.Publish(new EncryptionStop(), CacheNotifyAction.Insert);
}
}
public static class EncryptionServiceClientExtension
{
public static DIHelper AddEncryptionServiceClient(this DIHelper services)
{
services.TryAddScoped<EncryptionServiceClient>();
return services;
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using ASC.Common.Caching;
namespace ASC.Data.Storage.Encryption
{
public class EncryptionServiceNotifier
{
private ICacheNotify<ProgressEncryption> СacheBackupProgress { get; }
private ICache Cache { get; }
public EncryptionServiceNotifier(ICacheNotify<ProgressEncryption> сacheBackupProgress)
{
Cache = AscCache.Memory;
СacheBackupProgress = сacheBackupProgress;
СacheBackupProgress.Subscribe((a) =>
{
Cache.Insert(GetCacheKey(a.TenantId), a, DateTime.UtcNow.AddDays(1));
},
CacheNotifyAction.InsertOrUpdate);
}
public ProgressEncryption GetEncryptionProgress(int tenantId)
{
return Cache.Get<ProgressEncryption>(GetCacheKey(tenantId));
}
private string GetCacheKey(int tenantId) => $"encryption{tenantId}";
}
}

View File

@ -1,178 +1,205 @@
/*
*
* (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();
}
}
}
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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.Security.Cryptography;
using ASC.Common;
using ASC.Common.Caching;
using ASC.Core;
using ASC.Security.Cryptography;
namespace ASC.Data.Storage.Encryption
{
public class EncryptionSettings
{
internal string password;
public string Password
{
get { return password; }
set { password = (value ?? string.Empty).Replace('#', '_'); }
}
public EncryprtionStatus Status { get; set; }
public bool NotifyUsers { get; set; }
public EncryptionSettings(EncryptionSettingsProto encryptionSettingsProto)
{
Password = encryptionSettingsProto.Password;
Status = encryptionSettingsProto.Status;
NotifyUsers = encryptionSettingsProto.NotifyUsers;
}
public EncryptionSettings()
{
Password = string.Empty;
Status = EncryprtionStatus.Decrypted;
NotifyUsers = true;
}
}
public class EncryptionSettingsHelper
{
private const string key = "EncryptionSettings";
private CoreConfiguration CoreConfiguration { get; }
private AscCacheNotify AscCacheNotify { get; }
private InstanceCrypto InstanceCrypto { get; }
public EncryptionSettingsHelper(CoreConfiguration coreConfiguration, AscCacheNotify ascCacheNotify, InstanceCrypto instanceCrypto)
{
CoreConfiguration = coreConfiguration;
AscCacheNotify = ascCacheNotify;
InstanceCrypto = instanceCrypto;
}
public void Save(EncryptionSettings encryptionSettings)
{
var settings = Serialize(encryptionSettings);
CoreConfiguration.SaveSetting(key, settings);
AscCacheNotify.ClearCache();
}
public EncryptionSettings Load()
{
var settings = CoreConfiguration.GetSetting(key);
return Deserialize(settings);
}
public string Serialize(EncryptionSettings encryptionSettings)
{
return string.Join("#",
string.IsNullOrEmpty(encryptionSettings.password) ? string.Empty : InstanceCrypto.Encrypt(encryptionSettings.password),
(int)encryptionSettings.Status,
encryptionSettings.NotifyUsers
);
}
public 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 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 class EncryptionSettingsHelperExtension
{
public static DIHelper AddEncryptionSettingsHelperService(this DIHelper services)
{
services.TryAddScoped<EncryptionSettingsHelper>();
services.TryAddSingleton<AscCacheNotify>();
return services
.AddCoreConfigurationService();
}
}
}

View File

@ -1,16 +1,25 @@
/*
*
* (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.
*
* 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.
*
*/

View File

@ -1,34 +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();
}
}
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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 interface IEncryptionService
{
void Start(EncryptionSettingsProto encryptionSettingsProto);
void Stop();
}
}

View File

@ -1,16 +1,25 @@
/*
*
* (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.
*
* 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.
*
*/

View File

@ -1,16 +1,25 @@
/*
*
* (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.
*
* 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.
*
*/

View File

@ -1,301 +1,319 @@
/*
*
* (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;
}
}
}
}
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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.Linq;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Configuration;
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 IConfiguration Configuration { get; set; }
public Metadata(IConfiguration configuration)
{
Configuration = configuration;
}
private int Iterations
{
get
{
if (iterations.HasValue)
{
return iterations.Value;
}
int iterationsCount;
if (!int.TryParse(Configuration["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

@ -1,110 +1,119 @@
/*
*
* (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();
}
}
}
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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
{
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

@ -36,6 +36,7 @@ using ASC.Core.Common.Configuration;
using ASC.Core.Common.Settings;
using ASC.Data.Storage.Configuration;
using ASC.Data.Storage.DiscStorage;
using ASC.Data.Storage.Encryption;
using ASC.Security.Cryptography;
using Microsoft.AspNetCore.Http;
@ -168,6 +169,8 @@ namespace ASC.Data.Storage
private EmailValidationKeyProvider EmailValidationKeyProvider { get; }
private IOptionsMonitor<ILog> Options { get; }
private IHttpContextAccessor HttpContextAccessor { get; }
private EncryptionSettingsHelper EncryptionSettingsHelper { get; }
private EncryptionFactory EncryptionFactory { get; }
public StorageFactory(
StorageFactoryListener storageFactoryListener,
@ -178,8 +181,10 @@ namespace ASC.Data.Storage
CoreBaseSettings coreBaseSettings,
PathUtils pathUtils,
EmailValidationKeyProvider emailValidationKeyProvider,
IOptionsMonitor<ILog> options) :
this(storageFactoryListener, storageFactoryConfig, settingsManager, storageSettingsHelper, tenantManager, coreBaseSettings, pathUtils, emailValidationKeyProvider, options, null)
IOptionsMonitor<ILog> options,
EncryptionSettingsHelper encryptionSettingsHelper,
EncryptionFactory encryptionFactory) :
this(storageFactoryListener, storageFactoryConfig, settingsManager, storageSettingsHelper, tenantManager, coreBaseSettings, pathUtils, emailValidationKeyProvider, options, null, encryptionSettingsHelper, encryptionFactory)
{
}
public StorageFactory(
@ -192,7 +197,9 @@ namespace ASC.Data.Storage
PathUtils pathUtils,
EmailValidationKeyProvider emailValidationKeyProvider,
IOptionsMonitor<ILog> options,
IHttpContextAccessor httpContextAccessor)
IHttpContextAccessor httpContextAccessor,
EncryptionSettingsHelper encryptionSettingsHelper,
EncryptionFactory encryptionFactory)
{
StorageFactoryListener = storageFactoryListener;
StorageFactoryConfig = storageFactoryConfig;
@ -204,6 +211,8 @@ namespace ASC.Data.Storage
EmailValidationKeyProvider = emailValidationKeyProvider;
Options = options;
HttpContextAccessor = httpContextAccessor;
EncryptionSettingsHelper = encryptionSettingsHelper;
EncryptionFactory = encryptionFactory;
}
public IDataStore GetStorage(string tenant, string module)
@ -301,7 +310,7 @@ namespace ASC.Data.Storage
props = handler.Property.ToDictionary(r => r.Name, r => r.Value);
}
return ((IDataStore)Activator.CreateInstance(instanceType, TenantManager, PathUtils, EmailValidationKeyProvider, HttpContextAccessor, Options))
return ((IDataStore)Activator.CreateInstance(instanceType, TenantManager, PathUtils, EmailValidationKeyProvider, HttpContextAccessor, Options, EncryptionSettingsHelper, EncryptionFactory))
.Configure(tenant, handler, moduleElement, props)
.SetQuotaController(moduleElement.Count ? controller : null
/*don't count quota if specified on module*/);
@ -331,7 +340,9 @@ namespace ASC.Data.Storage
.AddPathUtilsService()
.AddEmailValidationKeyProviderService()
.AddStorageSettingsService()
.AddStorage();
.AddStorage()
.AddEncryptionFactoryService()
.AddEncryptionSettingsHelperService();
}
return services;

View File

@ -0,0 +1,17 @@
syntax = "proto3";
package ASC.Data.Storage.Encryption;
message EncryptionSettingsProto {
string password = 1;
EncryprtionStatus Status = 2;
bool NotifyUsers = 3;
string ServerRootPath = 4;
}
enum EncryprtionStatus {
Decrypted = 0;
EncryptionStarted = 1;
Encrypted = 2;
DecryptionStarted = 3;
}

View File

@ -0,0 +1,7 @@
syntax = "proto3";
package ASC.Data.Storage.Encryption;
message EncryptionStop {
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
package ASC.Data.Storage.Encryption;
message ProgressEncryption {
double Progress = 1;
int32 TenantId = 2;
}

View File

@ -506,6 +506,10 @@ namespace ASC.MessagingSystem
LicenseKeyUploaded = 5042,
StartStorageEncryption = 5050,
StartStorageDecryption = 5053,
#endregion
#region others

View File

@ -13,6 +13,7 @@
<ItemGroup>
<None Remove="protos\BackupProgress.proto" />
<None Remove="protos\DeleteSchedule.proto" />
</ItemGroup>
<ItemGroup>
@ -32,6 +33,7 @@
<ProjectReference Include="..\..\ASC.Data.Storage\ASC.Data.Storage.csproj" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="protos\DeleteSchedule.proto" />
<Protobuf Include="protos\BackupProgress.proto" />
</ItemGroup>
<Target Name="ChangeAliasesOfStrongNameAssemblies" BeforeTargets="FindReferenceAssembliesForReferences;ResolveReferences">

View File

@ -30,6 +30,7 @@ using System.Threading.Tasks;
using ASC.Common;
using ASC.Common.Utils;
using ASC.Data.Backup.Listerners;
using ASC.Web.Studio.Core.Notify;
using Microsoft.Extensions.Configuration;
@ -44,19 +45,22 @@ namespace ASC.Data.Backup.Service
private BackupSchedulerService SchedulerService { get; set; }
private BackupWorker BackupWorker { get; set; }
private IConfiguration Configuration { get; set; }
private BackupListener BackupListener { get; set; }
public BackupServiceLauncher(
IServiceProvider serviceProvider,
BackupCleanerService cleanerService,
BackupSchedulerService schedulerService,
BackupWorker backupWorker,
IConfiguration configuration)
IConfiguration configuration,
BackupListener backupListener)
{
ServiceProvider = serviceProvider;
CleanerService = cleanerService;
SchedulerService = schedulerService;
BackupWorker = backupWorker;
Configuration = configuration;
BackupListener = backupListener;
}
public Task StartAsync(CancellationToken cancellationToken)
@ -66,6 +70,7 @@ namespace ASC.Data.Backup.Service
var settings = Configuration.GetSetting<BackupSettings>("backup");
BackupWorker.Start(settings);
BackupListener.Start();
CleanerService.Period = settings.Cleaner.Period;
CleanerService.Start();
@ -79,6 +84,7 @@ namespace ASC.Data.Backup.Service
public Task StopAsync(CancellationToken cancellationToken)
{
BackupWorker.Stop();
BackupListener.Stop();
if (CleanerService != null)
{
CleanerService.Stop();
@ -101,7 +107,8 @@ namespace ASC.Data.Backup.Service
.AddBackupCleanerService()
.AddBackupSchedulerService()
.AddBackupWorkerService()
.AddBackupService();
.AddBackupService()
.AddBackupListenerService();
}
}
}

View File

@ -0,0 +1,50 @@

using System;
using ASC.Common;
using ASC.Common.Caching;
using ASC.Data.Backup.Contracts;
using ASC.Data.Backup.Service;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Data.Backup.Listerners
{
public class BackupListener
{
private ICacheNotify<DeleteSchedule> CacheDeleteSchedule { get; }
private IServiceProvider ServiceProvider { get; }
public BackupListener(ICacheNotify<DeleteSchedule> cacheDeleteSchedule, IServiceProvider serviceProvider)
{
CacheDeleteSchedule = cacheDeleteSchedule;
ServiceProvider = serviceProvider;
}
public void Start()
{
CacheDeleteSchedule.Subscribe((n) => DeleteScheldure(n), CacheNotifyAction.Insert);
}
public void Stop()
{
CacheDeleteSchedule.Unsubscribe(CacheNotifyAction.Insert);
}
public void DeleteScheldure(DeleteSchedule deleteSchedule)
{
using var scope = ServiceProvider.CreateScope();
var backupService = scope.ServiceProvider.GetService<BackupService>();
backupService.DeleteSchedule(deleteSchedule.TenantId);
}
}
public static class BackupListenerExtension
{
public static DIHelper AddBackupListenerService(this DIHelper services)
{
services.TryAddSingleton<BackupListener>();
return services.AddBackupService();
}
}
}

View File

@ -394,7 +394,7 @@ namespace ASC.Data.Backup.Tasks
{
if (services.TryAddScoped<RestorePortalTask>())
{
services.TryAddScoped<AscCacheNotify>();
services.TryAddSingleton<AscCacheNotify>();
return services
.AddCoreConfigurationService()
.AddStorageFactoryService()

View File

@ -0,0 +1,7 @@
syntax = "proto3";
package ASC.Data.Backup.Contracts;
message DeleteSchedule {
int32 TenantId = 1;
}

View File

@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ApplicationIcon />
<OutputType>Exe</OutputType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.13.0" />
<PackageReference Include="Grpc" Version="2.31.0" />
<PackageReference Include="Grpc.Tools" Version="2.31.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SharpCompress" Version="0.26.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\products\ASC.Files\Server\ASC.Files.csproj" />
<ProjectReference Include="..\..\..\products\ASC.People\Server\ASC.People.csproj" />
<ProjectReference Include="..\..\ASC.Api.Core\ASC.Api.Core.csproj" />
<ProjectReference Include="..\..\ASC.Common\ASC.Common.csproj" />
<ProjectReference Include="..\..\ASC.Core.Common\ASC.Core.Common.csproj" />
<ProjectReference Include="..\..\ASC.Data.Storage\ASC.Data.Storage.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,404 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using ASC.Common;
using ASC.Common.Caching;
using ASC.Common.Logging;
using ASC.Common.Threading.Progress;
using ASC.Core;
using ASC.Core.Tenants;
using ASC.Data.Storage.DiscStorage;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace ASC.Data.Storage.Encryption
{
public class EncryptionOperation : ProgressBase
{
private const string ConfigPath = "";
private bool HasErrors = false;
private const string ProgressFileName = "EncryptionProgress.tmp";
private IServiceProvider ServiceProvider { get; }
private EncryptionSettings EncryptionSettings { get; set; }
private bool IsEncryption { get; set; }
private bool UseProgressFile { get; set; }
private IEnumerable<string> Modules { get; set; }
private IEnumerable<Tenant> Tenants { get; set; }
private string ServerRootPath { get; set; }
public EncryptionOperation(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public void Init(EncryptionSettingsProto encryptionSettingsProto)
{
EncryptionSettings = new EncryptionSettings(encryptionSettingsProto);
IsEncryption = EncryptionSettings.Status == EncryprtionStatus.EncryptionStarted;
ServerRootPath = encryptionSettingsProto.ServerRootPath;
}
protected override void DoJob()
{
using var scope = ServiceProvider.CreateScope();
var scopeClass = scope.ServiceProvider.GetService<EncryptionOperationScope>();
var (log, encryptionSettingsHelper, tenantManager, notifyHelper, coreBaseSettings, storageFactoryConfig, storageFactory, progressEncryption, configuration) = scopeClass;
notifyHelper.Init(ServerRootPath);
Tenants = tenantManager.GetTenants(false);
Modules = storageFactoryConfig.GetModuleList(ConfigPath, true);
UseProgressFile = Convert.ToBoolean(configuration["storage:encryption:progressfile"] ?? "true");
Percentage = 10;
GetProgress(progressEncryption);
try
{
if (!coreBaseSettings.Standalone)
{
throw new NotSupportedException();
}
if (EncryptionSettings.Status == EncryprtionStatus.Encrypted || EncryptionSettings.Status == EncryprtionStatus.Decrypted)
{
log.Debug("Storage already " + EncryptionSettings.Status);
return;
}
Percentage = 30;
GetProgress(progressEncryption);
foreach (var tenant in Tenants)
{
Dictionary<string, DiscDataStore> dictionary = new Dictionary<string, DiscDataStore>();
foreach (var module in Modules)
{
dictionary.Add(module, (DiscDataStore)storageFactory.GetStorage(ConfigPath, tenant.TenantId.ToString(), module));
}
Parallel.ForEach(dictionary, (elem) =>
{
EncryptStore(tenant, elem.Key, elem.Value, storageFactoryConfig, log);
});
}
Percentage = 70;
GetProgress(progressEncryption);
if (!HasErrors)
{
DeleteProgressFiles(storageFactory);
SaveNewSettings(encryptionSettingsHelper, log);
}
Percentage = 90;
GetProgress(progressEncryption);
ActivateTenants(tenantManager, log, notifyHelper);
Percentage = 100;
GetProgress(progressEncryption);
}
catch (Exception e)
{
Error = e;
log.Error(e);
}
}
private void EncryptStore(Tenant tenant, string module, DiscDataStore store, StorageFactoryConfig storageFactoryConfig, ILog log)
{
var domains = storageFactoryConfig.GetDomainList(ConfigPath, module).ToList();
domains.Add(string.Empty);
var progress = ReadProgress(store);
foreach (var domain in domains)
{
var logParent = string.Format("Tenant: {0}, Module: {1}, Domain: {2}", tenant.TenantAlias, module, domain);
var files = GetFiles(domains, progress, store, domain);
EncryptFiles(store, domain, files, logParent, log);
}
StepDone();
log.DebugFormat("Percentage: {0}", Percentage);
}
private List<string> ReadProgress(DiscDataStore store)
{
var encryptedFiles = new List<string>();
if (!UseProgressFile)
{
return encryptedFiles;
}
if (store.IsFile(string.Empty, ProgressFileName))
{
using (var stream = store.GetReadStream(string.Empty, ProgressFileName))
{
using (var reader = new StreamReader(stream))
{
string line;
while ((line = reader.ReadLine()) != null)
{
encryptedFiles.Add(line);
}
}
}
}
else
{
store.GetWriteStream(string.Empty, ProgressFileName).Close();
}
return encryptedFiles;
}
public void GetProgress(ICacheNotify<ProgressEncryption> progress)
{
var progressEncryption = new ProgressEncryption()
{
Proggress = Percentage
};
progress.Publish(progressEncryption, CacheNotifyAction.Insert);
}
private IEnumerable<string> GetFiles(List<string> domains, List<string> progress, DiscDataStore targetStore, string targetDomain)
{
IEnumerable<string> files = targetStore.ListFilesRelative(targetDomain, "\\", "*.*", true);
if (progress.Any())
{
files = files.Where(path => !progress.Contains(path));
}
if (!string.IsNullOrEmpty(targetDomain))
{
return files;
}
var notEmptyDomains = domains.Where(domain => !string.IsNullOrEmpty(domain));
if (notEmptyDomains.Any())
{
files = files.Where(path => notEmptyDomains.All(domain => !path.Contains(domain + Path.DirectorySeparatorChar)));
}
files = files.Where(path => !path.EndsWith(ProgressFileName));
return files;
}
private void EncryptFiles(DiscDataStore store, string domain, IEnumerable<string> files, string logParent, ILog log)
{
foreach (var file in files)
{
var logItem = string.Format("{0}, File: {1}", logParent, file);
log.Debug(logItem);
try
{
if (IsEncryption)
{
store.Encrypt(domain, file);
}
else
{
store.Decrypt(domain, file);
}
WriteProgress(store, file, UseProgressFile);
}
catch (Exception e)
{
HasErrors = true;
log.Error(logItem + " " + e.Message, e);
// ERROR_DISK_FULL: There is not enough space on the disk.
// if (e is IOException && e.HResult == unchecked((int)0x80070070)) break;
}
}
}
private void WriteProgress(DiscDataStore store, string file, bool useProgressFile)
{
if (!useProgressFile)
{
return;
}
using (var stream = store.GetWriteStream(string.Empty, ProgressFileName, FileMode.Append))
{
using (var writer = new StreamWriter(stream))
{
writer.WriteLine(file);
}
}
}
private void DeleteProgressFiles(StorageFactory storageFactory)
{
foreach (var tenant in Tenants)
{
foreach (var module in Modules)
{
var store = (DiscDataStore)storageFactory.GetStorage(ConfigPath, tenant.TenantId.ToString(), module);
if (store.IsFile(string.Empty, ProgressFileName))
{
store.Delete(string.Empty, ProgressFileName);
}
}
}
}
private void SaveNewSettings(EncryptionSettingsHelper encryptionSettingsHelper, ILog log)
{
if (IsEncryption)
{
EncryptionSettings.Status = EncryprtionStatus.Encrypted;
}
else
{
EncryptionSettings.Status = EncryprtionStatus.Decrypted;
EncryptionSettings.Password = string.Empty;
}
encryptionSettingsHelper.Save(EncryptionSettings);
log.Debug("Save new EncryptionSettings");
}
private void ActivateTenants(TenantManager tenantManager, ILog log, NotifyHelper notifyHelper)
{
foreach (var tenant in Tenants)
{
if (tenant.Status == TenantStatus.Encryption)
{
tenantManager.SetCurrentTenant(tenant);
tenant.SetStatus(TenantStatus.Active);
tenantManager.SaveTenant(tenant);
log.DebugFormat("Tenant {0} SetStatus Active", tenant.TenantAlias);
if (!HasErrors)
{
if (EncryptionSettings.NotifyUsers)
{
if (IsEncryption)
{
notifyHelper.SendStorageEncryptionSuccess(tenant.TenantId);
}
else
{
notifyHelper.SendStorageDecryptionSuccess(tenant.TenantId);
}
log.DebugFormat("Tenant {0} SendStorageEncryptionSuccess", tenant.TenantAlias);
}
}
else
{
if (IsEncryption)
{
notifyHelper.SendStorageEncryptionError(tenant.TenantId);
}
else
{
notifyHelper.SendStorageDecryptionError(tenant.TenantId);
}
log.DebugFormat("Tenant {0} SendStorageEncryptionError", tenant.TenantAlias);
}
}
}
}
}
public class EncryptionOperationScope
{
private ILog Log { get; set; }
private EncryptionSettingsHelper EncryptionSettingsHelper { get; set; }
private TenantManager TenantManager { get; set; }
private NotifyHelper NotifyHelper { get; set; }
private CoreBaseSettings CoreBaseSettings { get; set; }
private StorageFactoryConfig StorageFactoryConfig { get; set; }
private StorageFactory StorageFactory { get; set; }
private ICacheNotify<ProgressEncryption> ProgressEncryption { get; }
private IConfiguration Configuration { get; }
public EncryptionOperationScope(IOptionsMonitor<ILog> options,
StorageFactoryConfig storageFactoryConfig,
StorageFactory storageFactory,
TenantManager tenantManager,
CoreBaseSettings coreBaseSettings,
NotifyHelper notifyHelper,
EncryptionSettingsHelper encryptionSettingsHelper,
IConfiguration configuration,
ICacheNotify<ProgressEncryption> progressEncryption)
{
Log = options.CurrentValue;
StorageFactoryConfig = storageFactoryConfig;
StorageFactory = storageFactory;
TenantManager = tenantManager;
CoreBaseSettings = coreBaseSettings;
NotifyHelper = notifyHelper;
EncryptionSettingsHelper = encryptionSettingsHelper;
ProgressEncryption = progressEncryption;
Configuration = configuration;
}
public void Deconstruct(out ILog log, out EncryptionSettingsHelper encryptionSettingsHelper, out TenantManager tenantManager, out NotifyHelper notifyHelper, out CoreBaseSettings coreBaseSettings, out StorageFactoryConfig storageFactoryConfig, out StorageFactory storageFactory, out ICacheNotify<ProgressEncryption> progressEncryption, out IConfiguration configuration)
{
log = Log;
encryptionSettingsHelper = EncryptionSettingsHelper;
tenantManager = TenantManager;
notifyHelper = NotifyHelper;
coreBaseSettings = CoreBaseSettings;
storageFactoryConfig = StorageFactoryConfig;
storageFactory = StorageFactory;
progressEncryption = ProgressEncryption;
configuration = Configuration;
}
}
public static class EncryptionOperationExtension
{
public static DIHelper AddEncryptionOperationService(this DIHelper services)
{
services.TryAddTransient<EncryptionOperation>();
services.TryAddTransient<EncryptionOperationScope>();
return services
.AddStorageFactoryConfigService()
.AddStorageFactoryService()
.AddNotifyHelperService()
.AddEncryptionSettingsHelperService();
}
}
}

View File

@ -1,6 +1,6 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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).
@ -23,14 +23,50 @@
*
*/
using System.Threading;
using System.Threading.Tasks;
using ASC.Common;
using Microsoft.Extensions.Hosting;
namespace ASC.Data.Storage.Encryption
{
public enum EncryprtionStatus
public class EncryptionServiceLauncher : IHostedService
{
Decrypted,
EncryptionStarted,
Encrypted,
DecryptionStarted
private EncryptionServiceListener EncryptionServiceListener { get; set; }
public EncryptionServiceLauncher(EncryptionServiceListener encryptionServiceListener)
{
EncryptionServiceListener = encryptionServiceListener;
}
public Task StartAsync(CancellationToken cancellationToken)
{
if (EncryptionServiceListener != null)
{
EncryptionServiceListener.Start();
}
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
if (EncryptionServiceListener != null)
{
EncryptionServiceListener.Stop();
}
return Task.CompletedTask;
}
}
public static class EncryptionServiceLauncherExtension
{
public static DIHelper AddEncryptionServiceLauncher(this DIHelper services)
{
services.TryAddSingleton<EncryptionServiceLauncher>();
return services.AddEncryptionServiceListener();
}
}
}

View File

@ -0,0 +1,74 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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.Caching;
using ASC.Common;
namespace ASC.Data.Storage.Encryption
{
public class EncryptionServiceListener
{
private ICacheNotify<EncryptionSettingsProto> NotifySettings { get; }
private ICacheNotify<EncryptionStop> NotifyStop { get; }
private EncryptionWorker EncryptionWorker { get; }
public EncryptionServiceListener( ICacheNotify<EncryptionSettingsProto> notifySettings, ICacheNotify<EncryptionStop> notifyStop, EncryptionWorker encryptionWorker)
{
NotifySettings = notifySettings;
NotifyStop = notifyStop;
EncryptionWorker = encryptionWorker;
}
public void Start()
{
NotifySettings.Subscribe((n) => StartEncryption(n), CacheNotifyAction.Insert);
NotifyStop.Subscribe((n) => StopEncryption(), CacheNotifyAction.Insert);
}
public void Stop()
{
NotifySettings.Unsubscribe(CacheNotifyAction.Insert);
NotifySettings.Unsubscribe(CacheNotifyAction.Insert);
}
public void StartEncryption(EncryptionSettingsProto encryptionSettings)
{
EncryptionWorker.Start(encryptionSettings);
}
public void StopEncryption()
{
EncryptionWorker.Stop();
}
}
public static class EncryptionServiceListenerExtension
{
public static DIHelper AddEncryptionServiceListener(this DIHelper services)
{
services.TryAddSingleton<EncryptionServiceListener>();
return services.AddEncryptionWorkerService();
}
}
}

View File

@ -0,0 +1,115 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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.Threading;
using System.Threading.Tasks;
using ASC.Common;
using ASC.Common.Caching;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Data.Storage.Encryption
{
public class EncryptionWorker
{
private CancellationTokenSource TokenSource { get; set; }
private ICache Cache { get; }
private object Locker { get; }
private FactoryOperation FactoryOperation { get; }
public EncryptionWorker(FactoryOperation factoryOperation)
{
Cache = AscCache.Memory;
Locker = new object();
FactoryOperation = factoryOperation;
}
public void Start(EncryptionSettingsProto encryptionSettings)
{
EncryptionOperation encryptionOperation;
lock (Locker)
{
if (Cache.Get<EncryptionOperation>(GetCacheKey()) != null) return;
TokenSource = new CancellationTokenSource();
encryptionOperation = FactoryOperation.CreateOperation(encryptionSettings);
Cache.Insert(GetCacheKey(), encryptionOperation, DateTime.MaxValue);
}
var task = new Task(encryptionOperation.RunJob, TokenSource.Token, TaskCreationOptions.LongRunning);
task.ConfigureAwait(false)
.GetAwaiter()
.OnCompleted(() =>
{
lock (Locker)
{
Cache.Remove(GetCacheKey());
}
});
task.Start();
}
public void Stop()
{
TokenSource.Cancel();
}
public string GetCacheKey()
{
return typeof(EncryptionOperation).FullName;
}
}
public class FactoryOperation
{
private IServiceProvider ServiceProvider { get; set; }
public FactoryOperation(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public EncryptionOperation CreateOperation(EncryptionSettingsProto encryptionSettings)
{
var item = ServiceProvider.GetService<EncryptionOperation>();
item.Init(encryptionSettings);
return item;
}
}
public static class EncryptionWorkerExtension
{
public static DIHelper AddEncryptionWorkerService(this DIHelper services)
{
services.TryAddSingleton<EncryptionWorker>();
services.TryAddSingleton<FactoryOperation>();
return services.AddEncryptionOperationService();
}
}
}

View File

@ -0,0 +1,116 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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 ASC.Common;
using ASC.Common.Logging;
using ASC.Core.Notify;
using ASC.Notify.Messages;
using Microsoft.Extensions.Options;
namespace ASC.Data.Storage.Encryption
{
public class NotifyHelper
{
private const string NotifyService = "ASC.Web.Studio.Core.Notify.StudioNotifyService, ASC.Web.Core";
private string ServerRootPath { get; set; }
private NotifyServiceClient NotifyServiceClient { get; set; }
private ILog Log { get; set; }
public NotifyHelper(IOptionsMonitor<ILog> option, NotifyServiceClient notifyServiceClient)
{
NotifyServiceClient = notifyServiceClient;
Log = option.CurrentValue;
}
public void Init(string serverRootPath)
{
ServerRootPath = serverRootPath;
}
public void SendStorageEncryptionStart(int tenantId)
{
SendStorageEncryptionNotification("SendStorageEncryptionStart", tenantId);
}
public void SendStorageEncryptionSuccess(int tenantId)
{
SendStorageEncryptionNotification("SendStorageEncryptionSuccess", tenantId);
}
public void SendStorageEncryptionError(int tenantId)
{
SendStorageEncryptionNotification("SendStorageEncryptionError", tenantId);
}
public void SendStorageDecryptionStart(int tenantId)
{
SendStorageEncryptionNotification("SendStorageDecryptionStart", tenantId);
}
public void SendStorageDecryptionSuccess(int tenantId)
{
SendStorageEncryptionNotification("SendStorageDecryptionSuccess", tenantId);
}
public void SendStorageDecryptionError(int tenantId)
{
SendStorageEncryptionNotification("SendStorageDecryptionError", tenantId);
}
private void SendStorageEncryptionNotification(string method, int tenantId)
{
var notifyInvoke = new NotifyInvoke()
{
Service = NotifyService,
Method = method,
Tenant = tenantId
};
notifyInvoke.Parameters.Add(ServerRootPath);
try
{
NotifyServiceClient.InvokeSendMethod(notifyInvoke);
}
catch (Exception error)
{
Log.Warn("Error while sending notification", error);
}
}
}
public static class NotifyHelperExtension
{
public static DIHelper AddNotifyHelperService(this DIHelper services)
{
services.TryAddScoped<NotifyHelper>();
services.TryAddScoped<NotifyServiceClient>();
return services;
}
}
}

View File

@ -0,0 +1,73 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace ASC.Data.Storage.Encryption
{
public class Program
{
public static async Task Main(string[] args)
{
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureAppConfiguration((hostContext, config) =>
{
var buided = config.Build();
var path = buided["pathToConf"];
if (!Path.IsPathRooted(path))
{
path = Path.GetFullPath(Path.Combine(hostContext.HostingEnvironment.ContentRootPath, path));
}
config.SetBasePath(path);
var env = hostContext.Configuration.GetValue("ENVIRONMENT", "Production");
config
.AddInMemoryCollection(new Dictionary<string, string>
{
{"pathToConf", path }
}
)
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env}.json", true)
.AddJsonFile("storage.json")
.AddJsonFile("notify.json")
.AddJsonFile("kafka.json")
.AddJsonFile($"kafka.{env}.json", true)
.AddEnvironmentVariables();
})
.UseConsoleLifetime()
.Build()
.Run();
}
}
}

View File

@ -0,0 +1,35 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:5019/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"$STORAGE_ROOT": "../../../Data",
"log__name": "Encryption",
"log__dir": "../../../Logs",
"core__products__folder": "../../../products"
}
},
"ASC.Data.Storage.Encryption": {
"commandName": "Project",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"$STORAGE_ROOT": "../../../Data",
"log__name": "Encryption",
"log__dir": "../../../Logs",
"core__products__folder": "../../../products"
},
"applicationUrl": "http://localhost:5019/"
}
}
}

View File

@ -0,0 +1,60 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2020
*
* 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.Api.Core;
using ASC.Common;
using Autofac.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ASC.Common.DependencyInjection;
namespace ASC.Data.Storage.Encryption
{
public class Startup : BaseStartup
{
public override string[] LogParams { get => new string[] { "ASC.Data.Storage.Encryption" }; }
public Startup(IConfiguration configuration, IHostEnvironment hostEnvironment) : base(configuration, hostEnvironment)
{
}
public override void ConfigureServices(IServiceCollection services)
{
var diHelper = new DIHelper(services);
diHelper.AddEncryptionServiceLauncher();
services.AddHostedService<EncryptionServiceLauncher>();
base.ConfigureServices(services);
services.AddAutofac(Configuration, HostEnvironment.ContentRootPath);
}
}
}

View File

@ -0,0 +1,3 @@
{
"pathToConf": "..\\..\\..\\config"
}

View File

@ -25,7 +25,7 @@
using System;
using System.Reflection;
using System.Linq;
using ASC.Common;
using ASC.Common.Caching;
@ -42,51 +42,58 @@ namespace ASC.Notify
{
public class NotifyService : INotifyService, IDisposable
{
private readonly ILog log;
private readonly ICacheNotify<NotifyMessage> cacheNotify;
private readonly DbWorker db;
private ILog Log { get; }
private ICacheNotify<NotifyMessage> CacheNotify { get; }
private ICacheNotify<NotifyInvoke> CacheInvoke { get; }
private DbWorker Db { get; }
private IServiceProvider ServiceProvider { get; }
public NotifyService(DbWorker db, IServiceProvider serviceProvider, ICacheNotify<NotifyMessage> cacheNotify, IOptionsMonitor<ILog> options)
public NotifyService(DbWorker db, IServiceProvider serviceProvider, ICacheNotify<NotifyMessage> cacheNotify, ICacheNotify<NotifyInvoke> cacheInvoke, IOptionsMonitor<ILog> options)
{
this.db = db;
Db = db;
ServiceProvider = serviceProvider;
this.cacheNotify = cacheNotify;
log = options.CurrentValue;
CacheNotify = cacheNotify;
CacheInvoke = cacheInvoke;
Log = options.CurrentValue;
}
public void Start()
{
cacheNotify.Subscribe((n) => SendNotifyMessage(n), CacheNotifyAction.InsertOrUpdate);
CacheNotify.Subscribe((n) => SendNotifyMessage(n), CacheNotifyAction.InsertOrUpdate);
CacheInvoke.Subscribe((n) => InvokeSendMethod(n), CacheNotifyAction.InsertOrUpdate);
}
public void Stop()
{
cacheNotify.Unsubscribe(CacheNotifyAction.InsertOrUpdate);
CacheNotify.Unsubscribe(CacheNotifyAction.InsertOrUpdate);
}
public void SendNotifyMessage(NotifyMessage notifyMessage)
{
try
{
db.SaveMessage(notifyMessage);
Db.SaveMessage(notifyMessage);
}
catch (Exception e)
{
log.Error(e);
Log.Error(e);
}
}
public void InvokeSendMethod(string service, string method, int tenant, params object[] parameters)
public void InvokeSendMethod(NotifyInvoke notifyInvoke)
{
var serviceType = Type.GetType(service, true);
var getinstance = serviceType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);
var service = notifyInvoke.Service;
var method = notifyInvoke.Method;
var tenant = notifyInvoke.Tenant;
var parameters = notifyInvoke.Parameters;
var instance = getinstance.GetValue(serviceType, null);
var serviceType = Type.GetType(service, true);
using var scope = ServiceProvider.CreateScope();
var instance = scope.ServiceProvider.GetService(serviceType);
if (instance == null)
{
throw new Exception("Service instance not found.");
@ -98,17 +105,17 @@ namespace ASC.Notify
throw new Exception("Method not found.");
}
using var scope = ServiceProvider.CreateScope();
var scopeClass = scope.ServiceProvider.GetService<NotifyServiceScope>();
var (tenantManager, tenantWhiteLabelSettingsHelper, settingsManager) = scopeClass;
tenantManager.SetCurrentTenant(tenant);
tenantWhiteLabelSettingsHelper.Apply(settingsManager.Load<TenantWhiteLabelSettings>(), tenant);
methodInfo.Invoke(instance, parameters);
methodInfo.Invoke(instance, parameters.ToArray());
}
public void Dispose()
{
cacheNotify.Unsubscribe(CacheNotifyAction.InsertOrUpdate);
CacheNotify.Unsubscribe(CacheNotifyAction.InsertOrUpdate);
CacheInvoke.Unsubscribe(CacheNotifyAction.InsertOrUpdate);
}
}

View File

@ -122,5 +122,11 @@
},
"bookmarking": {
"thumbnail-url": "http://localhost:9800/?url={0}"
}
},
"storage": {
"encryption": {
"progressfile": "false",
"tempdir": ""
}
}
}

View File

@ -78,7 +78,7 @@
"appendTenantId": false,
"public": true,
"disableMigrate": true,
"disableEncryption": true
"disableEncryption": true
},
{
"name": "crm",
@ -261,7 +261,7 @@
"appendTenantId": false,
"public": true,
"disableMigrate": true,
"disableEncryption": true
"disableEncryption": true
}
]
}

View File

@ -58,7 +58,7 @@ namespace ASC.Web.Files.Core.Entries
{
var fileShares = FileSharing.GetSharedInfo<T>(new ItemList<string> { string.Format("file_{0}", fileId) }).ToList();
fileShares = fileShares.Where(share => !share.SubjectGroup && !share.SubjectId.Equals(FileConstant.ShareLinkId) && share.Share == FileShare.ReadWrite).ToList();
var accountsString = fileShares.Select(share => EncryptionLoginProvider.GetAddress(share.SubjectId)).Where(address => !string.IsNullOrEmpty(address));
var accountsString = fileShares.Select(share => EncryptionLoginProvider.GetKeys(share.SubjectId)).Where(address => !string.IsNullOrEmpty(address));
return accountsString;
}
}

View File

@ -33,6 +33,8 @@
<ProjectReference Include="..\..\common\ASC.Common\ASC.Common.csproj" />
<ProjectReference Include="..\..\common\ASC.Core.Common\ASC.Core.Common.csproj" />
<ProjectReference Include="..\..\common\ASC.Data.Reassigns\ASC.Data.Reassigns.csproj" />
<ProjectReference Include="..\..\common\ASC.Data.Storage\ASC.Data.Storage.csproj" />
<ProjectReference Include="..\..\common\services\ASC.Data.Backup\ASC.Data.Backup.csproj" />
<ProjectReference Include="..\ASC.Web.Core\ASC.Web.Core.csproj" />
</ItemGroup>

View File

@ -40,6 +40,7 @@ using ASC.Api.Collections;
using ASC.Api.Core;
using ASC.Api.Utils;
using ASC.Common;
using ASC.Common.Caching;
using ASC.Common.Logging;
using ASC.Common.Utils;
using ASC.Common.Web;
@ -49,8 +50,11 @@ using ASC.Core.Common.Configuration;
using ASC.Core.Common.Settings;
using ASC.Core.Tenants;
using ASC.Core.Users;
using ASC.Data.Backup.Contracts;
using ASC.Data.Backup.Service;
using ASC.Data.Storage;
using ASC.Data.Storage.Configuration;
using ASC.Data.Storage.Encryption;
using ASC.Data.Storage.Migration;
using ASC.FederatedLogin;
using ASC.FederatedLogin.LoginProviders;
@ -82,6 +86,7 @@ using ASC.Web.Studio.Utility;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
@ -150,8 +155,12 @@ namespace ASC.Api.Settings
private StorageSettingsHelper StorageSettingsHelper { get; }
private ServiceClient ServiceClient { get; }
private StorageFactory StorageFactory { get; }
public UrlShortener UrlShortener { get; }
public ILog Log { get; set; }
private UrlShortener UrlShortener { get; } private EncryptionServiceClient EncryptionServiceClient { get; }
private EncryptionSettingsHelper EncryptionSettingsHelper { get; }
private BackupServiceNotifier BackupServiceNotifier { get; }
private ICacheNotify<DeleteSchedule> CacheDeleteSchedule { get; }
private EncryptionServiceNotifier EncryptionServiceNotifier { get; }
private ILog Log { get; set; }
public SettingsController(
IOptionsMonitor<ILog> option,
@ -205,8 +214,14 @@ namespace ASC.Api.Settings
IOptionsSnapshot<AccountLinker> accountLinker,
FirstTimeTenantSettings firstTimeTenantSettings,
ServiceClient serviceClient,
StorageFactory storageFactory,
UrlShortener urlShortener)
UrlShortener urlShortener
EncryptionServiceClient encryptionServiceClient,
EncryptionSettingsHelper encryptionSettingsHelper,
BackupServiceNotifier backupServiceNotifier,
ICacheNotify<DeleteSchedule> cacheDeleteSchedule,
EncryptionServiceNotifier encryptionServiceNotifier)
{
Log = option.Get("ASC.Api");
WebHostEnvironment = webHostEnvironment;
@ -259,6 +274,11 @@ namespace ASC.Api.Settings
CoreSettings = coreSettings;
StorageSettingsHelper = storageSettingsHelper;
ServiceClient = serviceClient;
EncryptionServiceClient = encryptionServiceClient;
EncryptionSettingsHelper = encryptionSettingsHelper;
BackupServiceNotifier = backupServiceNotifier;
CacheDeleteSchedule = cacheDeleteSchedule;
EncryptionServiceNotifier = encryptionServiceNotifier;
StorageFactory = storageFactory;
UrlShortener = urlShortener;
}
@ -1729,6 +1749,199 @@ namespace ASC.Api.Settings
return ServiceClient.GetProgress(Tenant.TenantId);
}
[Read("encryption")]
public void StartEncryption(EncryptionSettingsModel settings)
{
EncryptionSettingsProto encryptionSettingsProto = new EncryptionSettingsProto
{
NotifyUsers = settings.NotifyUsers,
Password = settings.Password,
Status = settings.Status,
ServerRootPath = settings.ServerRootPath
};
EncryptionServiceClient.Start(encryptionSettingsProto);
}
public readonly object Locker = new object();
[Create("encryption/start")]
public void StartStorageEncryption(StorageEncryptionModel storageEncryption)
{
lock (Locker)
{
var activeTenants = TenantManager.GetTenants();
if (activeTenants.Any())
{
StartEncryption(storageEncryption.NotifyUsers);
}
}
}
private void StartEncryption(bool notifyUsers)
{
if (!SetupInfo.IsVisibleSettings<EncryptionSettings>())
{
throw new NotSupportedException();
}
if (!CoreBaseSettings.Standalone)
{
throw new NotSupportedException();
}
PermissionContext.DemandPermissions(SecutiryConstants.EditPortalSettings);
TenantExtra.DemandControlPanelPermission();
if (!TenantManager.GetTenantQuota(TenantManager.GetCurrentTenant().TenantId).DiscEncryption)
{
throw new BillingException(Resource.ErrorNotAllowedOption, "DiscEncryption");
}
var storages = GetAllStorages();
if (storages.Any(s => s.Current))
{
throw new NotSupportedException();
}
var cdnStorages = GetAllCdnStorages();
if (cdnStorages.Any(s => s.Current))
{
throw new NotSupportedException();
}
var tenants = TenantManager.GetTenants();
foreach (var tenant in tenants)
{
var progress = BackupServiceNotifier.GetBackupProgress(tenant.TenantId);
if (progress != null && progress.IsCompleted == false)
{
throw new Exception();
}
}
foreach (var tenant in tenants)
{
CacheDeleteSchedule.Publish(new DeleteSchedule() { TenantId = tenant.TenantId }, CacheNotifyAction.Insert);
}
var settings = EncryptionSettingsHelper.Load();
settings.NotifyUsers = notifyUsers;
if (settings.Status == EncryprtionStatus.Decrypted)
{
settings.Status = EncryprtionStatus.EncryptionStarted;
settings.Password = EncryptionSettingsHelper.GeneratePassword(32, 16);
}
else if (settings.Status == EncryprtionStatus.Encrypted)
{
settings.Status = EncryprtionStatus.DecryptionStarted;
}
MessageService.Send(settings.Status == EncryprtionStatus.EncryptionStarted ? MessageAction.StartStorageEncryption : MessageAction.StartStorageDecryption);
var serverRootPath = CommonLinkUtility.GetFullAbsolutePath("~").TrimEnd('/');
foreach (var tenant in tenants)
{
TenantManager.SetCurrentTenant(tenant);
if (notifyUsers)
{
if (settings.Status == EncryprtionStatus.EncryptionStarted)
{
StudioNotifyService.SendStorageEncryptionStart(serverRootPath);
}
else
{
StudioNotifyService.SendStorageDecryptionStart(serverRootPath);
}
}
tenant.SetStatus(TenantStatus.Encryption);
TenantManager.SaveTenant(tenant);
}
EncryptionSettingsHelper.Save(settings);
var encryptionSettingsProto = new EncryptionSettingsProto
{
NotifyUsers = settings.NotifyUsers,
Password = settings.Password,
Status = settings.Status,
ServerRootPath = serverRootPath
};
EncryptionServiceClient.Start(encryptionSettingsProto);
}
/// <summary>
/// Get storage encryption settings
/// </summary>
/// <returns>EncryptionSettings</returns>
/// <visible>false</visible>
[Read("encryption/settings")]
public EncryptionSettings GetStorageEncryptionSettings()
{
try
{
if (!SetupInfo.IsVisibleSettings<EncryptionSettings>())
{
throw new NotSupportedException();
}
if (!CoreBaseSettings.Standalone)
{
throw new NotSupportedException();
}
PermissionContext.DemandPermissions(SecutiryConstants.EditPortalSettings);
TenantExtra.DemandControlPanelPermission();
if (!TenantManager.GetTenantQuota(TenantManager.GetCurrentTenant().TenantId).DiscEncryption)
{
throw new BillingException(Resource.ErrorNotAllowedOption, "DiscEncryption");
}
var settings = EncryptionSettingsHelper.Load();
settings.Password = string.Empty; // Don't show password
return settings;
}
catch (Exception e)
{
Log.Error("GetStorageEncryptionSettings", e);
return null;
}
}
[Read("encryption/progress")]
public double? GetStorageEncryptionProgress()
{
if (!SetupInfo.IsVisibleSettings<EncryptionSettings>())
{
throw new NotSupportedException();
}
if (!CoreBaseSettings.Standalone)
{
throw new NotSupportedException();
}
if (!TenantManager.GetTenantQuota(TenantManager.GetCurrentTenant().TenantId).DiscEncryption)
{
throw new BillingException(Resource.ErrorNotAllowedOption, "DiscEncryption");
}
return EncryptionServiceNotifier.GetEncryptionProgress(Tenant.TenantId)?.Progress;
}
[Update("storage")]
public StorageSettings UpdateStorage(StorageModel model)
{
@ -2173,6 +2386,9 @@ namespace ASC.Api.Settings
.AddFirstTimeTenantSettings()
.AddServiceClient()
.AddTwilioProviderService()
.AddEncryptionServiceClient()
.AddEncryptionSettingsHelperService()
.AddTwilioProviderService()
.AddStorageFactoryService();
}
}

View File

@ -0,0 +1,15 @@
using ASC.Data.Storage.Encryption;
namespace ASC.Web.Api.Models
{
public class EncryptionSettingsModel
{
public string Password { get; set; }
public EncryprtionStatus Status { get; set; }
public bool NotifyUsers { get; set; }
public string ServerRootPath { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace ASC.Web.Api.Models
{
public class StorageEncryptionModel
{
public bool NotifyUsers { get; set; }
}
}

View File

@ -944,6 +944,74 @@ namespace ASC.Web.Studio.Core.Notify
}
#endregion
#region Storage encryption
public void SendStorageEncryptionStart(string serverRootPath)
{
SendStorageEncryptionNotify(Actions.StorageEncryptionStart, false, serverRootPath);
}
public void SendStorageEncryptionSuccess(string serverRootPath)
{
SendStorageEncryptionNotify(Actions.StorageEncryptionSuccess, false, serverRootPath);
}
public void SendStorageEncryptionError(string serverRootPath)
{
SendStorageEncryptionNotify(Actions.StorageEncryptionError, true, serverRootPath);
}
public void SendStorageDecryptionStart(string serverRootPath)
{
SendStorageEncryptionNotify(Actions.StorageDecryptionStart, false, serverRootPath);
}
public void SendStorageDecryptionSuccess(string serverRootPath)
{
SendStorageEncryptionNotify(Actions.StorageDecryptionSuccess, false, serverRootPath);
}
public void SendStorageDecryptionError(string serverRootPath)
{
SendStorageEncryptionNotify(Actions.StorageDecryptionError, true, serverRootPath);
}
private void SendStorageEncryptionNotify(INotifyAction action, bool notifyAdminsOnly, string serverRootPath)
{
var users = notifyAdminsOnly
? UserManager.GetUsersByGroup(Constants.GroupAdmin.ID)
: UserManager.GetUsers().Where(u => u.ActivationStatus.HasFlag(EmployeeActivationStatus.Activated));
foreach (var u in users)
{
client.SendNoticeToAsync(
action,
null,
new[] { StudioNotifyHelper.ToRecipient(u.ID) },
new[] { EMailSenderName },
null,
new TagValue(Tags.UserName, u.FirstName.HtmlEncode()),
new TagValue(Tags.PortalUrl, serverRootPath),
new TagValue(Tags.ControlPanelUrl, GetControlPanelUrl(serverRootPath)));
}
}
private string GetControlPanelUrl(string serverRootPath)
{
var controlPanelUrl = SetupInfo.ControlPanelUrl;
if (string.IsNullOrEmpty(controlPanelUrl))
return string.Empty;
if (controlPanelUrl.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) ||
controlPanelUrl.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
return controlPanelUrl;
return serverRootPath + "/" + controlPanelUrl.TrimStart('~', '/').TrimEnd('/');
}
#endregion
}
public class StudioNotifyServiceScope

View File

@ -349,7 +349,8 @@ $activity.Key
<!-- saas user activation v10 -->
<pattern id="saas_user_activation_v10">
<subject resource="|subject_saas_user_activation_v10|ASC.Web.Core.PublicResources.WebstudioNotifyPatternResource,ASC.Web.Core"/>
<subject resource="|subject_saas_user_activation_v10|ASC.Web.Core.PublicResources.WebstudioNotifyPatternRes
ource,ASC.Web.Core"/>
<body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_saas_user_activation_v10|ASC.Web.Core.PublicResources.WebstudioNotifyPatternResource,ASC.Web.Core" />
</pattern>
@ -743,41 +744,41 @@ $activity.Key
<subject resource="|subject_saas_custom_mode_reg_data|ASC.Web.Studio.PublicResources.CustomModeResource,ASC.Web.Core" />
<body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_saas_custom_mode_reg_data|ASC.Web.Core.PublicResources.CustomModeResource,ASC.Web.Core" />
</pattern>
<!-- encryption_start -->
<pattern id="storage_encryption_start">
<subject resource="|subject_storage_encryption_start|ASC.Web.Studio.Core.Notify.WebstudioNotifyPatternResource,ASC.Web.Studio" />
<body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_storage_encryption_start|ASC.Web.Studio.Core.Notify.WebstudioNotifyPatternResource,ASC.Web.Studio" />
<subject resource="|subject_storage_encryption_start|ASC.Web.Core.PublicResources.WebstudioNotifyPatternResource,ASC.Web.Core" />
<body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_storage_encryption_start|ASC.Web.Core.PublicResources.WebstudioNotifyPatternResource,ASC.Web.Core" />
</pattern>
<!-- encryption_success -->
<pattern id="storage_encryption_success">
<subject resource="|subject_storage_encryption_success|ASC.Web.Studio.Core.Notify.WebstudioNotifyPatternResource,ASC.Web.Studio" />
<body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_storage_encryption_success|ASC.Web.Studio.Core.Notify.WebstudioNotifyPatternResource,ASC.Web.Studio" />
<subject resource="|subject_storage_encryption_success|ASC.Web.Core.PublicResources.WebstudioNotifyPatternResource,ASC.Web.Core" />
<body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_storage_encryption_success|ASC.Web.Core.PublicResources.WebstudioNotifyPatternResource,ASC.Web.Core" />
</pattern>
<!-- encryption_error -->
<pattern id="storage_encryption_error">
<subject resource="|subject_storage_encryption_error|ASC.Web.Studio.Core.Notify.WebstudioNotifyPatternResource,ASC.Web.Studio" />
<body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_storage_encryption_error|ASC.Web.Studio.Core.Notify.WebstudioNotifyPatternResource,ASC.Web.Studio" />
<subject resource="|subject_storage_encryption_error|ASC.Web.Core.PublicResources.WebstudioNotifyPatternResource,ASC.Web.Core" />
<body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_storage_encryption_error|ASC.Web.Core.PublicResources.WebstudioNotifyPatternResource,ASC.Web.Core" />
</pattern>
<!-- decryption_start -->
<pattern id="storage_decryption_start">
<subject resource="|subject_storage_decryption_start|ASC.Web.Studio.Core.Notify.WebstudioNotifyPatternResource,ASC.Web.Studio" />
<body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_storage_decryption_start|ASC.Web.Studio.Core.Notify.WebstudioNotifyPatternResource,ASC.Web.Studio" />
<subject resource="|subject_storage_decryption_start|ASC.Web.Core.PublicResources.WebstudioNotifyPatternResource,ASC.Web.Core" />
<body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_storage_decryption_start|ASC.Web.Core.PublicResources.WebstudioNotifyPatternResource,ASC.Web.Core" />
</pattern>
<!-- decryption_success -->
<pattern id="storage_decryption_success">
<subject resource="|subject_storage_decryption_success|ASC.Web.Studio.Core.Notify.WebstudioNotifyPatternResource,ASC.Web.Studio" />
<body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_storage_decryption_success|ASC.Web.Studio.Core.Notify.WebstudioNotifyPatternResource,ASC.Web.Studio" />
<subject resource="|subject_storage_decryption_success|ASC.Web.Core.PublicResources.WebstudioNotifyPatternResource,ASC.Web.Core" />
<body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_storage_decryption_success|ASC.Web.Core.PublicResources.WebstudioNotifyPatternResource,ASC.Web.Core" />
</pattern>
<!-- decryption_error -->
<pattern id="storage_decryption_error">
<subject resource="|subject_storage_decryption_error|ASC.Web.Studio.Core.Notify.WebstudioNotifyPatternResource,ASC.Web.Studio" />
<body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_storage_decryption_error|ASC.Web.Studio.Core.Notify.WebstudioNotifyPatternResource,ASC.Web.Studio" />
<subject resource="|subject_storage_decryption_error|ASC.Web.Core.PublicResources.WebstudioNotifyPatternResource,ASC.Web.Core" />
<body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_storage_decryption_error|ASC.Web.Core.PublicResources.WebstudioNotifyPatternResource,ASC.Web.Core" />
</pattern>
</patterns>

View File

@ -54,6 +54,7 @@ namespace ASC.Web.Studio.Utility
private LicenseReader LicenseReader { get; }
private SetupInfo SetupInfo { get; }
private SettingsManager SettingsManager { get; }
private TenantControlPanelSettings TenantControlPanelSettings { get; }
public TenantExtra(
UserManager userManager,