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:
commit
0000129b3b
@ -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
|
||||
|
2
build/run/EncryptionService.bat
Normal file
2
build/run/EncryptionService.bat
Normal 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
|
@ -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" />
|
||||
|
@ -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"];
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
10
common/ASC.Core.Common/protos/NotifyInvoke.proto
Normal file
10
common/ASC.Core.Common/protos/NotifyInvoke.proto
Normal 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;
|
||||
}
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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).
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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}";
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
17
common/ASC.Data.Storage/protos/EncryptionSettingsProto.proto
Normal file
17
common/ASC.Data.Storage/protos/EncryptionSettingsProto.proto
Normal 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;
|
||||
}
|
7
common/ASC.Data.Storage/protos/EncryptionStop.proto
Normal file
7
common/ASC.Data.Storage/protos/EncryptionStop.proto
Normal file
@ -0,0 +1,7 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package ASC.Data.Storage.Encryption;
|
||||
|
||||
message EncryptionStop {
|
||||
|
||||
}
|
8
common/ASC.Data.Storage/protos/ProgressEncryption.proto
Normal file
8
common/ASC.Data.Storage/protos/ProgressEncryption.proto
Normal file
@ -0,0 +1,8 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package ASC.Data.Storage.Encryption;
|
||||
|
||||
message ProgressEncryption {
|
||||
double Progress = 1;
|
||||
int32 TenantId = 2;
|
||||
}
|
@ -506,6 +506,10 @@ namespace ASC.MessagingSystem
|
||||
|
||||
LicenseKeyUploaded = 5042,
|
||||
|
||||
StartStorageEncryption = 5050,
|
||||
|
||||
StartStorageDecryption = 5053,
|
||||
|
||||
#endregion
|
||||
|
||||
#region others
|
||||
|
@ -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">
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
50
common/services/ASC.Data.Backup/Listerners/BackupListener.cs
Normal file
50
common/services/ASC.Data.Backup/Listerners/BackupListener.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -394,7 +394,7 @@ namespace ASC.Data.Backup.Tasks
|
||||
{
|
||||
if (services.TryAddScoped<RestorePortalTask>())
|
||||
{
|
||||
services.TryAddScoped<AscCacheNotify>();
|
||||
services.TryAddSingleton<AscCacheNotify>();
|
||||
return services
|
||||
.AddCoreConfigurationService()
|
||||
.AddStorageFactoryService()
|
||||
|
@ -0,0 +1,7 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package ASC.Data.Backup.Contracts;
|
||||
|
||||
message DeleteSchedule {
|
||||
int32 TenantId = 1;
|
||||
}
|
@ -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>
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
115
common/services/ASC.Data.Storage.Encryption/EncryptionWorker.cs
Normal file
115
common/services/ASC.Data.Storage.Encryption/EncryptionWorker.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
116
common/services/ASC.Data.Storage.Encryption/NotifyHelper.cs
Normal file
116
common/services/ASC.Data.Storage.Encryption/NotifyHelper.cs
Normal 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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
73
common/services/ASC.Data.Storage.Encryption/Program.cs
Normal file
73
common/services/ASC.Data.Storage.Encryption/Program.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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/"
|
||||
}
|
||||
}
|
||||
}
|
60
common/services/ASC.Data.Storage.Encryption/Startup.cs
Normal file
60
common/services/ASC.Data.Storage.Encryption/Startup.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"pathToConf": "..\\..\\..\\config"
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,5 +122,11 @@
|
||||
},
|
||||
"bookmarking": {
|
||||
"thumbnail-url": "http://localhost:9800/?url={0}"
|
||||
}
|
||||
},
|
||||
"storage": {
|
||||
"encryption": {
|
||||
"progressfile": "false",
|
||||
"tempdir": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
15
web/ASC.Web.Api/Models/EncryptionSettingsModel.cs
Normal file
15
web/ASC.Web.Api/Models/EncryptionSettingsModel.cs
Normal 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; }
|
||||
}
|
||||
}
|
7
web/ASC.Web.Api/Models/StorageEncryptionModel.cs
Normal file
7
web/ASC.Web.Api/Models/StorageEncryptionModel.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace ASC.Web.Api.Models
|
||||
{
|
||||
public class StorageEncryptionModel
|
||||
{
|
||||
public bool NotifyUsers { get; set; }
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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>
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user