Merge branch 'master' into feature/advanced-selector-v2

# Conflicts:
#	web/ASC.Web.Client/yarn.lock
#	web/ASC.Web.Components/src/components/help-button/index.js
This commit is contained in:
Alexey Safronov 2019-11-27 14:14:07 +03:00
commit 506704c54a
662 changed files with 34382 additions and 14773 deletions

3
.gitignore vendored
View File

@ -7,8 +7,11 @@
**/node_modules/
**/storybook-static/
/web/ASC.Web.Components/dist
/web/ASC.Web.Common/dist
/build/deploy
*.log
/packages/asc-web-components
/packages/asc-web-common
/products/ASC.People/Data/
Data/
Logs/

View File

@ -4,6 +4,10 @@
"name": "ASC.Web.Components"
"path": "./web/ASC.Web.Components"
},
{
"name": "ASC.Web.Common"
"path": "./web/ASC.Web.Common"
},
{
"name": "ASC.Web.Client"
"path": "./web/ASC.Web.Client"

View File

@ -10,12 +10,19 @@ echo "ASC.Web.Components"
call yarn install --cwd web/ASC.Web.Components --frozen-lockfile > build\ASC.Web.Components.log
call yarn link --cwd packages/asc-web-components
echo "ASC.Web.Common"
call yarn link "asc-web-components" --cwd web/ASC.Web.Common
call yarn install --cwd web/ASC.Web.Common --frozen-lockfile > build\ASC.Web.Common.log
call yarn link --cwd packages/asc-web-common
echo "ASC.Web.Client"
call yarn link "asc-web-components" --cwd web/ASC.Web.Client
call yarn link "asc-web-common" --cwd web/ASC.Web.Client
call yarn install --cwd web/ASC.Web.Client --frozen-lockfile > build\ASC.Web.Client.log
echo "ASC.Web.People.Client"
call yarn link "asc-web-components" --cwd products/ASC.People/Client
call yarn link "asc-web-common" --cwd products/ASC.People/Client
call yarn install --cwd products/ASC.People/Client --frozen-lockfile > build\ASC.Web.People.Client.log
xcopy build\cra\*.* products\ASC.People\Client\node_modules\ /E /R /Y

View File

@ -31,6 +31,8 @@ RUN apt-get -y update && \
apt-get install -yq openjdk-8-jre-headless && \
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | apt-key add - && \
apt-get install -yq apt-transport-https && \
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF && \
echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | tee /etc/apt/sources.list.d/mono-official.list && \
echo "deb https://artifacts.elastic.co/packages/6.x/apt stable main" | tee -a /etc/apt/sources.list.d/elastic-6.x.list && \
apt-get -y update && \
apt-get install -yq elasticsearch=6.5.0 && \
@ -49,9 +51,24 @@ RUN apt-get -y update && \
tar xzf kafka_2.12-2.2.1.tgz && \
rm kafka_2.12-2.2.1.tgz && \
echo "#!/bin/sh\nexit 0" > /usr/sbin/policy-rc.d && \
apt-get install -yq dumb-init python-certbot-nginx htop nano dnsutils python3-pip multiarch-support iproute2 ffmpeg jq git yarn dotnet-sdk-3.0 supervisor mysql-client mysql-server
apt-get install -yq libgdiplus \
python-certbot-nginx \
htop \
nano \
dnsutils \
python3-pip \
multiarch-support \
iproute2 \
ffmpeg \
jq \
git \
yarn \
dotnet-sdk-3.0 \
supervisor \
mysql-client \
mysql-server
RUN git clone https://github.com/ONLYOFFICE/CommunityServer-AspNetCore.git /app/onlyoffice/src/
RUN git clone https://github.com/ONLYOFFICE/AppServer.git /app/onlyoffice/src/
RUN cd /app/onlyoffice/src/ && \
yarn install --cwd web/ASC.Web.Components --frozen-lockfile > build/ASC.Web.Components.log && \

View File

@ -4,20 +4,29 @@ call start\stop.bat
PUSHD %~dp0..
del /s /q packages\asc-web-components
del /s /q packages\asc-web-common
echo "ASC.Web.Components"
call yarn install --cwd web/ASC.Web.Components > build\ASC.Web.Components.log
REM xcopy web\ASC.Web.Components\node_modules packages\asc-web-components\node_modules\ /E /R /Y >> build\ASC.Web.Components.log
call yarn install --cwd packages/asc-web-components >> build\ASC.Web.Components.log
call yarn link --cwd packages/asc-web-components
echo "ASC.Web.Common"
call yarn link "asc-web-components" --cwd web/ASC.Web.Common
call yarn install --cwd web/ASC.Web.Common > build\ASC.Web.Common.log
REM xcopy web\ASC.Web.Common\node_modules packages\asc-web-common\node_modules\ /E /R /Y >> build\ASC.Web.Components.log
call yarn install --cwd packages/asc-web-common >> build\ASC.Web.Common.log
call yarn link --cwd packages/asc-web-common
echo "ASC.Web.Client"
call yarn link "asc-web-components" --cwd web/ASC.Web.Client
call yarn link "asc-web-common" --cwd web/ASC.Web.Client
call yarn install --cwd web/ASC.Web.Client > build\ASC.Web.Client.log
echo "ASC.Web.People.Client"
call yarn link "asc-web-components" --cwd products/ASC.People/Client
call yarn link "asc-web-common" --cwd products/ASC.People/Client
call yarn install --cwd products/ASC.People/Client > build\ASC.Web.People.Client.log
xcopy build\cra\*.* products\ASC.People\Client\node_modules\ /E /R /Y

View File

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

View File

@ -1,2 +1,2 @@
echo "RUN ASC.People"
call dotnet run --project ..\..\products\ASC.People\Server\ASC.People.csproj --no-build --$STORAGE_ROOT=..\..\..\Data
call dotnet run --project ..\..\products\ASC.People\Server\ASC.People.csproj --no-build --$STORAGE_ROOT=..\..\..\Data --log__dir=..\..\..\Logs --log__name=people

View File

@ -1,2 +1,2 @@
echo "RUN ASC.Studio.Notify"
call dotnet run --project ..\..\common\services\ASC.Studio.Notify\ASC.Studio.Notify.csproj --no-build --$STORAGE_ROOT=..\..\..\Data
call dotnet run --project ..\..\common\services\ASC.Studio.Notify\ASC.Studio.Notify.csproj --no-build --$STORAGE_ROOT=..\..\..\Data --log__dir=..\..\..\Logs --log__name=studio.notify

View File

@ -1,2 +1,2 @@
echo "RUN ASC.Web.Api"
call dotnet run --project ..\..\web\ASC.Web.Api\ASC.Web.Api.csproj --no-build --$STORAGE_ROOT=..\..\Data
call dotnet run --project ..\..\web\ASC.Web.Api\ASC.Web.Api.csproj --no-build --$STORAGE_ROOT=..\..\Data --log__dir=..\..\Logs --log__name=api

View File

@ -1,2 +1,2 @@
echo "RUN ASC.Web.Studio"
call dotnet run --project ..\..\web\ASC.Web.Studio\ASC.Web.Studio.csproj --no-build --$STORAGE_ROOT=..\..\Data
call dotnet run --project ..\..\web\ASC.Web.Studio\ASC.Web.Studio.csproj --no-build --$STORAGE_ROOT=..\..\Data --log__dir=..\..\Logs --log__name=studio

View File

@ -8,8 +8,9 @@ using System.Threading.Tasks;
using ASC.Core;
using ASC.Security.Cryptography;
using ASC.Web.Studio.Core;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -20,6 +21,36 @@ namespace ASC.Api.Core.Auth
public ConfirmAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
public ConfirmAuthHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
SecurityContext securityContext,
EmailValidationKeyProvider emailValidationKeyProvider,
SetupInfo setupInfo,
TenantManager tenantManager,
UserManager userManager,
AuthManager authManager,
AuthContext authContext) :
base(options, logger, encoder, clock)
{
SecurityContext = securityContext;
EmailValidationKeyProvider = emailValidationKeyProvider;
SetupInfo = setupInfo;
TenantManager = tenantManager;
UserManager = userManager;
AuthManager = authManager;
AuthContext = authContext;
}
public SecurityContext SecurityContext { get; }
public EmailValidationKeyProvider EmailValidationKeyProvider { get; }
public SetupInfo SetupInfo { get; }
public TenantManager TenantManager { get; }
public UserManager UserManager { get; }
public AuthManager AuthManager { get; }
public AuthContext AuthContext { get; }
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
@ -35,7 +66,7 @@ namespace ASC.Api.Core.Auth
EmailValidationKeyProvider.ValidationResult checkKeyResult;
try
{
checkKeyResult = emailValidationKeyModel.Validate();
checkKeyResult = emailValidationKeyModel.Validate(EmailValidationKeyProvider, AuthContext, TenantManager, UserManager, AuthManager);
}
catch (ArgumentNullException)
{
@ -53,7 +84,7 @@ namespace ASC.Api.Core.Auth
{
if (emailValidationKeyModel.UiD.HasValue && !emailValidationKeyModel.UiD.Equals(Guid.Empty))
{
SecurityContext.AuthenticateMe(CoreContext.TenantManager.GetCurrentTenant().TenantId, emailValidationKeyModel.UiD.Value, claims);
SecurityContext.AuthenticateMe(emailValidationKeyModel.UiD.Value, claims);
}
else
{
@ -75,4 +106,19 @@ namespace ASC.Api.Core.Auth
return Task.FromResult(result);
}
}
public static class ConfirmAuthHandlerExtension
{
public static IServiceCollection AddConfirmAuthHandler(this IServiceCollection services)
{
return services
.AddSecurityContextService()
.AddEmailValidationKeyProviderService()
.AddSetupInfo()
.AddTenantManagerService()
.AddUserManagerService()
.AddAuthManager()
.AddAuthContextService();
}
}
}

View File

@ -4,7 +4,7 @@ using System.Text.Encodings.Web;
using System.Threading.Tasks;
using ASC.Core;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -15,13 +15,15 @@ namespace ASC.Api.Core.Auth
public CookieAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
public CookieAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IHttpContextAccessor httpContextAccessor)
//
public CookieAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, SecurityContext securityContext)
: this(options, logger, encoder, clock)
{
SecurityContext = securityContext;
}
public SecurityContext SecurityContext { get; }
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var token = Context.Request.Cookies["asc_auth_key"] ?? Context.Request.Headers["Authorization"];
@ -34,4 +36,12 @@ namespace ASC.Api.Core.Auth
);
}
}
public static class CookieAuthHandlerExtension
{
public static IServiceCollection AddCookieAuthHandler(this IServiceCollection services)
{
return services.AddSecurityContextService();
}
}
}

View File

@ -30,6 +30,8 @@ using System.Security.Claims;
using ASC.Core;
using ASC.Core.Tenants;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Api.Core
{
@ -37,12 +39,12 @@ namespace ASC.Api.Core
{
public HttpContext HttpContext { get; set; }
private Tenant tenant;
public Tenant Tenant { get { return tenant ?? (tenant = CoreContext.TenantManager.GetCurrentTenant(HttpContext)); } }
public Tenant Tenant { get { return tenant ?? (tenant = TenantManager.GetCurrentTenant(HttpContext)); } }
public ApiContext(HttpContext httpContext)
public ApiContext(IHttpContextAccessor httpContextAccessor, SecurityContext securityContext, TenantManager tenantManager)
{
if (httpContext == null) return;
HttpContext = httpContext;
if (httpContextAccessor == null || httpContextAccessor.HttpContext == null) return;
HttpContext = httpContextAccessor.HttpContext;
Count = 0;
var query = HttpContext.Request.Query;
@ -80,6 +82,9 @@ namespace ASC.Api.Core
{
UpdatedSince = Convert.ToDateTime(updatedSince);
}
SecurityContext = securityContext;
TenantManager = tenantManager;
}
public string[] Fields { get; set; }
@ -192,6 +197,10 @@ namespace ASC.Api.Core
}
}
}
public SecurityContext SecurityContext { get; }
public TenantManager TenantManager { get; }
public ApiContext SetCount(int count)
{
HttpContext.Items[nameof(Count)] = count;
@ -214,14 +223,9 @@ namespace ASC.Api.Core
var id = HttpContext.User.Claims.FirstOrDefault(r => r.Type == ClaimTypes.Sid);
if (Guid.TryParse(id?.Value, out var userId))
{
_ = SecurityContext.AuthenticateMe(Tenant.TenantId, userId);
_ = SecurityContext.AuthenticateMe(userId);
}
}
public static implicit operator ApiContext(HttpContext httpContext)
{
return new ApiContext(httpContext);
}
}
public static class QueryExtension
@ -263,4 +267,17 @@ namespace ASC.Api.Core
return context == null || context.Fields == null || (context.Fields != null && context.Fields.Contains(field));
}
}
public static class ApiContextConfigExtension
{
public static IServiceCollection AddApiContextService(this IServiceCollection services)
{
services.TryAddScoped<ApiContext>();
return services
.AddTenantManagerService()
.AddHttpContextAccessor()
.AddSecurityContextService();
}
}
}

View File

@ -38,7 +38,7 @@ namespace ASC.Api.Core
[TypeConverter(typeof(ApiDateTimeTypeConverter))]
public class ApiDateTime : IComparable<ApiDateTime>, IComparable
{
private static readonly string[] Formats = new[]
internal static readonly string[] Formats = new[]
{
"o",
"yyyy'-'MM'-'dd'T'HH'-'mm'-'ss'.'fffffffK",
@ -50,19 +50,20 @@ namespace ASC.Api.Core
};
public ApiDateTime()
: this(null)
: this(null, null)
{
}
public ApiDateTime(DateTime? dateTime)
: this(dateTime, null)
public ApiDateTime(TenantManager tenantManager, DateTime? dateTime)
: this(tenantManager, dateTime, null)
{
}
public ApiDateTime(DateTime? dateTime, TimeZoneInfo timeZone)
public ApiDateTime(TenantManager tenantManager, DateTime? dateTime, TimeZoneInfo timeZone)
{
if (dateTime.HasValue && dateTime.Value > DateTime.MinValue && dateTime.Value < DateTime.MaxValue)
{
TenantManager = tenantManager;
SetDate(dateTime.Value, timeZone);
}
else
@ -78,12 +79,12 @@ namespace ASC.Api.Core
TimeZoneOffset = offset;
}
public static ApiDateTime Parse(string data)
public static ApiDateTime Parse(string data, TenantManager tenantManager)
{
return Parse(data, null);
return Parse(data, null, tenantManager);
}
public static ApiDateTime Parse(string data, TimeZoneInfo tz)
public static ApiDateTime Parse(string data, TimeZoneInfo tz, TenantManager tenantManager)
{
if (string.IsNullOrEmpty(data)) throw new ArgumentNullException("data");
@ -102,7 +103,7 @@ namespace ASC.Api.Core
{
if (tz == null)
{
tz = GetTimeZoneInfo();
tz = GetTimeZoneInfo(tenantManager);
}
tzOffset = tz.GetUtcOffset(dateTime);
dateTime = dateTime.Subtract(tzOffset);
@ -121,7 +122,7 @@ namespace ASC.Api.Core
if (timeZone == null)
{
timeZone = GetTimeZoneInfo();
timeZone = GetTimeZoneInfo(TenantManager);
}
//Hack
@ -148,12 +149,12 @@ namespace ASC.Api.Core
}
private static TimeZoneInfo GetTimeZoneInfo()
private static TimeZoneInfo GetTimeZoneInfo(TenantManager tenantManager)
{
var timeZone = TimeZoneInfo.Local;
try
{
timeZone = CoreContext.TenantManager.GetCurrentTenant().TimeZone;
timeZone = tenantManager.GetCurrentTenant().TimeZone;
}
catch (Exception)
{
@ -169,17 +170,17 @@ namespace ASC.Api.Core
return dateString + offsetString;
}
public static explicit operator ApiDateTime(DateTime d)
public static ApiDateTime FromDate(TenantManager tenantManager, DateTime d)
{
var date = new ApiDateTime(d);
var date = new ApiDateTime(tenantManager, d);
return date;
}
public static explicit operator ApiDateTime(DateTime? d)
public static ApiDateTime FromDate(TenantManager tenantManager, DateTime? d)
{
if (d.HasValue)
{
var date = new ApiDateTime(d);
var date = new ApiDateTime(tenantManager, d);
return date;
}
return null;
@ -235,7 +236,7 @@ namespace ASC.Api.Core
public int CompareTo(DateTime other)
{
return this.CompareTo(new ApiDateTime(other));
return CompareTo(new ApiDateTime(TenantManager, other));
}
public int CompareTo(ApiDateTime other)
@ -286,6 +287,7 @@ namespace ASC.Api.Core
public DateTime UtcTime { get; private set; }
public TimeSpan TimeZoneOffset { get; private set; }
public TenantManager TenantManager { get; }
public static ApiDateTime GetSample()
{
@ -306,11 +308,11 @@ namespace ASC.Api.Core
{
if (value is string)
{
return ApiDateTime.Parse((string)value);
return ApiDateTime.Parse((string)value, null);
}
if (value is DateTime)
{
return new ApiDateTime((DateTime)value);
return new ApiDateTime(null, (DateTime)value);
}
return base.ConvertFrom(context, culture, value);
}
@ -332,8 +334,14 @@ namespace ASC.Api.Core
{
if (reader.ValueType.Name == "String")
{
DateTime.TryParseExact(reader.Value?.ToString(), "o", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var result);
return new ApiDateTime(result, TimeSpan.Zero);
if (DateTime.TryParseExact(reader.Value?.ToString(), ApiDateTime.Formats, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var result))
{
return new ApiDateTime(result, TimeSpan.Zero);
}
else
{
return new ApiDateTime();
}
}
else if (reader.ValueType.Name == "DateTime")
{

View File

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using ASC.Core;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Api.Core.Middleware
{
@ -16,13 +17,13 @@ namespace ASC.Api.Core.Middleware
this.next = next;
}
public async Task Invoke(HttpContext context)
public async Task Invoke(HttpContext context, UserManager userManager, TenantManager tenantManager, AuthContext authContext)
{
CultureInfo culture = null;
if (SecurityContext.IsAuthenticated)
if (authContext.IsAuthenticated)
{
var user = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID);
var user = userManager.GetUsers(authContext.CurrentAccount.ID);
if (!string.IsNullOrEmpty(user.CultureName))
{
@ -32,7 +33,7 @@ namespace ASC.Api.Core.Middleware
if (culture == null)
{
culture = CoreContext.TenantManager.GetCurrentTenant().GetCulture();
culture = tenantManager.GetCurrentTenant().GetCulture();
}
Thread.CurrentThread.CurrentCulture = culture;
@ -48,5 +49,13 @@ namespace ASC.Api.Core.Middleware
{
return builder.UseMiddleware<CultureMiddleware>();
}
public static IServiceCollection AddCultureMiddleware(this IServiceCollection services)
{
return services
.AddUserManagerService()
.AddTenantManagerService()
.AddAuthContextService();
}
}
}

View File

@ -1,9 +1,12 @@
using System.Net;
using ASC.Common.Logging;
using ASC.Core;
using ASC.Core.Common.Settings;
using ASC.IPSecurity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace ASC.Api.Core.Middleware
{
@ -11,25 +14,43 @@ namespace ASC.Api.Core.Middleware
{
private readonly ILog log;
public IpSecurityFilter(LogManager logManager)
public IpSecurityFilter(
IOptionsMonitor<ILog> options,
AuthContext authContext,
IPSecurity.IPSecurity IPSecurity)
{
log = logManager.Get("Api");
log = options.CurrentValue;
AuthContext = authContext;
this.IPSecurity = IPSecurity;
}
public AuthContext AuthContext { get; }
public IPRestrictionsSettings IPRestrictionsSettings { get; }
public IPSecurity.IPSecurity IPSecurity { get; }
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
public void OnResourceExecuting(ResourceExecutingContext context)
{
var tenant = CoreContext.TenantManager.GetCurrentTenant(context.HttpContext);
var settings = IPRestrictionsSettings.LoadForTenant(tenant.TenantId);
if (settings.Enable && SecurityContext.IsAuthenticated && !IPSecurity.IPSecurity.Verify(context.HttpContext, tenant))
if (AuthContext.IsAuthenticated && !IPSecurity.Verify())
{
context.Result = new StatusCodeResult((int)HttpStatusCode.Forbidden);
log.WarnFormat("IPSecurity: Tenant {0}, user {1}", tenant.TenantId, SecurityContext.CurrentAccount.ID);
log.WarnFormat("IPSecurity: user {0}", AuthContext.CurrentAccount.ID);
return;
}
}
}
public static class IpSecurityFilterExtension
{
public static IServiceCollection AddIpSecurityFilter(this IServiceCollection services)
{
return services
.AddSettingsManagerService()
.AddAuthContextService()
.AddIPSecurityService();
}
}
}

View File

@ -3,10 +3,12 @@ using System.Net;
using System.Web;
using ASC.Common.Logging;
using ASC.Web.Api.Routing;
using ASC.Web.Studio.UserControls.Statistics;
using ASC.Web.Studio.Utility;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace ASC.Api.Core.Middleware
{
@ -14,11 +16,14 @@ namespace ASC.Api.Core.Middleware
{
private readonly ILog log;
public PaymentFilter(LogManager logManager)
public PaymentFilter(IOptionsMonitor<ILog> options, TenantExtra tenantExtra)
{
log = logManager.Get("Api");
log = options.CurrentValue;
TenantExtra = tenantExtra;
}
public TenantExtra TenantExtra { get; }
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
@ -34,7 +39,7 @@ namespace ASC.Api.Core.Middleware
var header = context.HttpContext.Request.Headers["Payment-Info"];
if (string.IsNullOrEmpty(header) || (bool.TryParse(header, out var flag) && flag))
{
if (TenantStatisticsProvider.IsNotPaid())
if (TenantExtra.IsNotPaid())
{
context.Result = new StatusCodeResult((int)HttpStatusCode.PaymentRequired);
log.WarnFormat("Payment Required {0}.", context.HttpContext.Request.Url());
@ -42,4 +47,13 @@ namespace ASC.Api.Core.Middleware
}
}
}
public static class PaymentFilterExtension
{
public static IServiceCollection AddPaymentFilter(this IServiceCollection services)
{
return services
.AddTenantExtraService();
}
}
}

View File

@ -10,6 +10,8 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace ASC.Api.Core.Middleware
{
@ -18,6 +20,10 @@ namespace ASC.Api.Core.Middleware
private static readonly IDictionary<string, Guid> products;
private readonly ILog log;
public UserManager UserManager { get; }
public TenantManager TenantManager { get; }
public WebItemSecurity WebItemSecurity { get; }
public AuthContext AuthContext { get; }
static ProductSecurityFilter()
{
@ -47,9 +53,18 @@ namespace ASC.Api.Core.Middleware
}
public ProductSecurityFilter(LogManager logManager)
public ProductSecurityFilter(
IOptionsMonitor<ILog> options,
UserManager userManager,
TenantManager tenantManager,
WebItemSecurity webItemSecurity,
AuthContext authContext)
{
log = logManager.Get("Api");
log = options.CurrentValue;
UserManager = userManager;
TenantManager = tenantManager;
WebItemSecurity = webItemSecurity;
AuthContext = authContext;
}
public void OnResourceExecuted(ResourceExecutedContext context)
@ -58,7 +73,7 @@ namespace ASC.Api.Core.Middleware
public void OnResourceExecuting(ResourceExecutingContext context)
{
if (!SecurityContext.IsAuthenticated) return;
if (!AuthContext.IsAuthenticated) return;
if (context.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
{
@ -69,10 +84,10 @@ namespace ASC.Api.Core.Middleware
{
CallContext.SetData("asc.web.product_id", pid);
}
if (!WebItemSecurity.IsAvailableForMe(CoreContext.TenantManager.GetCurrentTenant(context.HttpContext), pid))
if (!WebItemSecurity.IsAvailableForMe(pid))
{
context.Result = new StatusCodeResult((int)HttpStatusCode.Forbidden);
log.WarnFormat("Product {0} denied for user {1}", controllerActionDescriptor.ControllerName, SecurityContext.CurrentAccount);
log.WarnFormat("Product {0} denied for user {1}", controllerActionDescriptor.ControllerName, AuthContext.CurrentAccount);
}
}
}
@ -105,4 +120,16 @@ namespace ASC.Api.Core.Middleware
return default;
}
}
public static class ProductSecurityFilterExtension
{
public static IServiceCollection AddProductSecurityFilter(this IServiceCollection services)
{
return services
.AddUserManagerService()
.AddTenantManagerService()
.AddWebItemSecurity()
.AddAuthContextService();
}
}
}

View File

@ -4,6 +4,8 @@ using ASC.Core;
using ASC.Core.Tenants;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace ASC.Api.Core.Middleware
{
@ -11,18 +13,21 @@ namespace ASC.Api.Core.Middleware
{
private readonly ILog log;
public TenantStatusFilter(LogManager logManager)
public TenantStatusFilter(IOptionsMonitor<ILog> options, TenantManager tenantManager)
{
log = logManager.Get("Api");
log = options.CurrentValue;
TenantManager = tenantManager;
}
public TenantManager TenantManager { get; }
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
public void OnResourceExecuting(ResourceExecutingContext context)
{
var tenant = CoreContext.TenantManager.GetCurrentTenant(false);
var tenant = TenantManager.GetCurrentTenant(false);
if (tenant == null)
{
context.Result = new StatusCodeResult((int)HttpStatusCode.NotFound);
@ -38,4 +43,13 @@ namespace ASC.Api.Core.Middleware
}
}
}
public static class TenantStatusFilterExtension
{
public static IServiceCollection AddTenantStatusFilter(this IServiceCollection services)
{
return services
.AddTenantManagerService();
}
}
}

View File

@ -27,43 +27,17 @@
using System;
using System.Runtime.Serialization;
using ASC.Api.Core;
using ASC.Core;
using ASC.Core.Users;
using ASC.Web.Core.Users;
using ASC.Web.Studio.Utility;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Web.Api.Models
{
[DataContract(Name = "person", Namespace = "")]
public class EmployeeWraper
{
protected EmployeeWraper()
{
}
public EmployeeWraper(UserInfo userInfo, ApiContext httpContext)
{
Id = userInfo.ID;
DisplayName = DisplayUserSettings.GetFullUserName(userInfo);
if (!string.IsNullOrEmpty(userInfo.Title))
{
Title = userInfo.Title;
}
var userInfoLM = userInfo.LastModified.GetHashCode();
if (httpContext.Check("avatarSmall"))
{
AvatarSmall = UserPhotoManager.GetSmallPhotoURL(httpContext.Tenant.TenantId, userInfo.ID, out var isdef) + (isdef ? "" : $"?_={userInfoLM}");
}
if (Id != Guid.Empty)
{
var profileUrl = CommonLinkUtility.GetUserProfile(userInfo, false);
ProfileUrl = CommonLinkUtility.GetFullAbsolutePath(httpContext.HttpContext, profileUrl);
}
}
[DataMember(Order = 1)]
public Guid Id { get; set; }
@ -79,23 +53,6 @@ namespace ASC.Web.Api.Models
[DataMember(Order = 30)]
public string ProfileUrl { get; set; }
public static EmployeeWraper Get(Guid userId, ApiContext context)
{
try
{
return Get(CoreContext.UserManager.GetUsers(context.Tenant.TenantId, userId), context);
}
catch (Exception)
{
return Get(Constants.LostUser, context);
}
}
public static EmployeeWraper Get(UserInfo userInfo, ApiContext context)
{
return new EmployeeWraper(userInfo, context);
}
public static EmployeeWraper GetSample()
{
return new EmployeeWraper
@ -107,4 +64,65 @@ namespace ASC.Web.Api.Models
};
}
}
public class EmployeeWraperHelper
{
public UserInfo UserInfo { get; set; }
public ApiContext HttpContext { get; }
public DisplayUserSettingsHelper DisplayUserSettingsHelper { get; }
public UserPhotoManager UserPhotoManager { get; }
public CommonLinkUtility CommonLinkUtility { get; }
public EmployeeWraperHelper(ApiContext httpContext, DisplayUserSettingsHelper displayUserSettingsHelper, UserPhotoManager userPhotoManager, CommonLinkUtility commonLinkUtility)
{
HttpContext = httpContext;
DisplayUserSettingsHelper = displayUserSettingsHelper;
UserPhotoManager = userPhotoManager;
CommonLinkUtility = commonLinkUtility;
}
public EmployeeWraper Get(UserInfo userInfo)
{
return Init(new EmployeeWraper(), userInfo);
}
protected EmployeeWraper Init(EmployeeWraper result, UserInfo userInfo)
{
result.Id = userInfo.ID;
result.DisplayName = DisplayUserSettingsHelper.GetFullUserName(userInfo);
if (!string.IsNullOrEmpty(userInfo.Title))
{
result.Title = userInfo.Title;
}
var userInfoLM = userInfo.LastModified.GetHashCode();
if (HttpContext.Check("avatarSmall"))
{
result.AvatarSmall = UserPhotoManager.GetSmallPhotoURL(userInfo.ID, out var isdef) + (isdef ? "" : $"?_={userInfoLM}");
}
if (result.Id != Guid.Empty)
{
var profileUrl = CommonLinkUtility.GetUserProfile(userInfo, false);
result.ProfileUrl = CommonLinkUtility.GetFullAbsolutePath(profileUrl);
}
return result;
}
}
public static class EmployeeWraperExtension
{
public static IServiceCollection AddEmployeeWraper(this IServiceCollection services)
{
services.TryAddScoped<EmployeeWraperHelper>();
return services
.AddApiContextService()
.AddDisplayUserSettingsService()
.AddUserPhotoManagerService()
.AddCommonLinkUtilityService();
}
}
}

View File

@ -26,7 +26,6 @@
using System;
using System.Runtime.Serialization;
using ASC.Api.Core;
using ASC.Core;
using ASC.Core.Users;
@ -35,11 +34,11 @@ namespace ASC.Web.Api.Models
[DataContract(Name = "group", Namespace = "")]
public class GroupWrapperSummary
{
public GroupWrapperSummary(GroupInfo group, ApiContext context)
public GroupWrapperSummary(GroupInfo group, UserManager userManager)
{
Id = group.ID;
Name = group.Name;
Manager = CoreContext.UserManager.GetUsers(context.Tenant.TenantId, CoreContext.UserManager.GetDepartmentManager(context.Tenant.TenantId, group.ID)).UserName;
Manager = userManager.GetUsers(userManager.GetDepartmentManager(group.ID)).UserName;
}
protected GroupWrapperSummary()

View File

@ -32,10 +32,10 @@
<PackageReference Include="ARSoft.Tools.NetStandard.DXSdata" Version="1.0.0" />
<PackageReference Include="Autofac" Version="4.9.4" />
<PackageReference Include="Autofac.Configuration" Version="4.1.0" />
<PackageReference Include="Confluent.Kafka" Version="1.2.0" />
<PackageReference Include="Google.Protobuf" Version="3.10.0-rc1" />
<PackageReference Include="Grpc" Version="2.24.0-pre1" />
<PackageReference Include="Grpc.Tools" Version="2.24.0-pre1">
<PackageReference Include="Confluent.Kafka" Version="1.2.1" />
<PackageReference Include="Google.Protobuf" Version="3.10.1" />
<PackageReference Include="Grpc" Version="2.25.0" />
<PackageReference Include="Grpc.Tools" Version="2.25.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
@ -47,10 +47,18 @@
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.0.0" />
<PackageReference Include="Microsoft.Windows.Compatibility" Version="3.0.0" />
<PackageReference Include="MySql.Data" Version="8.0.17" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3-beta1" />
<PackageReference Include="NLog" Version="4.6.7" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.8.5" />
<!-- <PackageReference Include="Microsoft.CodeQuality.Analyzers" Version="2.9.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> -->
<!-- <PackageReference Include="Microsoft.NetCore.Analyzers" Version="2.9.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> -->
<PackageReference Include="MySql.Data" Version="8.0.18" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NLog" Version="4.6.8" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.9.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NVelocity" Version="1.2.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />

View File

@ -32,12 +32,37 @@ using System.Text.RegularExpressions;
using Google.Protobuf;
namespace ASC.Common.Caching
{
{
public class AscCacheNotify
{
public ICacheNotify<AscCacheItem> CacheNotify { get; }
public AscCacheNotify(ICacheNotify<AscCacheItem> cacheNotify)
{
CacheNotify = cacheNotify;
CacheNotify.Subscribe((item) => { OnClearCache(); }, CacheNotifyAction.Any);
}
public void ClearCache()
{
CacheNotify.Publish(new AscCacheItem { Id = ByteString.CopyFrom(Guid.NewGuid().ToByteArray()) }, CacheNotifyAction.Any);
}
private static void OnClearCache()
{
var keys = MemoryCache.Default.Select(r => r.Key).ToList();
foreach (var k in keys)
{
_ = MemoryCache.Default.Remove(k);
}
}
}
public class AscCache : ICache
{
public static readonly ICache Memory;
public readonly ICacheNotify<AscCacheItem> KafkaNotify;
static AscCache()
{
@ -45,17 +70,7 @@ namespace ASC.Common.Caching
}
private AscCache()
{
KafkaNotify = new KafkaCache<AscCacheItem>();
try
{
KafkaNotify.Subscribe((item) => { OnClearCache(); }, CacheNotifyAction.Any);
}
catch (Exception)
{
}
{
}
public T Get<T>(string key) where T : class
@ -142,24 +157,9 @@ namespace ASC.Common.Caching
}
}
public void ClearCache()
{
KafkaNotify.Publish(new AscCacheItem() { Id = ByteString.CopyFrom(Guid.NewGuid().ToByteArray()) }, CacheNotifyAction.Any);
}
private MemoryCache GetCache()
{
return MemoryCache.Default;
}
private static void OnClearCache()
{
var keys = MemoryCache.Default.Select(r => r.Key).ToList();
foreach (var k in keys)
{
MemoryCache.Default.Remove(k);
}
}
}
}

View File

@ -3,12 +3,13 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ASC.Common.Logging;
using ASC.Common.Utils;
using Confluent.Kafka;
using Google.Protobuf;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
namespace ASC.Common.Caching
{
@ -24,26 +25,20 @@ namespace ASC.Common.Caching
private ProtobufDeserializer<T> ValueDeserializer { get; } = new ProtobufDeserializer<T>();
private ProtobufSerializer<AscCacheItem> KeySerializer { get; } = new ProtobufSerializer<AscCacheItem>();
private ProtobufDeserializer<AscCacheItem> KeyDeserializer { get; } = new ProtobufDeserializer<AscCacheItem>();
private IProducer<AscCacheItem, T> Producer { get; }
private IProducer<AscCacheItem, T> Producer { get; set; }
private Guid Key { get; set; }
public KafkaCache()
public KafkaCache(IConfiguration configuration, IOptionsMonitor<ILog> options)
{
Log = LogManager.GetLogger("ASC");
Log = options.CurrentValue;
Cts = new ConcurrentDictionary<string, CancellationTokenSource>();
Actions = new ConcurrentDictionary<string, Action<T>>();
Key = Guid.NewGuid();
var settings = ConfigurationManager.GetSetting<KafkaSettings>("kafka");
var settings = configuration.GetSetting<KafkaSettings>("kafka");
if (settings != null && !string.IsNullOrEmpty(settings.BootstrapServers))
{
ClientConfig = new ClientConfig { BootstrapServers = settings.BootstrapServers };
var config = new ProducerConfig(ClientConfig);
Producer = new ProducerBuilder<AscCacheItem, T>(config)
.SetErrorHandler((_, e) => Log.Error(e))
.SetKeySerializer(KeySerializer)
.SetValueSerializer(ValueSerializer)
.Build();
}
else
{
@ -62,6 +57,15 @@ namespace ASC.Common.Caching
try
{
if (Producer == null)
{
Producer = new ProducerBuilder<AscCacheItem, T>(new ProducerConfig(ClientConfig))
.SetErrorHandler((_, e) => Log.Error(e))
.SetKeySerializer(KeySerializer)
.SetValueSerializer(ValueSerializer)
.Build();
}
var channelName = GetChannelName(cacheNotifyAction);
if (Actions.TryGetValue(channelName, out var onchange))
@ -171,7 +175,7 @@ namespace ASC.Common.Caching
{
if (!disposedValue)
{
if (disposing)
if (disposing && Producer != null)
{
Producer.Dispose();
}

View File

@ -30,7 +30,7 @@ using System.Data.Common;
namespace ASC.Common.Data.AdoProxy
{
class ProxyContext
internal class ProxyContext
{
private readonly Action<ExecutedEventArgs> executedEvent;

View File

@ -29,24 +29,110 @@ using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Threading.Tasks;
using ASC.Common.Data.AdoProxy;
using ASC.Common.Data.Sql;
using ASC.Common.Logging;
using ASC.Common.Web;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace ASC.Common.Data
{
public class DbOptionsManager : OptionsManager<DbManager>, IDisposable
{
private Dictionary<string, DbManager> Pairs { get; set; }
public DbOptionsManager(IOptionsFactory<DbManager> factory) : base(factory)
{
Pairs = new Dictionary<string, DbManager>();
}
public override DbManager Get(string name)
{
var result = base.Get(name);
if (!Pairs.ContainsKey(name))
{
Pairs.Add(name, result);
}
return result;
}
public void Dispose()
{
foreach (var v in Pairs)
{
v.Value.Dispose();
}
}
}
public class ConfigureDbManager : IConfigureNamedOptions<DbManager>
{
private DbRegistry DbRegistry { get; }
private IOptionsMonitor<ILog> Option { get; }
private IHttpContextAccessor HttpContextAccessor { get; }
public ConfigureDbManager(DbRegistry dbRegistry, IOptionsMonitor<ILog> option)
{
DbRegistry = dbRegistry;
Option = option;
}
public ConfigureDbManager(DbRegistry dbRegistry, IOptionsMonitor<ILog> option, IHttpContextAccessor httpContextAccessor) : this(dbRegistry, option)
{
HttpContextAccessor = httpContextAccessor;
}
public void Configure(string name, DbManager dbManager)
{
dbManager.DbRegistry = DbRegistry;
dbManager.DatabaseId = string.IsNullOrEmpty(name) ? "default" : name;
dbManager.Logger = Option.Get("ASC.SQL");
if (dbManager.Logger.IsDebugEnabled)
{
dbManager.ProxyContext = new ProxyContext(a =>
{
dbManager.Logger.DebugWithProps(a.SqlMethod,
new KeyValuePair<string, object>("duration", a.Duration.TotalMilliseconds),
new KeyValuePair<string, object>("sql", RemoveWhiteSpaces(a.Sql)),
new KeyValuePair<string, object>("sqlParams", RemoveWhiteSpaces(a.SqlParameters))
);
});
}
if (HttpContextAccessor != null)
{
dbManager.HttpContextAccessor = HttpContextAccessor;
}
}
public void Configure(DbManager dbManager)
{
Configure("default", dbManager);
}
private string RemoveWhiteSpaces(string str)
{
return !string.IsNullOrEmpty(str) ?
str.Replace(Environment.NewLine, " ").Replace("\n", "").Replace("\r", "").Replace("\t", " ") :
string.Empty;
}
}
public class DbManager : IDbManager
{
private readonly ILog logger = LogManager.GetLogger("ASC.SQL");
private readonly ProxyContext proxyContext;
private readonly bool shared;
public ILog Logger { get; internal set; }
public IHttpContextAccessor HttpContextAccessor { get; internal set; }
internal ProxyContext ProxyContext { get; set; }
private DbCommand command;
private ISqlDialect dialect;
private volatile bool disposed;
private readonly int? commandTimeout;
public int? CommandTimeout { get; set; }
private DbCommand Command
{
@ -62,16 +148,20 @@ namespace ASC.Common.Data
command = OpenConnection().CreateCommand();
}
if (commandTimeout.HasValue)
if (CommandTimeout.HasValue)
{
command.CommandTimeout = commandTimeout.Value;
command.CommandTimeout = CommandTimeout.Value;
}
return command;
}
}
public string DatabaseId { get; private set; }
public string DatabaseId
{
get;
set;
}
public bool InTransaction
{
@ -83,26 +173,11 @@ namespace ASC.Common.Data
get { return Command.Connection; }
}
public DbRegistry DbRegistry { get; internal set; }
public DbManager(string databaseId, int? commandTimeout = null)
: this(databaseId, true, commandTimeout)
public DbManager()
{
}
public DbManager(string databaseId, bool shared, int? commandTimeout = null)
{
DatabaseId = databaseId ?? throw new ArgumentNullException(nameof(databaseId));
this.shared = shared;
if (logger.IsDebugEnabled)
{
proxyContext = new ProxyContext(AdoProxyExecutedEventHandler);
}
if (commandTimeout.HasValue)
{
this.commandTimeout = commandTimeout;
}
}
#region IDisposable Members
@ -124,22 +199,6 @@ namespace ASC.Common.Data
#endregion
public static IDbManager FromHttpContext(string databaseId)
{
if (HttpContext.Current != null)
{
if (!(DisposableHttpContext.Current[databaseId] is DbManager dbManager) || dbManager.disposed)
{
var localDbManager = new DbManager(databaseId);
var dbManagerAdapter = new DbManagerProxy(localDbManager);
DisposableHttpContext.Current[databaseId] = localDbManager;
return dbManagerAdapter;
}
return new DbManagerProxy(dbManager);
}
return new DbManager(databaseId);
}
private DbConnection OpenConnection()
{
var connection = GetConnection();
@ -150,12 +209,12 @@ namespace ASC.Common.Data
private DbConnection GetConnection()
{
CheckDispose();
string key = null;
DbConnection connection;
if (shared && HttpContext.Current != null)
string key = null;
if (HttpContextAccessor?.HttpContext != null)
{
key = string.Format("Connection {0}|{1}", GetDialect(), DbRegistry.GetConnectionString(DatabaseId));
connection = DisposableHttpContext.Current[key] as DbConnection;
connection = HttpContextAccessor.HttpContext.Items[key] as DbConnection;
if (connection != null)
{
var state = ConnectionState.Closed;
@ -179,11 +238,11 @@ namespace ASC.Common.Data
}
}
connection = DbRegistry.CreateDbConnection(DatabaseId);
if (proxyContext != null)
if (ProxyContext != null)
{
connection = new DbConnectionProxy(connection, proxyContext);
connection = new DbConnectionProxy(connection, ProxyContext);
}
if (shared && HttpContext.Current != null) DisposableHttpContext.Current[key] = connection;
if (HttpContextAccessor?.HttpContext != null) HttpContextAccessor.HttpContext.Items[key] = connection;
return connection;
}
@ -304,107 +363,21 @@ namespace ASC.Common.Data
return dialect ?? (dialect = DbRegistry.GetSqlDialect(DatabaseId));
}
private void AdoProxyExecutedEventHandler(ExecutedEventArgs a)
public ISqlDialect GetSqlDialect(string databaseId)
{
logger.DebugWithProps(a.SqlMethod,
new KeyValuePair<string, object>("duration", a.Duration.TotalMilliseconds),
new KeyValuePair<string, object>("sql", RemoveWhiteSpaces(a.Sql)),
new KeyValuePair<string, object>("sqlParams", RemoveWhiteSpaces(a.SqlParameters))
);
}
private string RemoveWhiteSpaces(string str)
{
return !string.IsNullOrEmpty(str) ?
str.Replace(Environment.NewLine, " ").Replace("\n", "").Replace("\r", "").Replace("\t", " ") :
string.Empty;
return DbRegistry.GetSqlDialect(databaseId);
}
}
public class DbManagerProxy : IDbManager
public static class DbManagerExtension
{
private DbManager dbManager { get; set; }
public DbManagerProxy(DbManager dbManager)
public static IServiceCollection AddDbManagerService(this IServiceCollection services)
{
this.dbManager = dbManager;
}
services.TryAddScoped<DbOptionsManager>();
services.TryAddScoped<DbManager>();
services.AddScoped<IConfigureOptions<DbManager>, ConfigureDbManager>();
public void Dispose()
{
if (HttpContext.Current == null)
{
dbManager.Dispose();
}
}
public DbConnection Connection { get { return dbManager.Connection; } }
public string DatabaseId { get { return dbManager.DatabaseId; } }
public bool InTransaction { get { return dbManager.InTransaction; } }
public IDbTransaction BeginTransaction()
{
return dbManager.BeginTransaction();
}
public IDbTransaction BeginTransaction(IsolationLevel isolationLevel)
{
return dbManager.BeginTransaction(isolationLevel);
}
public IDbTransaction BeginTransaction(bool nestedIfAlreadyOpen)
{
return dbManager.BeginTransaction(nestedIfAlreadyOpen);
}
public List<object[]> ExecuteList(string sql, params object[] parameters)
{
return dbManager.ExecuteList(sql, parameters);
}
public List<object[]> ExecuteList(ISqlInstruction sql)
{
return dbManager.ExecuteList(sql);
}
public Task<List<object[]>> ExecuteListAsync(ISqlInstruction sql)
{
return dbManager.ExecuteListAsync(sql);
}
public List<T> ExecuteList<T>(ISqlInstruction sql, Converter<IDataRecord, T> converter)
{
return dbManager.ExecuteList(sql, converter);
}
public List<T> ExecuteList<T>(ISqlInstruction sql, Converter<object[], T> converter)
{
return dbManager.ExecuteList<T>(sql, converter);
}
public T ExecuteScalar<T>(string sql, params object[] parameters)
{
return dbManager.ExecuteScalar<T>(sql, parameters);
}
public T ExecuteScalar<T>(ISqlInstruction sql)
{
return dbManager.ExecuteScalar<T>(sql);
}
public int ExecuteNonQuery(string sql, params object[] parameters)
{
return dbManager.ExecuteNonQuery(sql, parameters);
}
public int ExecuteNonQuery(ISqlInstruction sql)
{
return dbManager.ExecuteNonQuery(sql);
}
public int ExecuteBatch(IEnumerable<ISqlInstruction> batch)
{
return dbManager.ExecuteBatch(batch);
return services.AddDbRegistryService();
}
}
}

View File

@ -30,6 +30,10 @@ using System.Configuration;
using System.Data.Common;
using ASC.Common.Data.Sql;
using ASC.Common.Data.Sql.Dialects;
using ASC.Common.Utils;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Common.Data
{
@ -41,22 +45,25 @@ namespace ASC.Common.Data
public string Type { get; set; }
}
public static class DbRegistry
public class DbRegistry
{
private const string DEFAULT = "DEFAULT";
private static readonly object syncRoot = new object();
private static readonly IDictionary<string, DbProviderFactory> providers = new Dictionary<string, DbProviderFactory>(StringComparer.InvariantCultureIgnoreCase);
private static readonly IDictionary<string, string> connnectionStrings = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
private static readonly IDictionary<string, ISqlDialect> dialects = new Dictionary<string, ISqlDialect>(StringComparer.InvariantCultureIgnoreCase);
private static volatile bool configured = false;
private readonly IDictionary<string, DbProviderFactory> providers = new Dictionary<string, DbProviderFactory>(StringComparer.InvariantCultureIgnoreCase);
private readonly IDictionary<string, string> connnectionStrings = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
private readonly IDictionary<string, ISqlDialect> dialects = new Dictionary<string, ISqlDialect>(StringComparer.InvariantCultureIgnoreCase);
static DbRegistry()
public IConfiguration Configuration { get; }
public DbRegistry(IConfiguration configuration)
{
dialects["MySql.Data.MySqlClient.MySqlClientFactory"] = new MySQLDialect();
dialects["System.Data.SQLite.SQLiteFactory"] = new SQLiteDialect();
Configuration = configuration;
Configure();
}
internal static void RegisterDatabase(string databaseId, DbProviderFactory providerFactory, string connectionString)
internal void RegisterDatabase(string databaseId, DbProviderFactory providerFactory, string connectionString)
{
if (string.IsNullOrEmpty(databaseId)) throw new ArgumentNullException(nameof(databaseId));
if (providerFactory == null) throw new ArgumentNullException(nameof(providerFactory));
@ -77,17 +84,17 @@ namespace ASC.Common.Data
}
}
internal static void RegisterDatabase(string databaseId, string providerInvariantName, string connectionString)
internal void RegisterDatabase(string databaseId, string providerInvariantName, string connectionString)
{
RegisterDatabase(databaseId, DbProviderFactories.GetFactory(providerInvariantName), connectionString);
}
public static void RegisterDatabase(string databaseId, ConnectionStringSettings connectionString)
public void RegisterDatabase(string databaseId, ConnectionStringSettings connectionString)
{
RegisterDatabase(databaseId, connectionString.ProviderName, connectionString.ConnectionString);
}
public static void UnRegisterDatabase(string databaseId)
public void UnRegisterDatabase(string databaseId)
{
if (string.IsNullOrEmpty(databaseId)) throw new ArgumentNullException(nameof(databaseId));
@ -114,7 +121,7 @@ namespace ASC.Common.Data
}
}
public static bool IsDatabaseRegistered(string databaseId)
public bool IsDatabaseRegistered(string databaseId)
{
lock (syncRoot)
{
@ -122,10 +129,8 @@ namespace ASC.Common.Data
}
}
public static DbConnection CreateDbConnection(string databaseId)
public DbConnection CreateDbConnection(string databaseId)
{
Configure();
if (!providers.ContainsKey(databaseId))
{
databaseId = DEFAULT;
@ -139,10 +144,8 @@ namespace ASC.Common.Data
return connection;
}
public static DbProviderFactory GetDbProviderFactory(string databaseId)
public DbProviderFactory GetDbProviderFactory(string databaseId)
{
Configure();
if (!providers.ContainsKey(databaseId))
{
databaseId = DEFAULT;
@ -150,10 +153,8 @@ namespace ASC.Common.Data
return providers.ContainsKey(databaseId) ? providers[databaseId] : null;
}
public static ConnectionStringSettings GetConnectionString(string databaseId)
public ConnectionStringSettings GetConnectionString(string databaseId)
{
Configure();
if (!connnectionStrings.ContainsKey(databaseId))
{
databaseId = DEFAULT;
@ -161,7 +162,7 @@ namespace ASC.Common.Data
return connnectionStrings.ContainsKey(databaseId) ? new ConnectionStringSettings(databaseId, connnectionStrings[databaseId], providers[databaseId].GetType().Name) : null;
}
public static ISqlDialect GetSqlDialect(string databaseId)
public ISqlDialect GetSqlDialect(string databaseId)
{
var provider = GetDbProviderFactory(databaseId);
if (provider != null && dialects.ContainsKey(provider.GetType().FullName))
@ -172,35 +173,35 @@ namespace ASC.Common.Data
}
public static void Configure()
{
if (!configured)
private void Configure()
{
foreach (var cs in Configuration.GetSettings<DbProviderFactoryCustom>("DbProviderFactories"))
{
DbProviderFactories.RegisterFactory(cs.Invariant, cs.Type);
}
var factories = DbProviderFactories.GetFactoryClasses();
AppDomain.CurrentDomain.SetData("DataDirectory", AppDomain.CurrentDomain.BaseDirectory); //SQLite
foreach (var cs in Configuration.GetConnectionStrings())
{
lock (syncRoot)
var factory = factories.Rows.Find(cs.ProviderName);
if (factory == null)
{
if (!configured)
{
foreach (var cs in Utils.ConfigurationManager.GetSettings<DbProviderFactoryCustom>("DbProviderFactories"))
{
DbProviderFactories.RegisterFactory(cs.Invariant, cs.Type);
}
var factories = DbProviderFactories.GetFactoryClasses();
AppDomain.CurrentDomain.SetData("DataDirectory", AppDomain.CurrentDomain.BaseDirectory); //SQLite
foreach (var cs in Utils.ConfigurationManager.ConnectionStrings)
{
var factory = factories.Rows.Find(cs.ProviderName);
if (factory == null)
{
throw new ConfigurationErrorsException("Db factory " + cs.ProviderName + " not found.");
}
RegisterDatabase(cs.Name, cs);
}
configured = true;
}
throw new ConfigurationErrorsException("Db factory " + cs.ProviderName + " not found.");
}
RegisterDatabase(cs.Name, cs);
}
}
}
public static class DbRegistryExtension
{
public static IServiceCollection AddDbRegistryService(this IServiceCollection services)
{
services.TryAddSingleton<DbRegistry>();
return services;
}
}
}

View File

@ -37,7 +37,8 @@ namespace ASC.Common.Data
{
DbConnection Connection { get; }
string DatabaseId { get; }
bool InTransaction { get; }
bool InTransaction { get; }
ISqlDialect GetSqlDialect(string databaseId);
IDbTransaction BeginTransaction();

View File

@ -31,7 +31,9 @@ using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
using ASC.Common.Data.Sql;
using ASC.Common.Utils;
using Microsoft.Extensions.Configuration;
namespace ASC.Common.Data
{
public class MultiRegionalDbManager : IDbManager
@ -52,17 +54,14 @@ namespace ASC.Common.Data
}
public MultiRegionalDbManager(string dbId)
public MultiRegionalDbManager(IConfiguration configuration, DbOptionsManager optionsManager, string dbId)
{
const StringComparison cmp = StringComparison.InvariantCultureIgnoreCase;
DatabaseId = dbId;
databases = Utils.ConfigurationManager.ConnectionStrings
databases = configuration.GetConnectionStrings()
.Where(c => c.Name.Equals(dbId, cmp) || c.Name.StartsWith(dbId + ".", cmp))
.Select(
c =>
HttpContext.Current != null
? DbManager.FromHttpContext(c.Name)
: new DbManager(c.Name))
.Select(c => optionsManager.Get(c.Name))
.Cast<IDbManager>()
.ToList();
localDb = databases.SingleOrDefault(db => db.DatabaseId.Equals(dbId, cmp));
}
@ -83,11 +82,6 @@ namespace ASC.Common.Data
}
}
public static MultiRegionalDbManager FromHttpContext(string databaseId)
{
return new MultiRegionalDbManager(databaseId);
}
public IDbTransaction BeginTransaction(IsolationLevel isolationLevel)
{
return localDb.BeginTransaction(isolationLevel);
@ -178,5 +172,10 @@ namespace ASC.Common.Data
{
return localDb.BeginTransaction();
}
public ISqlDialect GetSqlDialect(string databaseId)
{
return localDb.GetSqlDialect(databaseId);
}
}
}

View File

@ -8,6 +8,7 @@ using Autofac;
using Autofac.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Common.DependencyInjection
{
@ -73,7 +74,7 @@ namespace ASC.Common.DependencyInjection
var container = builder.Build();
services.AddSingleton(container);
services.TryAddSingleton(container);
return container;

View File

@ -1,22 +0,0 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Common.DependencyInjection
{
public static class CommonServiceProvider
{
public static T GetService<T>() => ServiceProvider.GetService<T>();
private static IServiceProvider ServiceProvider { get; set; }
public static void Init(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public static void UseCSP(this IApplicationBuilder applicationBuilder)
{
Init(applicationBuilder.ApplicationServices);
}
}
}

View File

@ -29,19 +29,22 @@ using System.Linq;
using ASC.Common.Data;
using ASC.Common.Data.Sql;
using ASC.Common.Data.Sql.Expressions;
using ASC.Common.Logging;
using ASC.Common.Logging;
using Microsoft.Extensions.Options;
namespace ASC.Geolocation
{
public class GeolocationHelper
{
private static readonly ILog log = LogManager.GetLogger("ASC.Geo");
private readonly string dbid;
public GeolocationHelper(string dbid)
{
private readonly string dbid;
public ILog Log { get; }
public DbOptionsManager DbOptions { get; }
public GeolocationHelper(DbOptionsManager dbOptions, IOptionsMonitor<ILog> option, string dbid)
{
Log = option.CurrentValue;
DbOptions = dbOptions;
this.dbid = dbid;
}
@ -51,7 +54,7 @@ namespace ASC.Geolocation
try
{
var ipformatted = FormatIP(ip);
using var db = new DbManager(dbid);
var db = DbOptions.Get(dbid);
var q = new SqlQuery("dbip_location")
.Select("ip_start", "ip_end", "country", "city", "timezone_offset", "timezone_name")
.Where(Exp.Le("ip_start", ipformatted))
@ -73,16 +76,11 @@ namespace ASC.Geolocation
}
catch (Exception error)
{
log.Error(error);
Log.Error(error);
}
return IPGeolocationInfo.Default;
}
public IPGeolocationInfo GetIPGeolocationFromHttpContext()
{
return GetIPGeolocationFromHttpContext(Common.HttpContext.Current);
}
public IPGeolocationInfo GetIPGeolocationFromHttpContext(Microsoft.AspNetCore.Http.HttpContext context)
{
if (context != null && context.Request != null)

View File

@ -25,14 +25,16 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using ASC.Common.Utils;
using Autofac;
using log4net.Config;
using log4net.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using NLog;
namespace ASC.Common.Logging
@ -91,7 +93,8 @@ namespace ASC.Common.Logging
void FatalFormat(string format, object arg0, object arg1, object arg2);
void FatalFormat(IFormatProvider provider, string format, params object[] args);
string LogDirectory { get; }
string LogDirectory { get; }
string Name { get; set; }
}
public class Log : ILog
@ -334,13 +337,70 @@ namespace ASC.Common.Logging
{
return log4net.GlobalContext.Properties["LogDirectory"].ToString();
}
}
}
public string Name
{
get;
set;
}
}
public class NLogSettings
{
public string Name { get; set; }
public string Dir { get; set; }
}
public class ConfigureLogNLog : IConfigureOptions<LogNLog>
{
public ConfigureLogNLog(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void Configure(LogNLog options)
{
LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(Path.Combine(Configuration["pathToConf"], "nlog.config"), true);
var settings = Configuration.GetSetting<NLogSettings>("log");
if (!string.IsNullOrEmpty(settings.Name))
{
LogManager.Configuration.Variables["name"] = settings.Name;
}
if (!string.IsNullOrEmpty(settings.Dir))
{
LogManager.Configuration.Variables["dir"] = settings.Dir.TrimEnd('/').TrimEnd('\\') + Path.DirectorySeparatorChar;
}
NLog.Targets.Target.Register<SelfCleaningTarget>("SelfCleaning");
}
}
public class LogNLog : ILog
{
private readonly NLog.ILogger loger;
private readonly string name;
private NLog.ILogger loger;
private NLog.ILogger Loger
{
get
{
return loger;
}
set
{
loger = value;
IsDebugEnabled = loger.IsDebugEnabled;
IsInfoEnabled = loger.IsInfoEnabled;
IsWarnEnabled = loger.IsWarnEnabled;
IsErrorEnabled = loger.IsErrorEnabled;
IsFatalEnabled = loger.IsFatalEnabled;
IsTraceEnabled = loger.IsEnabled(LogLevel.Trace);
}
}
public bool IsDebugEnabled { get; private set; }
@ -352,240 +412,225 @@ namespace ASC.Common.Logging
public bool IsFatalEnabled { get; private set; }
public bool IsTraceEnabled { get; private set; }
static LogNLog()
{
var args = Environment.GetCommandLineArgs();
for (var i = 0; i < args.Length; i++)
{
if (args[i] == "--log" && !string.IsNullOrEmpty(args[i + 1]))
{
NLog.LogManager.Configuration.Variables["svcName"] = args[i + 1].Trim().Trim('"');
}
}
NLog.Targets.Target.Register<SelfCleaningTarget>("SelfCleaning");
}
public LogNLog(string name)
{
this.name = name;
loger = NLog.LogManager.GetLogger(name);
IsDebugEnabled = loger.IsDebugEnabled;
IsInfoEnabled = loger.IsInfoEnabled;
IsWarnEnabled = loger.IsWarnEnabled;
IsErrorEnabled = loger.IsErrorEnabled;
IsFatalEnabled = loger.IsFatalEnabled;
IsTraceEnabled = loger.IsEnabled(LogLevel.Trace);
}
public bool IsTraceEnabled { get; private set; }
public void Trace(object message)
{
if (IsTraceEnabled) loger.Log(LogLevel.Trace, message);
if (IsTraceEnabled) Loger.Log(LogLevel.Trace, message);
}
public void TraceFormat(string message, object arg0)
{
if (IsTraceEnabled) loger.Log(LogLevel.Trace, string.Format(message, arg0));
if (IsTraceEnabled) Loger.Log(LogLevel.Trace, string.Format(message, arg0));
}
public void Debug(object message)
{
if (IsDebugEnabled) loger.Debug(message);
if (IsDebugEnabled) Loger.Debug(message);
}
public void Debug(object message, Exception exception)
{
if (IsDebugEnabled) loger.Debug(exception, "{0}", message);
if (IsDebugEnabled) Loger.Debug(exception, "{0}", message);
}
public void DebugFormat(string format, params object[] args)
{
if (IsDebugEnabled) loger.Debug(format, args);
if (IsDebugEnabled) Loger.Debug(format, args);
}
public void DebugFormat(string format, object arg0)
{
if (IsDebugEnabled) loger.Debug(format, arg0);
if (IsDebugEnabled) Loger.Debug(format, arg0);
}
public void DebugFormat(string format, object arg0, object arg1)
{
if (IsDebugEnabled) loger.Debug(format, arg0, arg1);
if (IsDebugEnabled) Loger.Debug(format, arg0, arg1);
}
public void DebugFormat(string format, object arg0, object arg1, object arg2)
{
if (IsDebugEnabled) loger.Debug(format, arg0, arg1, arg2);
if (IsDebugEnabled) Loger.Debug(format, arg0, arg1, arg2);
}
public void DebugFormat(IFormatProvider provider, string format, params object[] args)
{
if (IsDebugEnabled) loger.Debug(provider, format, args);
if (IsDebugEnabled) Loger.Debug(provider, format, args);
}
public void DebugWithProps(string message, params KeyValuePair<string, object>[] props)
{
if (!IsDebugEnabled) return;
var theEvent = new LogEventInfo { Message = message, LoggerName = name, Level = LogLevel.Debug };
var theEvent = new LogEventInfo { Message = message, LoggerName = Name, Level = LogLevel.Debug };
foreach (var p in props)
{
theEvent.Properties[p.Key] = p.Value;
}
loger.Log(theEvent);
Loger.Log(theEvent);
}
public void Info(object message)
{
if (IsInfoEnabled) loger.Info(message);
if (IsInfoEnabled) Loger.Info(message);
}
public void Info(string message, Exception exception)
{
if (IsInfoEnabled) loger.Info(exception, message);
if (IsInfoEnabled) Loger.Info(exception, message);
}
public void InfoFormat(string format, params object[] args)
{
if (IsInfoEnabled) loger.Info(format, args);
if (IsInfoEnabled) Loger.Info(format, args);
}
public void InfoFormat(string format, object arg0)
{
if (IsInfoEnabled) loger.Info(format, arg0);
if (IsInfoEnabled) Loger.Info(format, arg0);
}
public void InfoFormat(string format, object arg0, object arg1)
{
if (IsInfoEnabled) loger.Info(format, arg0, arg1);
if (IsInfoEnabled) Loger.Info(format, arg0, arg1);
}
public void InfoFormat(string format, object arg0, object arg1, object arg2)
{
if (IsInfoEnabled) loger.Info(format, arg0, arg1, arg2);
if (IsInfoEnabled) Loger.Info(format, arg0, arg1, arg2);
}
public void InfoFormat(IFormatProvider provider, string format, params object[] args)
{
if (IsInfoEnabled) loger.Info(provider, format, args);
if (IsInfoEnabled) Loger.Info(provider, format, args);
}
public void Warn(object message)
{
if (IsWarnEnabled) loger.Warn(message);
if (IsWarnEnabled) Loger.Warn(message);
}
public void Warn(object message, Exception exception)
{
if (IsWarnEnabled) loger.Warn(exception, "{0}", message);
if (IsWarnEnabled) Loger.Warn(exception, "{0}", message);
}
public void WarnFormat(string format, params object[] args)
{
if (IsWarnEnabled) loger.Warn(format, args);
if (IsWarnEnabled) Loger.Warn(format, args);
}
public void WarnFormat(string format, object arg0)
{
if (IsWarnEnabled) loger.Warn(format, arg0);
if (IsWarnEnabled) Loger.Warn(format, arg0);
}
public void WarnFormat(string format, object arg0, object arg1)
{
if (IsWarnEnabled) loger.Warn(format, arg0, arg1);
if (IsWarnEnabled) Loger.Warn(format, arg0, arg1);
}
public void WarnFormat(string format, object arg0, object arg1, object arg2)
{
if (IsWarnEnabled) loger.Warn(format, arg0, arg1, arg2);
if (IsWarnEnabled) Loger.Warn(format, arg0, arg1, arg2);
}
public void WarnFormat(IFormatProvider provider, string format, params object[] args)
{
if (IsWarnEnabled) loger.Warn(provider, format, args);
if (IsWarnEnabled) Loger.Warn(provider, format, args);
}
public void Error(object message)
{
if (IsErrorEnabled) loger.Error(message);
if (IsErrorEnabled) Loger.Error(message);
}
public void Error(object message, Exception exception)
{
if (IsErrorEnabled) loger.Error(exception, "{0}", message);
if (IsErrorEnabled) Loger.Error(exception, "{0}", message);
}
public void ErrorFormat(string format, params object[] args)
{
if (IsErrorEnabled) loger.Error(format, args);
if (IsErrorEnabled) Loger.Error(format, args);
}
public void ErrorFormat(string format, object arg0)
{
if (IsErrorEnabled) loger.Error(format, arg0);
if (IsErrorEnabled) Loger.Error(format, arg0);
}
public void ErrorFormat(string format, object arg0, object arg1)
{
if (IsErrorEnabled) loger.Error(format, arg0, arg1);
if (IsErrorEnabled) Loger.Error(format, arg0, arg1);
}
public void ErrorFormat(string format, object arg0, object arg1, object arg2)
{
if (IsErrorEnabled) loger.Error(format, arg0, arg1, arg2);
if (IsErrorEnabled) Loger.Error(format, arg0, arg1, arg2);
}
public void ErrorFormat(IFormatProvider provider, string format, params object[] args)
{
if (IsErrorEnabled) loger.Error(provider, format, args);
if (IsErrorEnabled) Loger.Error(provider, format, args);
}
public void Fatal(object message)
{
if (IsFatalEnabled) loger.Fatal(message);
if (IsFatalEnabled) Loger.Fatal(message);
}
public void Fatal(string message, Exception exception)
{
if (IsFatalEnabled) loger.Fatal(exception, message);
if (IsFatalEnabled) Loger.Fatal(exception, message);
}
public void FatalFormat(string format, params object[] args)
{
if (IsFatalEnabled) loger.Fatal(format, args);
if (IsFatalEnabled) Loger.Fatal(format, args);
}
public void FatalFormat(string format, object arg0)
{
if (IsFatalEnabled) loger.Fatal(format, arg0);
if (IsFatalEnabled) Loger.Fatal(format, arg0);
}
public void FatalFormat(string format, object arg0, object arg1)
{
if (IsFatalEnabled) loger.Fatal(format, arg0, arg1);
if (IsFatalEnabled) Loger.Fatal(format, arg0, arg1);
}
public void FatalFormat(string format, object arg0, object arg1, object arg2)
{
if (IsFatalEnabled) loger.Fatal(format, arg0, arg1, arg2);
if (IsFatalEnabled) Loger.Fatal(format, arg0, arg1, arg2);
}
public void FatalFormat(IFormatProvider provider, string format, params object[] args)
{
if (IsFatalEnabled) loger.Fatal(provider, format, args);
if (IsFatalEnabled) Loger.Fatal(provider, format, args);
}
public string LogDirectory { get { return NLog.LogManager.Configuration.Variables["logDirectory"].Text; } }
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
Loger = NLog.LogManager.GetLogger(name);
}
}
}
public class NullLog : ILog
@ -750,45 +795,53 @@ namespace ASC.Common.Logging
}
public string LogDirectory { get { return ""; } }
public string Name { get; set; }
}
public class LogManager
{
internal IContainer Builder { get; set; }
internal static ConcurrentDictionary<string, ILog> Logs;
static LogManager()
{
Logs = new ConcurrentDictionary<string, ILog>();
}
public LogManager(IContainer builder)
public class LogManager<T> : OptionsMonitor<T> where T : class, ILog, new()
{
public LogManager(IOptionsFactory<T> factory, IEnumerable<IOptionsChangeTokenSource<T>> sources, IOptionsMonitorCache<T> cache) : base(factory, sources, cache)
{
Builder = builder;
}
public static ILog GetLogger(string name)
{
return ConfigurationManager.LogManager?.Get(name) ?? new NullLog();
public override T Get(string name)
{
var log = base.Get(name);
if (string.IsNullOrEmpty(log?.Name))
{
log = CurrentValue;
}
return log;
}
public ILog Get(string name)
{
if (!Logs.TryGetValue(name, out var result))
{
result = Logs.AddOrUpdate(name, Builder != null ? Builder.Resolve<ILog>(new TypedParameter(typeof(string), name)) : new NullLog(), (k, v) => v);
}
return result;
}
}
public static class LogExtension
{
public static IServiceCollection AddLogManager(this IServiceCollection services)
public static class StudioNotifyHelperExtension
{
public static IServiceCollection AddLogManager<T>(this IServiceCollection services, params string[] additionalLoggers) where T : class, ILog, new()
{
return services.AddSingleton<LogManager>();
const string baseName = "ASC";
var baseSqlName = $"{baseName}.SQL";
services.Configure<T>(r => r.Name = baseName);
services.Configure<T>(baseName, r => r.Name = baseName);
services.Configure<T>(baseSqlName, r => r.Name = baseSqlName);
foreach (var l in additionalLoggers)
{
services.Configure<T>(l, r => r.Name = l);
}
services.TryAddSingleton(typeof(IOptionsMonitor<ILog>), typeof(LogManager<T>));
return services;
}
}
public static IServiceCollection AddNLogManager(this IServiceCollection services, params string[] additionalLoggers)
{
services.TryAddSingleton<IConfigureOptions<LogNLog>, ConfigureLogNLog>();
return services.AddLogManager<LogNLog>(additionalLoggers);
}
}
}

View File

@ -107,7 +107,7 @@ namespace ASC.Security.Cryptography
HashAlg.SHA1 => SHA1.Create(),
HashAlg.SHA256 => SHA256.Create(),
HashAlg.SHA512 => SHA512.Create(),
_ => SHA256.Create(),
_ => SHA256.Create()
};
}

View File

@ -24,25 +24,30 @@
*/
#region usings
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
#endregion
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Security.Cryptography
{
public static class InstanceCrypto
public class InstanceCrypto
{
public static string Encrypt(string data)
public MachinePseudoKeys MachinePseudoKeys { get; }
public InstanceCrypto(MachinePseudoKeys machinePseudoKeys)
{
MachinePseudoKeys = machinePseudoKeys;
}
public string Encrypt(string data)
{
return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(data)));
}
public static byte[] Encrypt(byte[] data)
public byte[] Encrypt(byte[] data)
{
var hasher = Rijndael.Create();
hasher.Key = EKey();
@ -55,12 +60,12 @@ namespace ASC.Security.Cryptography
return ms.ToArray();
}
public static string Decrypt(string data)
public string Decrypt(string data)
{
return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(data)));
}
public static byte[] Decrypt(byte[] data)
public byte[] Decrypt(byte[] data)
{
var hasher = Rijndael.Create();
hasher.Key = EKey();
@ -76,9 +81,20 @@ namespace ASC.Security.Cryptography
return newBuffer;
}
private static byte[] EKey()
private byte[] EKey()
{
return MachinePseudoKeys.GetMachineConstant(32);
}
}
public static class InstanceCryptoExtension
{
public static IServiceCollection AddInstanceCryptoService(this IServiceCollection services)
{
services.TryAddSingleton<InstanceCrypto>();
return services
.AddHttpContextAccessor()
.AddMachinePseudoKeysService();
}
}
}

View File

@ -29,20 +29,22 @@ using System.IO;
using System.Linq;
using System.Text;
using ASC.Common.Security;
using ASC.Common.Utils;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Security.Cryptography
{
public static class MachinePseudoKeys
public class MachinePseudoKeys
{
private static readonly byte[] confkey = null;
private readonly byte[] confkey = null;
static MachinePseudoKeys()
public MachinePseudoKeys(IConfiguration configuration)
{
var key = ConfigurationManager.AppSettings["core:machinekey"];
var key = configuration["core:machinekey"];
if (string.IsNullOrEmpty(key))
{
key = ConfigurationManager.AppSettings["asc:common.machinekey"];
key = configuration["asc:common.machinekey"];
}
if (!string.IsNullOrEmpty(key))
{
@ -51,7 +53,7 @@ namespace ASC.Security.Cryptography
}
public static byte[] GetMachineConstant()
public byte[] GetMachineConstant()
{
if (confkey != null)
{
@ -63,7 +65,7 @@ namespace ASC.Security.Cryptography
return BitConverter.GetBytes(fi.CreationTime.ToOADate());
}
public static byte[] GetMachineConstant(int bytesCount)
public byte[] GetMachineConstant(int bytesCount)
{
var cnst = Enumerable.Repeat<byte>(0, sizeof(int)).Concat(GetMachineConstant()).ToArray();
var icnst = BitConverter.ToInt32(cnst, cnst.Length - sizeof(int));
@ -73,4 +75,13 @@ namespace ASC.Security.Cryptography
return buff;
}
}
public static class MachinePseudoKeysExtension
{
public static IServiceCollection AddMachinePseudoKeysService(this IServiceCollection services)
{
services.TryAddSingleton<MachinePseudoKeys>();
return services;
}
}
}

View File

@ -36,7 +36,7 @@ namespace ASC.Common.Tests.Data
[Test]
public void ExecuteListTest()
{
using var db = new MultiRegionalDbManager("core");
using var db = new MultiRegionalDbManager(null, null, "core");
var r1 = db.ExecuteList("select 1");
Assert.IsTrue(r1.Count > 1);
}

View File

@ -36,7 +36,7 @@ namespace ASC.Common.Tests.Geolocation
[Test]
public void GetIPGeolocationTest()
{
var helper = new GeolocationHelper("db");
var helper = new GeolocationHelper(null, null, "db");
var info = helper.GetIPGeolocation("62.213.10.13");
Assert.AreEqual("Nizhny Novgorod", info.City);
Assert.AreEqual("062.213.011.127", info.IPEnd);

View File

@ -36,15 +36,15 @@ namespace ASC.Common.Threading
public DistributedTaskCache DistributedTaskCache { get; internal set; }
public string InstanseId
public string InstanceId
{
get
{
return DistributedTaskCache.InstanseId;
return DistributedTaskCache.InstanceId;
}
set
{
DistributedTaskCache.InstanseId = value;
DistributedTaskCache.InstanceId = value;
}
}
public string Id

View File

@ -33,24 +33,71 @@ using System.Threading.Tasks;
using ASC.Common.Caching;
namespace ASC.Common.Threading
{
{
public class DistributedTaskCacheNotify
{
public ConcurrentDictionary<string, CancellationTokenSource> Cancelations { get; }
public ICache Cache { get; }
private readonly ICacheNotify<DistributedTaskCancelation> notify;
private readonly ICacheNotify<DistributedTaskCache> notifyCache;
public DistributedTaskCacheNotify(ICacheNotify<DistributedTaskCancelation> notify, ICacheNotify<DistributedTaskCache> notifyCache)
{
Cancelations = new ConcurrentDictionary<string, CancellationTokenSource>();
this.notify = notify;
notify.Subscribe((c) =>
{
if (Cancelations.TryGetValue(c.Id, out var s))
{
s.Cancel();
}
}, CacheNotifyAction.Remove);
this.notifyCache = notifyCache;
notifyCache.Subscribe((c) =>
{
Cache.HashSet(c.Key, c.Id, (DistributedTaskCache)null);
}, CacheNotifyAction.Remove);
notifyCache.Subscribe((c) =>
{
Cache.HashSet(c.Key, c.Id, c);
}, CacheNotifyAction.InsertOrUpdate);
}
public void CancelTask(string id)
{
notify.Publish(new DistributedTaskCancelation() { Id = id }, CacheNotifyAction.Remove);
}
public void SetTask(DistributedTask task)
{
notifyCache.Publish(task.DistributedTaskCache, CacheNotifyAction.InsertOrUpdate);
}
public void RemoveTask(string id, string key)
{
notifyCache.Publish(new DistributedTaskCache() { Id = id, Key = key }, CacheNotifyAction.Remove);
}
}
public class DistributedTaskQueue
{
public static readonly string InstanseId;
public static readonly string InstanceId;
private readonly string key;
private static readonly ICache cache;
private readonly ICacheNotify<DistributedTaskCancelation> notify;
private readonly ICacheNotify<DistributedTaskCache> notifyCache;
private readonly ICache cache;
private readonly TaskScheduler scheduler;
private static readonly ConcurrentDictionary<string, CancellationTokenSource> cancelations = new ConcurrentDictionary<string, CancellationTokenSource>();
private readonly ConcurrentDictionary<string, CancellationTokenSource> cancelations;
public DistributedTaskCacheNotify DistributedTaskCacheNotify { get; }
static DistributedTaskQueue()
{
InstanseId = Process.GetCurrentProcess().Id.ToString();
cache = AscCache.Memory;
InstanceId = Process.GetCurrentProcess().Id.ToString();
}
@ -59,7 +106,7 @@ namespace ASC.Common.Threading
/// </summary>
/// <param name="name">Name of queue</param>
/// <param name="maxThreadsCount">limit of threads count; Default: -1 - no limit</param>
public DistributedTaskQueue(string name, int maxThreadsCount = -1)
public DistributedTaskQueue(DistributedTaskCacheNotify distributedTaskCacheNotify, string name, int maxThreadsCount = -1)
{
if (string.IsNullOrEmpty(name))
{
@ -70,26 +117,9 @@ namespace ASC.Common.Threading
scheduler = maxThreadsCount <= 0
? TaskScheduler.Default
: new LimitedConcurrencyLevelTaskScheduler(maxThreadsCount);
notify = new KafkaCache<DistributedTaskCancelation>();
notify.Subscribe((c) =>
{
if (cancelations.TryGetValue(c.Id, out var s))
{
s.Cancel();
}
}, CacheNotifyAction.Remove);
notifyCache = new KafkaCache<DistributedTaskCache>();
notifyCache.Subscribe((c) =>
{
cache.HashSet(key, c.Id, (DistributedTaskCache)null);
}, CacheNotifyAction.Remove);
notifyCache.Subscribe((c) =>
{
cache.HashSet(key, c.Id, c);
}, CacheNotifyAction.InsertOrUpdate);
DistributedTaskCacheNotify = distributedTaskCacheNotify;
cancelations = DistributedTaskCacheNotify.Cancelations;
cache = DistributedTaskCacheNotify.Cache;
}
@ -100,7 +130,7 @@ namespace ASC.Common.Threading
distributedTask = new DistributedTask();
}
distributedTask.InstanseId = InstanseId;
distributedTask.InstanceId = InstanceId;
var cancelation = new CancellationTokenSource();
var token = cancelation.Token;
@ -123,11 +153,6 @@ namespace ASC.Common.Threading
task.Start(scheduler);
}
public void CancelTask(string id)
{
notify.Publish(new DistributedTaskCancelation() { Id = id }, CacheNotifyAction.Remove);
}
public IEnumerable<DistributedTask> GetTasks()
{
var tasks = new List<DistributedTask>(cache.HashGetAll<DistributedTask>(key).Values);
@ -151,17 +176,6 @@ namespace ASC.Common.Threading
return task;
}
public void SetTask(DistributedTask task)
{
notifyCache.Publish(task.DistributedTaskCache, CacheNotifyAction.InsertOrUpdate);
}
public void RemoveTask(string id)
{
notifyCache.Publish(new DistributedTaskCache() { Id = id }, CacheNotifyAction.Remove);
}
private void OnCompleted(Task task, string id)
{
var distributedTask = GetTask(id);
@ -186,7 +200,14 @@ namespace ASC.Common.Threading
private Action<DistributedTask> GetPublication()
{
return (t) => SetTask(t);
return (t) =>
{
if (t.DistributedTaskCache != null)
{
t.DistributedTaskCache.Key = key;
}
DistributedTaskCacheNotify.SetTask(t);
};
}
}
}

View File

@ -25,30 +25,46 @@
using System;
using System.Linq;
using System.Linq;
using ASC.Common.Logging;
using ASC.Common.Threading.Workers;
using Microsoft.Extensions.Options;
namespace ASC.Common.Threading.Progress
{
public class ProgressQueue : WorkerQueue<IProgressItem>
public class ProgressQueueOptionsManager<T> : OptionsManager<ProgressQueue<T>> where T : class, IProgressItem
{
private readonly bool removeAfterCompleted;
public ProgressQueue(int workerCount, TimeSpan waitInterval) :
this(workerCount, waitInterval, false)
public ProgressQueueOptionsManager(IOptionsFactory<ProgressQueue<T>> factory) : base(factory)
{
}
}
public ProgressQueue(int workerCount, TimeSpan waitInterval, bool removeAfterCompleted)
: base(workerCount, waitInterval, 0, false)
public class ConfigureProgressQueue<T> : IConfigureOptions<ProgressQueue<T>> where T : class, IProgressItem
{
public ConfigureProgressQueue(IOptionsMonitor<ILog> log)
{
this.removeAfterCompleted = removeAfterCompleted;
Start(x => x.RunJob());
Log = log;
}
public override void Add(IProgressItem item)
public IOptionsMonitor<ILog> Log { get; }
public void Configure(ProgressQueue<T> queue)
{
queue.log = Log.Get("ASC.WorkerQueue");
queue.Start(x => x.RunJob());
}
}
public class ProgressQueue<T> : WorkerQueue<T> where T : class, IProgressItem
{
public bool removeAfterCompleted;
public ProgressQueue()
{
}
public override void Add(T item)
{
if (GetStatus(item.Id) == null)
{
@ -56,9 +72,9 @@ namespace ASC.Common.Threading.Progress
}
}
public IProgressItem GetStatus(object id)
public T GetStatus(object id)
{
IProgressItem item;
T item;
lock (SynchRoot)
{
item = GetItems().Where(x => Equals(x.Id, id)).SingleOrDefault();
@ -68,7 +84,7 @@ namespace ASC.Common.Threading.Progress
{
Remove(item);
}
return item.Clone() as IProgressItem;
return (T)item.Clone();
}
}
return item;
@ -92,7 +108,7 @@ namespace ASC.Common.Threading.Progress
}
}
protected override WorkItem<IProgressItem> Selector()
protected override WorkItem<T> Selector()
{
return Items
.Where(x => !x.IsProcessed && !x.IsCompleted)
@ -100,17 +116,17 @@ namespace ASC.Common.Threading.Progress
.FirstOrDefault();
}
protected override void PostComplete(WorkItem<IProgressItem> item)
protected override void PostComplete(WorkItem<T> item)
{
item.IsCompleted = true;
}
protected override void ErrorLimit(WorkItem<IProgressItem> item)
protected override void ErrorLimit(WorkItem<T> item)
{
PostComplete(item);
}
protected override void Error(WorkItem<IProgressItem> workItem, Exception exception)
protected override void Error(WorkItem<T> workItem, Exception exception)
{
workItem.Item.Error = exception;
workItem.Item.IsCompleted = true;

View File

@ -27,15 +27,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading;
using ASC.Common.Logging;
using Microsoft.Extensions.Options;
namespace ASC.Common.Threading.Workers
{
public class WorkerQueueOptionsManager<T> : OptionsManager<WorkerQueue<T>>
{
public WorkerQueueOptionsManager(IOptionsFactory<WorkerQueue<T>> factory) : base(factory)
{
}
}
public class ConfigureWorkerQueue<T> : IConfigureOptions<WorkerQueue<T>>
{
public ConfigureWorkerQueue(IOptionsMonitor<ILog> log)
{
Log = log;
}
public IOptionsMonitor<ILog> Log { get; }
public void Configure(Workers.WorkerQueue<T> queue)
{
queue.log = Log.Get("ASC.WorkerQueue");
}
}
public class WorkerQueue<T>
{
private static readonly ILog log = LogManager.GetLogger("ASC.WorkerQueue");
internal ILog log;
private readonly ICollection<WorkItem<T>> items = new List<WorkItem<T>>();
private readonly List<Thread> threads = new List<Thread>();
@ -43,10 +65,10 @@ namespace ASC.Common.Threading.Workers
private readonly AutoResetEvent waitEvent = new AutoResetEvent(false);
private readonly ManualResetEvent stopEvent = new ManualResetEvent(false);
private readonly int workerCount;
private readonly bool stopAfterFinsih;
private readonly int errorCount;
private readonly int waitInterval;
public int workerCount;
public bool stopAfterFinsih;
public int errorCount;
public int waitInterval;
private Action<T> action;
private volatile bool started;
@ -57,18 +79,9 @@ namespace ASC.Common.Threading.Workers
public bool IsStarted { get { return started; } }
public WorkerQueue(int workerCount, TimeSpan waitInterval)
: this(workerCount, waitInterval, 1, false)
public WorkerQueue()
{
}
public WorkerQueue(int workerCount, TimeSpan waitInterval, int errorCount, bool stopAfterFinsih)
{
this.workerCount = workerCount;
this.errorCount = errorCount;
this.stopAfterFinsih = stopAfterFinsih;
this.waitInterval = (int)waitInterval.TotalMilliseconds;
}
@ -133,8 +146,8 @@ namespace ASC.Common.Threading.Workers
{
if (started)
{
started = false;
started = false;
stopEvent.Set();
waitEvent.Set();
@ -152,8 +165,8 @@ namespace ASC.Common.Threading.Workers
{
if (started)
{
started = false;
started = false;
stopEvent.Set();
waitEvent.Set();
@ -200,7 +213,7 @@ namespace ASC.Common.Threading.Workers
protected virtual void Error(WorkItem<T> item, Exception exception)
{
LogManager.GetLogger("ASC.Common.Threading.Workers").Error(item, exception);
log.Error(item, exception);
item.IsProcessed = false;
item.Added = DateTime.Now;
@ -230,8 +243,8 @@ namespace ASC.Common.Threading.Workers
if (!started)
{
started = true;
action = starter;
action = starter;
stopEvent.Reset();
waitEvent.Reset();

View File

@ -1,57 +1,11 @@
using System;
using System.Collections;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using ASC.Common.Logging;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Common.Utils
{
public static class ConfigurationManager
{
public static IConfiguration AppSettings { get; private set; }
public static ConnectionStringCollection ConnectionStrings { get; private set; }
public static LogManager LogManager { get; private set; }
public static void Init(IServiceProvider serviceProvider)
{
AppSettings = serviceProvider.GetService<IConfiguration>();
LogManager = serviceProvider.GetService<LogManager>();
ConnectionStrings = new ConnectionStringCollection(GetSettings<ConnectionStringSettings>("ConnectionStrings"));
}
public static void UseCm(this IApplicationBuilder applicationBuilder)
{
Init(applicationBuilder.ApplicationServices);
}
public static IEnumerable<T> GetSettings<T>(string section) where T : new()
{
var result = new List<T>();
var sectionSettings = AppSettings.GetSection(section);
foreach (var ch in sectionSettings.GetChildren())
{
var cs = new T();
ch.Bind(cs);
result.Add(cs);
}
return result;
}
public static T GetSetting<T>(string section) where T : new()
{
var sectionSettings = AppSettings.GetSection(section);
var cs = new T();
sectionSettings.Bind(cs);
return cs;
}
}
public class ConnectionStringCollection : IEnumerable<ConnectionStringSettings>
{
private List<ConnectionStringSettings> Data { get; set; }
@ -76,4 +30,41 @@ namespace ASC.Common.Utils
}
}
}
public static class ConfigurationExtension
{
public static IEnumerable<T> GetSettings<T>(this IConfiguration configuration, string section) where T : new()
{
var result = new List<T>();
var sectionSettings = configuration.GetSection(section);
foreach (var ch in sectionSettings.GetChildren())
{
var cs = new T();
ch.Bind(cs);
result.Add(cs);
}
return result;
}
public static T GetSetting<T>(this IConfiguration configuration, string section) where T : new()
{
var sectionSettings = configuration.GetSection(section);
var cs = new T();
sectionSettings.Bind(cs);
return cs;
}
public static ConnectionStringCollection GetConnectionStrings(this IConfiguration configuration)
{
return new ConnectionStringCollection(configuration.GetSettings<ConnectionStringSettings>("ConnectionStrings"));
}
public static ConnectionStringSettings GetConnectionStrings(this IConfiguration configuration, string key)
{
return configuration.GetConnectionStrings()[key];
}
}
}

View File

@ -30,13 +30,22 @@ using System.Text;
using ASC.Security.Cryptography;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Newtonsoft.Json;
namespace ASC.Common.Utils
{
public static class Signature
public class Signature
{
public static string Create<T>(T obj)
public Signature(MachinePseudoKeys machinePseudoKeys)
{
MachinePseudoKeys = machinePseudoKeys;
}
public MachinePseudoKeys MachinePseudoKeys { get; }
public string Create<T>(T obj)
{
return Create(obj, Encoding.UTF8.GetString(MachinePseudoKeys.GetMachineConstant()));
}
@ -48,7 +57,7 @@ namespace ASC.Common.Utils
return WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(payload));
}
public static T Read<T>(string signature)
public T Read<T>(string signature)
{
return Read<T>(signature, Encoding.UTF8.GetString(MachinePseudoKeys.GetMachineConstant()));
}
@ -89,4 +98,13 @@ namespace ASC.Common.Utils
return Convert.ToBase64String(md5.ComputeHash(Encoding.UTF8.GetBytes(str)));
}
}
public static class SignatureExtension
{
public static IServiceCollection AddSignatureService(this IServiceCollection services)
{
services.TryAddSingleton<Signature>();
return services.AddMachinePseudoKeysService();
}
}
}

View File

@ -31,43 +31,48 @@ using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Xml.XPath;
using ASC.Common.Logging;
using ASC.Common.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
namespace ASC.Common.Utils
{
public class TimeZoneConverter
{
private static readonly ILog Log = LogManager.GetLogger("ASC.TimeZone");
private IEnumerable<MapZone> _mapZones;
private static IEnumerable<MapZone> _mapZones;
private bool _isR7;
private static bool _isR7;
private Dictionary<string, string> _translations;
private static Dictionary<string, string> _translations;
private IConfiguration Configuration { get; }
private ILog Log { get; }
static TimeZoneConverter()
public TimeZoneConverter(IConfiguration configuration, IOptionsMonitor<ILog> option)
{
Log = option.CurrentValue;
Configuration = configuration;
InitMapZones();
InitTranslations();
}
private static void InitMapZones()
private void InitMapZones()
{
try
{
var assembly = Assembly.GetExecutingAssembly();
using var stream = assembly.GetManifestResourceStream("ASC.Common.Utils.TimeZoneConverter.windowsZones.xml");
var xml = XElement.Load(stream);
_mapZones = from row in xml.XPathSelectElements("*/mapTimezones/mapZone")
let olsonTimeZones = row.Attribute("type").Value.Split(' ')
from olsonTimeZone in olsonTimeZones
select new MapZone
{
OlsonTimeZoneId = olsonTimeZone,
WindowsTimeZoneId = row.Attribute("other").Value,
Territory = row.Attribute("territory").Value
};
using var stream = assembly.GetManifestResourceStream("ASC.Common.Utils.TimeZoneConverter.windowsZones.xml");
var xml = XElement.Load(stream);
_mapZones = from row in xml.XPathSelectElements("*/mapTimezones/mapZone")
let olsonTimeZones = row.Attribute("type").Value.Split(' ')
from olsonTimeZone in olsonTimeZones
select new MapZone
{
OlsonTimeZoneId = olsonTimeZone,
WindowsTimeZoneId = row.Attribute("other").Value,
Territory = row.Attribute("territory").Value
};
_mapZones = _mapZones.ToList();
}
catch (Exception error)
@ -77,7 +82,7 @@ namespace ASC.Common.Utils
}
}
public static string OlsonTzId2WindowsTzId(string olsonTimeZoneId, bool defaultIfNoMatch = true)
public string OlsonTzId2WindowsTzId(string olsonTimeZoneId, bool defaultIfNoMatch = true)
{
var mapZone = GetMapZoneByWindowsTzId(olsonTimeZoneId);
@ -94,7 +99,7 @@ namespace ASC.Common.Utils
return defaultIfNoMatch ? "UTC" : null;
}
public static string WindowsTzId2OlsonTzId(string windowsTimeZoneId, bool defaultIfNoMatch = true)
public string WindowsTzId2OlsonTzId(string windowsTimeZoneId, bool defaultIfNoMatch = true)
{
var mapZone = GetMapZoneByOlsonTzId(windowsTimeZoneId);
@ -111,7 +116,7 @@ namespace ASC.Common.Utils
return defaultIfNoMatch ? "Etc/GMT" : null;
}
public static TimeZoneInfo GetTimeZone(string timeZoneId, bool defaultIfNoMatch = true)
public TimeZoneInfo GetTimeZone(string timeZoneId, bool defaultIfNoMatch = true)
{
try
{
@ -151,20 +156,20 @@ namespace ASC.Common.Utils
}
}
private static MapZone GetMapZoneByOlsonTzId(string olsonTimeZoneId)
private MapZone GetMapZoneByOlsonTzId(string olsonTimeZoneId)
{
return _mapZones.FirstOrDefault(x =>
x.OlsonTimeZoneId.Equals(olsonTimeZoneId, StringComparison.CurrentCultureIgnoreCase));
}
private static MapZone GetMapZoneByWindowsTzId(string windowsTimeZoneId)
private MapZone GetMapZoneByWindowsTzId(string windowsTimeZoneId)
{
return _mapZones.FirstOrDefault(x =>
x.WindowsTimeZoneId.Equals(windowsTimeZoneId, StringComparison.CurrentCultureIgnoreCase) &&
x.Territory.Equals("001", StringComparison.CurrentCultureIgnoreCase));
}
private static TimeZoneInfo GetTimeZoneByOffset(string timeZoneId)
private TimeZoneInfo GetTimeZoneByOffset(string timeZoneId)
{
var systemTimeZones = TimeZoneInfo.GetSystemTimeZones();
@ -188,11 +193,11 @@ namespace ASC.Common.Utils
return systemTimeZones.FirstOrDefault(tz => tz.BaseUtcOffset == offset);
}
private static void InitTranslations()
private void InitTranslations()
{
try
{
_isR7 = ConfigurationManager.AppSettings["core.r7office"] == "true";
_isR7 = Configuration["core:r7office"] == "true";
if (!_isR7)
{
@ -202,10 +207,10 @@ namespace ASC.Common.Utils
var assembly = Assembly.GetExecutingAssembly();
using var stream = assembly.GetManifestResourceStream("ASC.Common.Utils.TimeZoneConverter.timeZoneNames.xml");
var xml = XElement.Load(stream);
_translations = (from row in xml.XPathSelectElements("*/zone")
select new KeyValuePair<string, string>(row.Attribute("type").Value, row.Value)
using var stream = assembly.GetManifestResourceStream("ASC.Common.Utils.TimeZoneConverter.timeZoneNames.xml");
var xml = XElement.Load(stream);
_translations = (from row in xml.XPathSelectElements("*/zone")
select new KeyValuePair<string, string>(row.Attribute("type").Value, row.Value)
).ToDictionary(item => item.Key, item => item.Value);
}
catch (Exception error)
@ -215,7 +220,7 @@ namespace ASC.Common.Utils
}
}
public static string GetTimeZoneName(TimeZoneInfo timeZone)
public string GetTimeZoneName(TimeZoneInfo timeZone)
{
if (!_isR7)
return timeZone.DisplayName;

View File

@ -43,15 +43,6 @@ namespace ASC.Common.Web
this.ctx = ctx ?? throw new ArgumentNullException();
}
public static DisposableHttpContext Current
{
get
{
if (Common.HttpContext.Current == null) throw new NotSupportedException("Avaliable in web request only.");
return new DisposableHttpContext(Common.HttpContext.Current);
}
}
public object this[string key]
{
get { return Items.ContainsKey(key) ? Items[key] : null; }

View File

@ -1,59 +0,0 @@
/*
*
* (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.
*
*/
#region usings
#endregion
namespace ASC.Common.Web
{
//TODO: Find better way to make DisposableHttpContextHttpModule
/*
public class DisposableHttpContextHttpModule : IHttpModule
{
#region IHttpModule
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.EndRequest += Application_EndRequest;
}
#endregion
private void Application_EndRequest(Object source, EventArgs e)
{
var application = (HttpApplication) source;
new DisposableHttpContext(application.Context).Dispose();
}
}
*/
}

View File

@ -1,17 +0,0 @@
using ASC.Common.DependencyInjection;
using Microsoft.AspNetCore.Http;
namespace ASC.Common
{
public static class HttpContext
{
public static Microsoft.AspNetCore.Http.HttpContext Current
{
get
{
var currentContext = CommonServiceProvider.GetService<IHttpContextAccessor>();
return currentContext?.HttpContext;
}
}
}
}

View File

@ -4,10 +4,11 @@ package ASC.Common.Threading;
message DistributedTaskCache {
string Id = 1;
string InstanseId = 2;
string InstanceId = 2;
string Status = 3;
string Exception = 4;
repeated DistributedTaskCacheProp Props = 5;
string Key = 6;
message DistributedTaskCacheProp
{

View File

@ -28,31 +28,33 @@
<ItemGroup>
<Compile Remove="Notify\Jabber\IReverseJabberService.cs" />
</ItemGroup>
<ItemGroup>
<None Remove="protos\AzRecordCache.proto" />
<None Remove="protos\ConsumerCacheItem.proto" />
<None Remove="protos\GroupCacheItem.proto" />
<None Remove="protos\NotifyMessage.proto" />
<None Remove="protos\QuotaCacheItem.proto" />
<None Remove="protos\SettingsCacheItem.proto" />
<None Remove="protos\SubscriptionMethodCache.proto" />
<None Remove="protos\SubscriptionRecord.proto" />
<None Remove="protos\TenantCacheItem.proto" />
<None Remove="protos\TenantSetting.proto" />
<None Remove="protos\UserGroupRefCacheItem.proto" />
<None Remove="protos\UserInfoCacheItem.proto" />
<None Remove="protos\UserPhotoCacheItem.proto" />
<ItemGroup>
<None Remove="protos\AzRecordCache.proto" />
<None Remove="protos\ConsumerCacheItem.proto" />
<None Remove="protos\GroupCacheItem.proto" />
<None Remove="protos\NotifyMessage.proto" />
<None Remove="protos\QuotaCacheItem.proto" />
<None Remove="protos\SettingsCacheItem.proto" />
<None Remove="protos\SubscriptionMethodCache.proto" />
<None Remove="protos\SubscriptionRecord.proto" />
<None Remove="protos\TenantCacheItem.proto" />
<None Remove="protos\TenantSetting.proto" />
<None Remove="protos\UserGroupRefCacheItem.proto" />
<None Remove="protos\UserInfoCacheItem.proto" />
<None Remove="protos\UserPhotoCacheItem.proto" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AWSSDK.CloudFront" Version="3.3.101.46" />
<PackageReference Include="AWSSDK.Core" Version="3.3.103.43" />
<PackageReference Include="AWSSDK.S3" Version="3.3.104.31" />
<PackageReference Include="AWSSDK.SimpleEmail" Version="3.3.101.50" />
<PackageReference Include="Grpc.Tools" Version="2.24.0-pre1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PackageReference Include="AutoMapper" Version="9.0.0" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
<PackageReference Include="AWSSDK.CloudFront" Version="3.3.101.64" />
<PackageReference Include="AWSSDK.Core" Version="3.3.103.62" />
<PackageReference Include="AWSSDK.S3" Version="3.3.106.4" />
<PackageReference Include="AWSSDK.SimpleEmail" Version="3.3.101.68" />
<PackageReference Include="Grpc.Tools" Version="2.25.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MailKit" Version="2.3.1.6" />
<PackageReference Include="MailKit" Version="2.4.1" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="protos\NotifyMessage.proto" />

View File

@ -1,159 +1,201 @@
/*
*
* (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;
/*
*
* (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.Linq;
using System.Text.RegularExpressions;
using System.Web;
using ASC.Common.Logging;
using ASC.Common.Logging;
using ASC.Common.Web;
using HttpContext = Microsoft.AspNetCore.Http.HttpContext;
namespace ASC.Core.Common
{
public static class BaseCommonLinkUtility
{
private const string LOCALHOST = "localhost";
private static UriBuilder _serverRoot;
private static string _vpath;
static BaseCommonLinkUtility()
{
try
{
var uriBuilder = new UriBuilder(Uri.UriSchemeHttp, LOCALHOST);
if (ASC.Common.HttpContext.Current != null && ASC.Common.HttpContext.Current.Request != null)
{
var u = ASC.Common.HttpContext.Current.Request.GetUrlRewriter();
uriBuilder = new UriBuilder(u.Scheme, LOCALHOST, u.Port);
}
_serverRoot = uriBuilder;
}
catch (Exception error)
{
LogManager.GetLogger("ASC.Web").Error(error);
}
}
public static void Initialize(string serverUri)
{
if (string.IsNullOrEmpty(serverUri))
{
throw new ArgumentNullException("serverUri");
}
var uri = new Uri(serverUri.Replace('*', 'x').Replace('+', 'x'));
_serverRoot = new UriBuilder(uri.Scheme, LOCALHOST, uri.Port);
_vpath = "/" + uri.AbsolutePath.Trim('/');
}
public static string VirtualRoot
{
get { return ToAbsolute("~"); }
}
public static string ServerRootPath(HttpContext context)
{
UriBuilder result;
// first, take from current request
if (context != null && context.Request != null)
{
var u = context.Request.GetUrlRewriter();
result = new UriBuilder(u.Scheme, u.Host, u.Port);
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using HttpContext = Microsoft.AspNetCore.Http.HttpContext;
if (CoreContext.Configuration.Standalone && !result.Uri.IsLoopback)
{
// save for stanalone
_serverRoot.Host = result.Host;
}
namespace ASC.Core.Common
{
public class CommonLinkUtilitySettings
{
public string ServerUri { get; set; }
}
public class BaseCommonLinkUtility
{
private const string LOCALHOST = "localhost";
private UriBuilder _serverRoot;
private string _vpath;
public HttpContext HttpContext { get; set; }
public BaseCommonLinkUtility(
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings,
TenantManager tenantManager,
IOptionsMonitor<ILog> options,
IOptions<CommonLinkUtilitySettings> settings)
: this(null, coreBaseSettings, coreSettings, tenantManager, options, settings)
{
}
public BaseCommonLinkUtility(
IHttpContextAccessor httpContextAccessor,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings,
TenantManager tenantManager,
IOptionsMonitor<ILog> options,
IOptions<CommonLinkUtilitySettings> settings)
{
var serverUri = settings.Value.ServerUri;
if (!string.IsNullOrEmpty(serverUri))
{
var uri = new Uri(serverUri.Replace('*', 'x').Replace('+', 'x'));
_serverRoot = new UriBuilder(uri.Scheme, LOCALHOST, uri.Port);
_vpath = "/" + uri.AbsolutePath.Trim('/');
}
else
{
result = new UriBuilder(_serverRoot.Uri);
}
if (result.Uri.IsLoopback)
{
// take values from db if localhost or no http context thread
var tenant = CoreContext.TenantManager.GetCurrentTenant(context);
result.Host = tenant.TenantDomain;
#if DEBUG
// for Visual Studio debug
if (tenant.TenantAlias == LOCALHOST)
try
{
result.Host = LOCALHOST;
}
#endif
if (!string.IsNullOrEmpty(tenant.MappedDomain))
{
var mapped = tenant.MappedDomain.ToLowerInvariant();
if (!mapped.Contains(Uri.SchemeDelimiter))
HttpContext = httpContextAccessor?.HttpContext;
var uriBuilder = new UriBuilder(Uri.UriSchemeHttp, LOCALHOST);
if (HttpContext?.Request != null)
{
mapped = Uri.UriSchemeHttp + Uri.SchemeDelimiter + mapped;
var u = HttpContext.Request.GetUrlRewriter();
uriBuilder = new UriBuilder(u.Scheme, LOCALHOST, u.Port);
}
result = new UriBuilder(mapped);
_serverRoot = uriBuilder;
}
catch (Exception error)
{
options.Get("ASC.Web").Error(error);
}
}
return result.Uri.ToString().TrimEnd('/');
}
public static string GetFullAbsolutePath(HttpContext context, string virtualPath)
{
if (string.IsNullOrEmpty(virtualPath))
return ServerRootPath(context);
if (virtualPath.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) ||
virtualPath.StartsWith("mailto:", StringComparison.InvariantCultureIgnoreCase) ||
virtualPath.StartsWith("javascript:", StringComparison.InvariantCultureIgnoreCase) ||
virtualPath.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
return virtualPath;
if (string.IsNullOrEmpty(virtualPath) || virtualPath.StartsWith("/"))
{
return ServerRootPath(context) + virtualPath;
}
return ServerRootPath(context) + VirtualRoot.TrimEnd('/') + "/" + virtualPath.TrimStart('~', '/');
}
public static string ToAbsolute(string virtualPath)
{
if (_vpath == null)
{
return VirtualPathUtility.ToAbsolute(virtualPath);
}
if (string.IsNullOrEmpty(virtualPath) || virtualPath.StartsWith("/"))
{
return virtualPath;
}
return (_vpath != "/" ? _vpath : string.Empty) + "/" + virtualPath.TrimStart('~', '/');
CoreBaseSettings = coreBaseSettings;
CoreSettings = coreSettings;
TenantManager = tenantManager;
}
public string VirtualRoot
{
get { return ToAbsolute("~"); }
}
public CoreBaseSettings CoreBaseSettings { get; }
public CoreSettings CoreSettings { get; }
public TenantManager TenantManager { get; }
private string serverRootPath;
public string ServerRootPath
{
get
{
if (!string.IsNullOrEmpty(serverRootPath)) return serverRootPath;
UriBuilder result;
// first, take from current request
if (HttpContext?.Request != null)
{
var u = HttpContext.Request.GetUrlRewriter();
result = new UriBuilder(u.Scheme, u.Host, u.Port);
if (CoreBaseSettings.Standalone && !result.Uri.IsLoopback)
{
// save for stanalone
_serverRoot.Host = result.Host;
}
}
else
{
result = new UriBuilder(_serverRoot.Uri);
}
if (result.Uri.IsLoopback)
{
// take values from db if localhost or no http context thread
var tenant = TenantManager.GetCurrentTenant();
result.Host = tenant.GetTenantDomain(CoreSettings);
#if DEBUG
// for Visual Studio debug
if (tenant.TenantAlias == LOCALHOST)
{
result.Host = LOCALHOST;
}
#endif
if (!string.IsNullOrEmpty(tenant.MappedDomain))
{
var mapped = tenant.MappedDomain.ToLowerInvariant();
if (!mapped.Contains(Uri.SchemeDelimiter))
{
mapped = Uri.UriSchemeHttp + Uri.SchemeDelimiter + mapped;
}
result = new UriBuilder(mapped);
}
}
return serverRootPath = result.Uri.ToString().TrimEnd('/');
}
}
public string GetFullAbsolutePath(string virtualPath)
{
if (string.IsNullOrEmpty(virtualPath))
{
return ServerRootPath;
}
if (virtualPath.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) ||
virtualPath.StartsWith("mailto:", StringComparison.InvariantCultureIgnoreCase) ||
virtualPath.StartsWith("javascript:", StringComparison.InvariantCultureIgnoreCase) ||
virtualPath.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
return virtualPath;
if (string.IsNullOrEmpty(virtualPath) || virtualPath.StartsWith("/"))
{
return ServerRootPath + virtualPath;
}
return ServerRootPath + VirtualRoot.TrimEnd('/') + "/" + virtualPath.TrimStart('~', '/');
}
public string ToAbsolute(string virtualPath)
{
if (_vpath == null)
{
return VirtualPathUtility.ToAbsolute(virtualPath);
}
if (string.IsNullOrEmpty(virtualPath) || virtualPath.StartsWith("/"))
{
return virtualPath;
}
return (_vpath != "/" ? _vpath : string.Empty) + "/" + virtualPath.TrimStart('~', '/');
}
public static string GetRegionalUrl(string url, string lang)
@ -188,6 +230,19 @@ namespace ASC.Core.Common
return baseUri.ToString().TrimEnd('/');
}
}
}
}
}
public static class BaseCommonLinkUtilityExtension
{
public static IServiceCollection AddBaseCommonLinkUtilityService(this IServiceCollection services)
{
services.TryAddScoped<BaseCommonLinkUtility>(); ;
return services
.AddCoreBaseSettingsService()
.AddCoreSettingsService()
.AddTenantManagerService();
}
}
}

View File

@ -32,27 +32,33 @@ using System.Runtime.Serialization;
using System.ServiceModel;
using System.Web;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Xml.XPath;
using ASC.Common.Logging;
using ASC.Common.Utils;
using ASC.Common.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
namespace ASC.Core.Billing
{
public class BillingClient : ClientBase<IService>, IDisposable
{
private readonly static ILog log = LogManager.GetLogger("ASC");
private readonly ILog log;
private readonly bool test;
private string Security { get; set; }
private string PartnersProduct { get; set; }
public BillingClient()
: this(false)
public BillingClient(IConfiguration configuration, IOptionsMonitor<ILog> option)
: this(false, configuration, option)
{
}
public BillingClient(bool test)
public BillingClient(bool test, IConfiguration configuration, IOptionsMonitor<ILog> option)
{
this.test = test;
Security = configuration["core:payment:security"];
PartnersProduct = configuration["core:payment:partners-product"];
log = option.CurrentValue;
}
@ -229,8 +235,8 @@ namespace ASC.Core.Billing
{
return Request("SetPartnerStatus",
partnerId.Replace("-", ""),
Tuple.Create("Security", ConfigurationManager.AppSettings["core:payment:security"]),
Tuple.Create("ProductId", ConfigurationManager.AppSettings["core:payment:partners-product"]),
Tuple.Create("Security", Security),
Tuple.Create("ProductId", PartnersProduct),
Tuple.Create("Status", setAuthorized ? "1" : "0"),
Tuple.Create("RecreateSKey", "0"),
Tuple.Create("Renewal", (!setAuthorized || startDate == default || startDate == DateTime.MinValue
@ -364,8 +370,8 @@ namespace ASC.Core.Billing
void IDisposable.Dispose()
{
try
{
// Close();
{
// Close();
}
catch (CommunicationException)
{

View File

@ -35,27 +35,28 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ASC.Common.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
namespace ASC.Core.Common.Billing
{
public class CouponManager
{
private static IEnumerable<AvangateProduct> Products { get; set; }
private static IEnumerable<string> Groups { get; set; }
private static readonly int Percent;
private static readonly int Schedule;
private static readonly string VendorCode;
private static readonly byte[] Secret;
private static readonly Uri BaseAddress;
private static readonly string ApiVersion;
private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1);
private static readonly ILog Log;
private IEnumerable<AvangateProduct> Products { get; set; }
private IEnumerable<string> Groups { get; set; }
private readonly int Percent;
private readonly int Schedule;
private readonly string VendorCode;
private readonly byte[] Secret;
private readonly Uri BaseAddress;
private readonly string ApiVersion;
private readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1);
private readonly ILog Log;
static CouponManager()
public CouponManager(IOptionsMonitor<ILog> option)
{
SemaphoreSlim = new SemaphoreSlim(1, 1);
Log = LogManager.GetLogger("ASC");
Log = option.CurrentValue;
try
{
@ -81,24 +82,24 @@ namespace ASC.Core.Common.Billing
}
}
public static string CreateCoupon()
public string CreateCoupon(TenantManager tenantManager)
{
return CreatePromotionAsync().Result;
return CreatePromotionAsync(tenantManager).Result;
}
private static async Task<string> CreatePromotionAsync()
private async Task<string> CreatePromotionAsync(TenantManager tenantManager)
{
try
{
using var httpClient = PrepaireClient();
using var content = new StringContent(await Promotion.GeneratePromotion(Percent, Schedule), Encoding.Default, "application/json");
using var response = await httpClient.PostAsync(string.Format("{0}/promotions/", ApiVersion), content);
if (!response.IsSuccessStatusCode)
throw new Exception(response.ReasonPhrase);
var result = await response.Content.ReadAsStringAsync();
await Task.Delay(1000 - DateTime.UtcNow.Millisecond); // otherwise authorize exception
var createdPromotion = JsonConvert.DeserializeObject<Promotion>(result);
using var httpClient = PrepaireClient();
using var content = new StringContent(await Promotion.GeneratePromotion(Log, this, tenantManager, Percent, Schedule), Encoding.Default, "application/json");
using var response = await httpClient.PostAsync(string.Format("{0}/promotions/", ApiVersion), content);
if (!response.IsSuccessStatusCode)
throw new Exception(response.ReasonPhrase);
var result = await response.Content.ReadAsStringAsync();
await Task.Delay(1000 - DateTime.UtcNow.Millisecond); // otherwise authorize exception
var createdPromotion = JsonConvert.DeserializeObject<Promotion>(result);
return createdPromotion.Coupon.Code;
}
catch (Exception ex)
@ -108,10 +109,10 @@ namespace ASC.Core.Common.Billing
}
}
internal static async Task<IEnumerable<AvangateProduct>> GetProducts()
internal async Task<IEnumerable<AvangateProduct>> GetProducts()
{
if (Products != null) return Products;
if (Products != null) return Products;
await SemaphoreSlim.WaitAsync();
if (Products != null)
@ -122,16 +123,16 @@ namespace ASC.Core.Common.Billing
try
{
using var httpClient = PrepaireClient();
using var response = await httpClient.GetAsync(string.Format("{0}/products/?Limit=1000&Enabled=true", ApiVersion));
if (!response.IsSuccessStatusCode)
throw new Exception(response.ReasonPhrase);
var result = await response.Content.ReadAsStringAsync();
Log.Debug(result);
var products = JsonConvert.DeserializeObject<List<AvangateProduct>>(result);
products = products.Where(r => r.ProductGroup != null && Groups.Contains(r.ProductGroup.Code)).ToList();
using var httpClient = PrepaireClient();
using var response = await httpClient.GetAsync(string.Format("{0}/products/?Limit=1000&Enabled=true", ApiVersion));
if (!response.IsSuccessStatusCode)
throw new Exception(response.ReasonPhrase);
var result = await response.Content.ReadAsStringAsync();
Log.Debug(result);
var products = JsonConvert.DeserializeObject<List<AvangateProduct>>(result);
products = products.Where(r => r.ProductGroup != null && Groups.Contains(r.ProductGroup.Code)).ToList();
return Products = products;
}
catch (Exception ex)
@ -145,9 +146,9 @@ namespace ASC.Core.Common.Billing
}
}
private static HttpClient PrepaireClient()
private HttpClient PrepaireClient()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
const string applicationJson = "application/json";
var httpClient = new HttpClient { BaseAddress = BaseAddress, Timeout = TimeSpan.FromMinutes(3) };
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("accept", applicationJson);
@ -156,23 +157,23 @@ namespace ASC.Core.Common.Billing
return httpClient;
}
private static string CreateAuthHeader()
private string CreateAuthHeader()
{
using var hmac = new HMACMD5(Secret);
var date = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss");
var hash = VendorCode.Length + VendorCode + date.Length + date;
var data = hmac.ComputeHash(Encoding.UTF8.GetBytes(hash));
var sBuilder = new StringBuilder();
foreach (var t in data)
{
sBuilder.Append(t.ToString("x2"));
}
var stringBuilder = new StringBuilder();
stringBuilder.AppendFormat("code='{0}' ", VendorCode);
stringBuilder.AppendFormat("date='{0}' ", date);
stringBuilder.AppendFormat("hash='{0}'", sBuilder);
using var hmac = new HMACMD5(Secret);
var date = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss");
var hash = VendorCode.Length + VendorCode + date.Length + date;
var data = hmac.ComputeHash(Encoding.UTF8.GetBytes(hash));
var sBuilder = new StringBuilder();
foreach (var t in data)
{
sBuilder.Append(t.ToString("x2"));
}
var stringBuilder = new StringBuilder();
stringBuilder.AppendFormat("code='{0}' ", VendorCode);
stringBuilder.AppendFormat("date='{0}' ", date);
stringBuilder.AppendFormat("hash='{0}'", sBuilder);
return stringBuilder.ToString();
}
}
@ -195,11 +196,11 @@ namespace ASC.Core.Common.Billing
public int PublishToAffiliatesNetwork { get; set; }
public int AutoApply { get; set; }
public static async Task<string> GeneratePromotion(int percent, int schedule)
public static async Task<string> GeneratePromotion(ILog log, CouponManager couponManager, TenantManager tenantManager, int percent, int schedule)
{
try
{
var tenant = CoreContext.TenantManager.GetCurrentTenant();
var tenant = tenantManager.GetCurrentTenant();
var startDate = DateTime.UtcNow.Date;
var endDate = startDate.AddDays(schedule);
var code = tenant.TenantAlias;
@ -220,7 +221,7 @@ namespace ASC.Core.Common.Billing
Name = string.Format("{0} {1}% off", code, percent),
Coupon = new Coupon { Type = "SINGLE", Code = code },
Discount = new Discount { Type = "PERCENT", Value = percent },
Products = (await CouponManager.GetProducts()).Select(r => new CouponProduct { Code = r.ProductCode })
Products = (await couponManager.GetProducts()).Select(r => new CouponProduct { Code = r.ProductCode })
};
@ -228,7 +229,7 @@ namespace ASC.Core.Common.Billing
}
catch (Exception ex)
{
LogManager.GetLogger("ASC").Error(ex.Message, ex);
log.Error(ex.Message, ex);
throw;
}
}

View File

@ -28,40 +28,53 @@ using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text;
using ASC.Common.Logging;
using ASC.Common.Utils;
using ASC.Core.Tenants;
using ASC.Core.Users;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace ASC.Core.Billing
{
public static class LicenseReader
public class LicenseReader
{
private static readonly ILog Log = LogManager.GetLogger("ASC");
private static readonly string LicensePath;
private static readonly string LicensePathTemp;
private readonly ILog Log;
private readonly string LicensePath;
private readonly string LicensePathTemp;
public const string CustomerIdKey = "CustomerId";
public const int MaxUserCount = 10000;
static LicenseReader()
public LicenseReader(
UserManager userManager,
TenantManager tenantManager,
PaymentManager paymentManager,
CoreSettings coreSettings,
IConfiguration configuration,
IOptionsMonitor<ILog> options)
{
LicensePath = ConfigurationManager.AppSettings["license:file:path"];
UserManager = userManager;
TenantManager = tenantManager;
PaymentManager = paymentManager;
CoreSettings = coreSettings;
Configuration = configuration;
LicensePath = Configuration["license:file:path"];
LicensePathTemp = LicensePath + ".tmp";
Log = options.CurrentValue;
}
public static string CustomerId
public string CustomerId
{
get { return CoreContext.Configuration.GetSetting(CustomerIdKey); }
private set { CoreContext.Configuration.SaveSetting(CustomerIdKey, value); }
get { return CoreSettings.GetSetting(CustomerIdKey); }
private set { CoreSettings.SaveSetting(CustomerIdKey, value); }
}
private static Stream GetLicenseStream(bool temp = false)
private Stream GetLicenseStream(bool temp = false)
{
var path = temp ? LicensePathTemp : LicensePath;
if (!File.Exists(path)) throw new BillingNotFoundException("License not found");
@ -69,17 +82,17 @@ namespace ASC.Core.Billing
return File.OpenRead(path);
}
public static void RejectLicense()
public void RejectLicense()
{
if (File.Exists(LicensePathTemp))
File.Delete(LicensePathTemp);
if (File.Exists(LicensePath))
File.Delete(LicensePath);
CoreContext.PaymentManager.DeleteDefaultTariff();
PaymentManager.DeleteDefaultTariff();
}
public static void RefreshLicense()
public void RefreshLicense()
{
try
{
@ -119,18 +132,18 @@ namespace ASC.Core.Billing
}
}
public static DateTime SaveLicenseTemp(Stream licenseStream)
public DateTime SaveLicenseTemp(Stream licenseStream)
{
try
{
using var reader = new StreamReader(licenseStream);
var licenseJsonString = reader.ReadToEnd();
var license = License.Parse(licenseJsonString);
var dueDate = Validate(license);
SaveLicense(licenseStream, LicensePathTemp);
using var reader = new StreamReader(licenseStream);
var licenseJsonString = reader.ReadToEnd();
var license = License.Parse(licenseJsonString);
var dueDate = Validate(license);
SaveLicense(licenseStream, LicensePathTemp);
return dueDate;
}
catch (Exception ex)
@ -150,16 +163,16 @@ namespace ASC.Core.Billing
}
const int bufferSize = 4096;
using var fs = File.Open(path, FileMode.Create);
var buffer = new byte[bufferSize];
int readed;
while ((readed = licenseStream.Read(buffer, 0, bufferSize)) != 0)
{
fs.Write(buffer, 0, readed);
using var fs = File.Open(path, FileMode.Create);
var buffer = new byte[bufferSize];
int readed;
while ((readed = licenseStream.Read(buffer, 0, bufferSize)) != 0)
{
fs.Write(buffer, 0, readed);
}
}
private static DateTime Validate(License license)
private DateTime Validate(License license)
{
if (string.IsNullOrEmpty(license.CustomerId)
|| string.IsNullOrEmpty(license.Signature))
@ -175,16 +188,16 @@ namespace ASC.Core.Billing
if (license.ActiveUsers.Equals(default) || license.ActiveUsers < 1)
license.ActiveUsers = MaxUserCount;
if (license.ActiveUsers < CoreContext.UserManager.GetUsers(CoreContext.TenantManager.GetCurrentTenant(), EmployeeStatus.Default, EmployeeType.User).Length)
if (license.ActiveUsers < UserManager.GetUsers(EmployeeStatus.Default, EmployeeType.User).Length)
{
throw new LicenseQuotaException("License quota", license.OriginalLicense);
}
if (license.PortalCount <= 0)
{
license.PortalCount = CoreContext.TenantManager.GetTenantQuota(Tenant.DEFAULT_TENANT).CountPortals;
license.PortalCount = TenantManager.GetTenantQuota(Tenant.DEFAULT_TENANT).CountPortals;
}
var activePortals = CoreContext.TenantManager.GetTenants().Count();
var activePortals = TenantManager.GetTenants().Count();
if (activePortals > 1 && license.PortalCount < activePortals)
{
throw new LicensePortalException("License portal count", license.OriginalLicense);
@ -193,57 +206,58 @@ namespace ASC.Core.Billing
return license.DueDate.Date;
}
private static void LicenseToDB(License license)
private void LicenseToDB(License license)
{
Validate(license);
CustomerId = license.CustomerId;
var defaultQuota = CoreContext.TenantManager.GetTenantQuota(Tenant.DEFAULT_TENANT);
var defaultQuota = TenantManager.GetTenantQuota(Tenant.DEFAULT_TENANT);
var quota = new TenantQuota(-1000)
{
ActiveUsers = license.ActiveUsers,
MaxFileSize = defaultQuota.MaxFileSize,
MaxTotalSize = defaultQuota.MaxTotalSize,
Name = "license",
HasDomain = true,
Audit = true,
ControlPanel = true,
HealthCheck = true,
Ldap = true,
Sso = true,
WhiteLabel = license.WhiteLabel || license.Customization,
Update = true,
Support = true,
Trial = license.Trial,
CountPortals = license.PortalCount,
var quota = new TenantQuota(-1000)
{
ActiveUsers = license.ActiveUsers,
MaxFileSize = defaultQuota.MaxFileSize,
MaxTotalSize = defaultQuota.MaxTotalSize,
Name = "license",
HasDomain = true,
Audit = true,
ControlPanel = true,
HealthCheck = true,
Ldap = true,
Sso = true,
WhiteLabel = license.WhiteLabel || license.Customization,
Update = true,
Support = true,
Trial = license.Trial,
CountPortals = license.PortalCount,
};
CoreContext.TenantManager.SaveTenantQuota(quota);
TenantManager.SaveTenantQuota(quota);
if (defaultQuota.CountPortals != license.PortalCount)
{
defaultQuota.CountPortals = license.PortalCount;
CoreContext.TenantManager.SaveTenantQuota(defaultQuota);
TenantManager.SaveTenantQuota(defaultQuota);
}
var tariff = new Tariff
{
QuotaId = quota.Id,
DueDate = license.DueDate,
var tariff = new Tariff
{
QuotaId = quota.Id,
DueDate = license.DueDate,
};
CoreContext.PaymentManager.SetTariff(-1, tariff);
PaymentManager.SetTariff(-1, tariff);
if (!string.IsNullOrEmpty(license.AffiliateId))
{
var tenant = CoreContext.TenantManager.GetCurrentTenant();
var tenant = TenantManager.GetCurrentTenant();
tenant.AffiliateId = license.AffiliateId;
CoreContext.TenantManager.SaveTenant(tenant);
TenantManager.SaveTenant(tenant);
}
}
private static void LogError(Exception error)
private void LogError(Exception error)
{
if (error is BillingNotFoundException)
{
@ -264,7 +278,7 @@ namespace ASC.Core.Billing
private static DateTime _date = DateTime.MinValue;
public static DateTime VersionReleaseDate
public DateTime VersionReleaseDate
{
get
{
@ -273,8 +287,8 @@ namespace ASC.Core.Billing
_date = DateTime.MaxValue;
try
{
var versionDate = ConfigurationManager.AppSettings["version:release:date"];
var sign = ConfigurationManager.AppSettings["version:release:sign"];
var versionDate = Configuration["version:release:date"];
var sign = Configuration["version:release:sign"];
if (!sign.StartsWith("ASC "))
{
@ -291,7 +305,7 @@ namespace ASC.Core.Billing
var date = splitted[1];
var orighash = splitted[2];
var skey = ConfigurationManager.AppSettings["core:machinekey"];
var skey = Configuration["core:machinekey"];
using (var hasher = new HMACSHA1(Encoding.UTF8.GetBytes(skey)))
{
@ -310,10 +324,30 @@ namespace ASC.Core.Billing
}
catch (Exception ex)
{
LogManager.GetLogger("WebStudio").Error("VersionReleaseDate", ex);
Log.Error("VersionReleaseDate", ex);
}
return _date;
}
}
public UserManager UserManager { get; }
public TenantManager TenantManager { get; }
public PaymentManager PaymentManager { get; }
public CoreSettings CoreSettings { get; }
public IConfiguration Configuration { get; }
}
public static class LicenseReaderExtension
{
public static IServiceCollection AddLicenseReaderService(this IServiceCollection services)
{
services.TryAddScoped<LicenseReader>();
return services
.AddUserManagerService()
.AddPaymentManagerService()
.AddTenantManagerService()
.AddCoreSettingsService();
}
}
}

View File

@ -32,46 +32,34 @@ using System.Text;
using System.Threading.Tasks;
using ASC.Common.Caching;
using ASC.Common.Data;
using ASC.Common.Data.Sql;
using ASC.Common.Data.Sql.Expressions;
using ASC.Common.Logging;
using ASC.Common.Utils;
using ASC.Common.Logging;
using ASC.Core.Data;
using ASC.Core.Tenants;
using ASC.Core.Tenants;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace ASC.Core.Billing
{
class TariffService : DbBaseService, ITariffService
public class TariffServiceStorage
{
private const int DEFAULT_TRIAL_PERIOD = 30;
private static readonly TimeSpan DEFAULT_CACHE_EXPIRATION = TimeSpan.FromMinutes(5);
private static readonly TimeSpan STANDALONE_CACHE_EXPIRATION = TimeSpan.FromMinutes(15);
public ICache Cache { get; }
public ICacheNotify<TariffCacheItem> Notify { get; }
private readonly static ICache cache;
private readonly static ICacheNotify<TariffCacheItem> notify;
private readonly static bool billingConfigured = false;
private static readonly ILog log = LogManager.GetLogger("ASC");
private readonly IQuotaService quotaService;
private readonly ITenantService tenantService;
private readonly CoreConfiguration config;
private readonly bool test;
private readonly int paymentDelay;
public TimeSpan CacheExpiration { get; set; }
static TariffService()
public TariffServiceStorage(ICacheNotify<TariffCacheItem> notify)
{
cache = AscCache.Memory;
notify = new KafkaCache<TariffCacheItem>();
notify.Subscribe((i) =>
Cache = AscCache.Memory;
Notify = notify;
Notify.Subscribe((i) =>
{
cache.Remove(GetTariffCacheKey(i.TenantId));
cache.Remove(GetBillingUrlCacheKey(i.TenantId));
cache.Remove(GetBillingPaymentCacheKey(i.TenantId, DateTime.MinValue, DateTime.MaxValue)); // clear all payments
Cache.Remove(TariffService.GetTariffCacheKey(i.TenantId));
Cache.Remove(TariffService.GetBillingUrlCacheKey(i.TenantId));
Cache.Remove(TariffService.GetBillingPaymentCacheKey(i.TenantId, DateTime.MinValue, DateTime.MaxValue)); // clear all payments
}, CacheNotifyAction.Remove);
//TODO: Change code of WCF -> not supported in .NET standard/.Net Core
@ -89,24 +77,65 @@ namespace ASC.Core.Billing
log.Error(err);
}*/
}
}
public class TariffService : DbBaseService, ITariffService
{
private readonly ICache cache;
private readonly ICacheNotify<TariffCacheItem> notify;
private const int DEFAULT_TRIAL_PERIOD = 30;
private static readonly TimeSpan DEFAULT_CACHE_EXPIRATION = TimeSpan.FromMinutes(5);
private static readonly TimeSpan STANDALONE_CACHE_EXPIRATION = TimeSpan.FromMinutes(15);
private readonly static bool billingConfigured = false;
private readonly ILog log;
private readonly IQuotaService quotaService;
private readonly ITenantService tenantService;
private readonly bool test;
private readonly int paymentDelay;
public TariffService(System.Configuration.ConnectionStringSettings connectionString, IQuotaService quotaService, ITenantService tenantService)
: base(connectionString, "tenant")
public TimeSpan CacheExpiration { get; set; }
public CoreBaseSettings CoreBaseSettings { get; }
public CoreSettings CoreSettings { get; }
public IConfiguration Configuration { get; }
public TariffServiceStorage TariffServiceStorage { get; }
public IOptionsMonitor<ILog> Options { get; }
public TariffService(
IQuotaService quotaService,
ITenantService tenantService,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings,
IConfiguration configuration,
DbOptionsManager dbOptionsManager,
TariffServiceStorage tariffServiceStorage,
IOptionsMonitor<ILog> options)
: base(dbOptionsManager, "tenant")
{
log = options.CurrentValue;
this.quotaService = quotaService;
this.tenantService = tenantService;
config = new CoreConfiguration(tenantService);
CoreSettings = coreSettings;
Configuration = configuration;
TariffServiceStorage = tariffServiceStorage;
Options = options;
CoreBaseSettings = coreBaseSettings;
CacheExpiration = DEFAULT_CACHE_EXPIRATION;
test = ConfigurationManager.AppSettings["core:payment:test"] == "true";
int.TryParse(ConfigurationManager.AppSettings["core:payment:delay"], out paymentDelay);
test = configuration["core:payment:test"] == "true";
int.TryParse(configuration["core:payment:delay"], out paymentDelay);
cache = TariffServiceStorage.Cache;
notify = TariffServiceStorage.Notify;
}
public Tariff GetTariff(int tenantId, bool withRequestToPaymentSystem = true)
{
//single tariff for all portals
if (CoreContext.Configuration.Standalone)
if (CoreBaseSettings.Standalone)
tenantId = -1;
var key = GetTariffCacheKey(tenantId);
@ -131,23 +160,23 @@ namespace ASC.Core.Billing
{
try
{
using var client = GetBillingClient();
var p = client.GetLastPayment(GetPortalId(tenantId));
var quota = quotaService.GetTenantQuotas().SingleOrDefault(q => q.AvangateId == p.ProductId);
if (quota == null)
{
throw new InvalidOperationException(string.Format("Quota with id {0} not found for portal {1}.", p.ProductId, GetPortalId(tenantId)));
}
var asynctariff = Tariff.CreateDefault();
asynctariff.QuotaId = quota.Id;
asynctariff.Autorenewal = p.Autorenewal;
asynctariff.DueDate = 9999 <= p.EndDate.Year ? DateTime.MaxValue : p.EndDate;
if (SaveBillingInfo(tenantId, Tuple.Create(asynctariff.QuotaId, asynctariff.DueDate), false))
{
asynctariff = CalculateTariff(tenantId, asynctariff);
ClearCache(tenantId);
cache.Insert(key, asynctariff, DateTime.UtcNow.Add(GetCacheExpiration()));
using var client = GetBillingClient();
var p = client.GetLastPayment(GetPortalId(tenantId));
var quota = quotaService.GetTenantQuotas().SingleOrDefault(q => q.AvangateId == p.ProductId);
if (quota == null)
{
throw new InvalidOperationException(string.Format("Quota with id {0} not found for portal {1}.", p.ProductId, GetPortalId(tenantId)));
}
var asynctariff = Tariff.CreateDefault();
asynctariff.QuotaId = quota.Id;
asynctariff.Autorenewal = p.Autorenewal;
asynctariff.DueDate = 9999 <= p.EndDate.Year ? DateTime.MaxValue : p.EndDate;
if (SaveBillingInfo(tenantId, Tuple.Create(asynctariff.QuotaId, asynctariff.DueDate), false))
{
asynctariff = CalculateTariff(tenantId, asynctariff);
ClearCache(tenantId);
cache.Insert(key, asynctariff, DateTime.UtcNow.Add(GetCacheExpiration()));
}
}
catch (Exception error)
@ -178,24 +207,24 @@ namespace ASC.Core.Billing
if (tenant != null)
{
tenant.VersionChanged = DateTime.UtcNow;
tenantService.SaveTenant(tenant);
tenantService.SaveTenant(CoreSettings, tenant);
}
}
ClearCache(tenantId);
}
private static string GetTariffCacheKey(int tenantId)
internal static string GetTariffCacheKey(int tenantId)
{
return string.Format("{0}:{1}", tenantId, "tariff");
}
private static string GetBillingUrlCacheKey(int tenantId)
internal static string GetBillingUrlCacheKey(int tenantId)
{
return string.Format("{0}:{1}", tenantId, "billing:urls");
}
private static string GetBillingPaymentCacheKey(int tenantId, DateTime from, DateTime to)
internal static string GetBillingPaymentCacheKey(int tenantId, DateTime from, DateTime to)
{
return string.Format("{0}:{1}:{2}-{3}", tenantId, "billing:payments", from.ToString("yyyyMMddHHmmss"), to.ToString("yyyyMMddHHmmss"));
}
@ -220,15 +249,15 @@ namespace ASC.Core.Billing
try
{
var quotas = quotaService.GetTenantQuotas();
using var client = GetBillingClient();
foreach (var pi in client.GetPayments(GetPortalId(tenantId), from, to))
{
var quota = quotas.SingleOrDefault(q => q.AvangateId == pi.ProductId);
if (quota != null)
{
pi.QuotaId = quota.Id;
}
payments.Add(pi);
using var client = GetBillingClient();
foreach (var pi in client.GetPayments(GetPortalId(tenantId), from, to))
{
var quota = quotas.SingleOrDefault(q => q.AvangateId == pi.ProductId);
if (quota != null)
{
pi.QuotaId = quota.Id;
}
payments.Add(pi);
}
}
catch (Exception error)
@ -264,9 +293,9 @@ namespace ASC.Core.Billing
.Select(q => q.AvangateId)
.ToArray();
using var client = GetBillingClient();
urls = tenant.HasValue ?
client.GetPaymentUrls(GetPortalId(tenant.Value), products, GetAffiliateId(tenant.Value), "__Currency__", "__Language__", "__CustomerID__") :
using var client = GetBillingClient();
urls = tenant.HasValue ?
client.GetPaymentUrls(GetPortalId(tenant.Value), products, GetAffiliateId(tenant.Value), "__Currency__", "__Language__", "__CustomerID__") :
client.GetPaymentUrls(null, products, !string.IsNullOrEmpty(affiliateId) ? affiliateId : null, "__Currency__", "__Language__", "__CustomerID__");
}
catch (Exception error)
@ -335,7 +364,7 @@ client.GetPaymentUrls(null, products, !string.IsNullOrEmpty(affiliateId) ? affil
{
try
{
using var client = GetBillingClient();
using var client = GetBillingClient();
result = client.GetInvoice(paymentId);
}
catch (Exception error)
@ -386,20 +415,20 @@ client.GetPaymentUrls(null, products, !string.IsNullOrEmpty(affiliateId) ? affil
var inserted = false;
if (!Equals(bi, GetBillingInfo(tenant)))
{
using var db = GetDb();
using var tx = db.BeginTransaction();
// last record is not the same
var q = new SqlQuery("tenants_tariff").SelectCount().Where("tenant", tenant).Where("tariff", bi.Item1).Where("stamp", bi.Item2);
if (bi.Item2 == DateTime.MaxValue || renewal || db.ExecuteScalar<int>(q) == 0)
{
var i = new SqlInsert("tenants_tariff")
.InColumnValue("tenant", tenant)
.InColumnValue("tariff", bi.Item1)
.InColumnValue("stamp", bi.Item2);
db.ExecuteNonQuery(i);
cache.Remove(GetTariffCacheKey(tenant));
inserted = true;
}
var db = GetDb();
using var tx = db.BeginTransaction();
// last record is not the same
var q = new SqlQuery("tenants_tariff").SelectCount().Where("tenant", tenant).Where("tariff", bi.Item1).Where("stamp", bi.Item2);
if (bi.Item2 == DateTime.MaxValue || renewal || db.ExecuteScalar<int>(q) == 0)
{
var i = new SqlInsert("tenants_tariff")
.InColumnValue("tenant", tenant)
.InColumnValue("tariff", bi.Item1)
.InColumnValue("stamp", bi.Item2);
db.ExecuteNonQuery(i);
cache.Remove(GetTariffCacheKey(tenant));
inserted = true;
}
tx.Commit();
}
@ -409,7 +438,7 @@ client.GetPaymentUrls(null, products, !string.IsNullOrEmpty(affiliateId) ? affil
if (t != null)
{
// update tenant.LastModified to flush cache in documents
tenantService.SaveTenant(t);
tenantService.SaveTenant(CoreSettings, t);
}
}
return inserted;
@ -418,7 +447,7 @@ client.GetPaymentUrls(null, products, !string.IsNullOrEmpty(affiliateId) ? affil
public void DeleteDefaultBillingInfo()
{
const int tenant = Tenant.DEFAULT_TENANT;
using (var db = GetDb())
var db = GetDb();
{
db.ExecuteNonQuery(new SqlUpdate("tenants_tariff")
.Set("tenant", -2)
@ -477,7 +506,7 @@ client.GetPaymentUrls(null, products, !string.IsNullOrEmpty(affiliateId) ? affil
{
tariff.State = TariffState.NotPaid;
if ((q == null || !q.Trial) && config.Standalone)
if ((q == null || !q.Trial) && CoreBaseSettings.Standalone)
{
if (q != null)
{
@ -513,7 +542,7 @@ client.GetPaymentUrls(null, products, !string.IsNullOrEmpty(affiliateId) ? affil
{
try
{
return new BillingClient(test);
return new BillingClient(test, Configuration, Options);
}
catch (InvalidOperationException ioe)
{
@ -531,17 +560,17 @@ client.GetPaymentUrls(null, products, !string.IsNullOrEmpty(affiliateId) ? affil
private string GetPortalId(int tenant)
{
return config.GetKey(tenant);
return CoreSettings.GetKey(tenant);
}
private string GetAffiliateId(int tenant)
{
return config.GetAffiliateId(tenant);
return CoreSettings.GetAffiliateId(tenant);
}
private TimeSpan GetCacheExpiration()
{
if (config.Standalone && CacheExpiration < STANDALONE_CACHE_EXPIRATION)
if (CoreBaseSettings.Standalone && CacheExpiration < STANDALONE_CACHE_EXPIRATION)
{
CacheExpiration = CacheExpiration.Add(TimeSpan.FromSeconds(30));
}
@ -550,13 +579,13 @@ client.GetPaymentUrls(null, products, !string.IsNullOrEmpty(affiliateId) ? affil
private void ResetCacheExpiration()
{
if (config.Standalone)
if (CoreBaseSettings.Standalone)
{
CacheExpiration = DEFAULT_CACHE_EXPIRATION;
}
}
private static void LogError(Exception error)
private void LogError(Exception error)
{
if (error is BillingNotFoundException)
{
@ -579,4 +608,14 @@ client.GetPaymentUrls(null, products, !string.IsNullOrEmpty(affiliateId) ? affil
}
}
}
public static class TariffConfigExtension
{
public static IServiceCollection AddTariffService(this IServiceCollection services)
{
services.TryAddSingleton<TariffServiceStorage>();
services.TryAddScoped<ITariffService, TariffService>();
return services;
}
}
}

View File

@ -28,25 +28,37 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using ASC.Common.Data;
using ASC.Common.Logging;
using ASC.Common.Module;
using ASC.Common.Utils;
using ASC.Common.Utils;
using ASC.Core.Data;
using ASC.Core.Tenants;
using ASC.Core.Tenants;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace ASC.Core.Billing
{
class TariffSyncService : ITariffSyncService, IServiceController
{
private readonly static ILog log = LogManager.GetLogger("ASC");
private readonly ILog log;
private readonly TariffSyncServiceSection config;
private readonly IDictionary<int, IEnumerable<TenantQuota>> quotaServices = new Dictionary<int, IEnumerable<TenantQuota>>();
private Timer timer;
public TariffSyncService()
public TariffSyncService(
IServiceProvider serviceProvider,
IConfiguration configuration,
DbQuotaService dbQuotaService,
IOptionsMonitor<ILog> options)
{
config = TariffSyncServiceSection.GetSection();
ServiceProvider = serviceProvider;
Configuration = configuration;
DbQuotaService = dbQuotaService;
log = options.CurrentValue;
}
@ -57,9 +69,9 @@ namespace ASC.Core.Billing
{
if (!quotaServices.ContainsKey(version))
{
var cs = ConfigurationManager.ConnectionStrings[config.ConnectionStringName + version] ??
ConfigurationManager.ConnectionStrings[config.ConnectionStringName];
quotaServices[version] = new DbQuotaService(cs).GetTenantQuotas();
var cs = Configuration.GetConnectionStrings(config.ConnectionStringName + version) ??
Configuration.GetConnectionStrings(config.ConnectionStringName);
quotaServices[version] = DbQuotaService.GetTenantQuotas();
}
return quotaServices[version];
}
@ -72,6 +84,10 @@ namespace ASC.Core.Billing
get { return "Tariffs synchronizer"; }
}
public IServiceProvider ServiceProvider { get; }
public IConfiguration Configuration { get; }
public DbQuotaService DbQuotaService { get; }
public void Start()
{
if (timer == null)
@ -94,27 +110,10 @@ namespace ASC.Core.Billing
{
try
{
var tenant = CoreContext.TenantManager.GetTenants(false).OrderByDescending(t => t.Version).FirstOrDefault();
if (tenant != null)
{
using var wcfClient = new TariffSyncClient();
var quotaService = new DbQuotaService(ConfigurationManager.ConnectionStrings[config.ConnectionStringName]);
var oldtariffs = quotaService.GetTenantQuotas().ToDictionary(t => t.Id);
// save new
foreach (var tariff in wcfClient.GetTariffs(tenant.Version, CoreContext.Configuration.GetKey(tenant.TenantId)))
{
quotaService.SaveTenantQuota(tariff);
oldtariffs.Remove(tariff.Id);
}
// remove old
foreach (var tariff in oldtariffs.Values)
{
tariff.Visible = false;
quotaService.SaveTenantQuota(tariff);
}
}
using var scope = ServiceProvider.CreateScope();
var tariffSync = scope.ServiceProvider.GetService<TariffSync>();
tariffSync.Sync();
}
catch (Exception error)
{
@ -122,4 +121,45 @@ namespace ASC.Core.Billing
}
}
}
class TariffSync
{
public TariffSync(TenantManager tenantManager, DbRegistry dbRegistry, CoreSettings coreSettings, DbQuotaService dbQuotaService)
{
TenantManager = tenantManager;
DbRegistry = dbRegistry;
CoreSettings = coreSettings;
DbQuotaService = dbQuotaService;
}
public TenantManager TenantManager { get; }
public DbRegistry DbRegistry { get; }
public CoreSettings CoreSettings { get; }
public DbQuotaService DbQuotaService { get; }
public void Sync()
{
var tenant = TenantManager.GetTenants(false).OrderByDescending(t => t.Version).FirstOrDefault();
if (tenant != null)
{
using var wcfClient = new TariffSyncClient();
var quotaService = DbQuotaService;
var oldtariffs = quotaService.GetTenantQuotas().ToDictionary(t => t.Id);
// save new
foreach (var tariff in wcfClient.GetTariffs(tenant.Version, CoreSettings.GetKey(tenant.TenantId)))
{
quotaService.SaveTenantQuota(tariff);
oldtariffs.Remove(tariff.Id);
}
// remove old
foreach (var tariff in oldtariffs.Values)
{
tariff.Visible = false;
quotaService.SaveTenantQuota(tariff);
}
}
}
}
}

View File

@ -27,39 +27,79 @@
using System;
using System.Collections.Generic;
using ASC.Common.Caching;
using ASC.Core.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Core.Caching
{
public class CachedAzService : IAzService
{
class AzServiceCache
{
internal ICache Cache { get; }
internal ICacheNotify<AzRecordCache> CacheNotify { get; }
public AzServiceCache(ICacheNotify<AzRecordCache> cacheNotify)
{
CacheNotify = cacheNotify;
Cache = AscCache.Memory;
cacheNotify.Subscribe((r) => UpdateCache(r, true), CacheNotifyAction.Remove);
cacheNotify.Subscribe((r) => UpdateCache(r, false), CacheNotifyAction.InsertOrUpdate);
}
private void UpdateCache(AzRecord r, bool remove)
{
var aces = Cache.Get<AzRecordStore>(GetKey(r.Tenant));
if (aces != null)
{
lock (aces)
{
if (remove)
{
aces.Remove(r);
}
else
{
aces.Add(r);
}
}
}
}
public static string GetKey(int tenant)
{
return "acl" + tenant.ToString();
}
}
class CachedAzService : IAzService
{
private readonly IAzService service;
private readonly ICache cache;
private readonly ICacheNotify<AzRecordCache> cacheNotify;
private ICache Cache { get; }
private TimeSpan CacheExpiration { get; set; }
public TimeSpan CacheExpiration { get; set; }
public CachedAzService(IAzService service)
public CachedAzService(DbAzService service, AzServiceCache azServiceCache)
{
this.service = service ?? throw new ArgumentNullException("service");
cache = AscCache.Memory;
Cache = azServiceCache.Cache;
cacheNotify = azServiceCache.CacheNotify;
CacheExpiration = TimeSpan.FromMinutes(10);
cacheNotify = new KafkaCache<AzRecordCache>();
cacheNotify.Subscribe((r) => UpdateCache(r, true), CacheNotifyAction.Remove);
cacheNotify.Subscribe((r) => UpdateCache(r, false), CacheNotifyAction.InsertOrUpdate);
}
public IEnumerable<AzRecord> GetAces(int tenant, DateTime from)
{
var key = GetKey(tenant);
var aces = cache.Get<AzRecordStore>(key);
var key = AzServiceCache.GetKey(tenant);
var aces = Cache.Get<AzRecordStore>(key);
if (aces == null)
{
var records = service.GetAces(tenant, default);
cache.Insert(key, aces = new AzRecordStore(records), DateTime.UtcNow.Add(CacheExpiration));
Cache.Insert(key, aces = new AzRecordStore(records), DateTime.UtcNow.Add(CacheExpiration));
}
return aces;
}
@ -76,30 +116,18 @@ namespace ASC.Core.Caching
service.RemoveAce(tenant, r);
cacheNotify.Publish(r, CacheNotifyAction.Remove);
}
private string GetKey(int tenant)
{
return "acl" + tenant.ToString();
}
private void UpdateCache(AzRecord r, bool remove)
{
var aces = cache.Get<AzRecordStore>(GetKey(r.Tenant));
if (aces != null)
{
lock (aces)
{
if (remove)
{
aces.Remove(r);
}
else
{
aces.Add(r);
}
}
}
}
}
}
public static class AzConfigExtension
{
public static IServiceCollection AddAzService(this IServiceCollection services)
{
services.TryAddScoped<DbAzService>();
services.TryAddScoped<IAzService, CachedAzService>();
services.TryAddSingleton<AzServiceCache>();
services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>));
return services;
}
}
}

View File

@ -28,55 +28,85 @@ using System;
using System.Collections.Generic;
using System.Linq;
using ASC.Common.Caching;
using ASC.Core.Tenants;
using ASC.Core.Data;
using ASC.Core.Tenants;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Core.Caching
{
public class CachedQuotaService : IQuotaService
{
class QuotaServiceCache
{
internal const string KEY_QUOTA = "quota";
internal const string KEY_QUOTA_ROWS = "quotarows";
internal TrustInterval Interval { get; set; }
internal ICache Cache { get; }
internal ICacheNotify<QuotaCacheItem> CacheNotify { get; }
internal bool QuotaCacheEnabled { get; }
public QuotaServiceCache(IConfiguration Configuration, ICacheNotify<QuotaCacheItem> cacheNotify)
{
if (Configuration["core:enable-quota-cache"] == null)
{
QuotaCacheEnabled = true;
}
else
{
QuotaCacheEnabled = !bool.TryParse(Configuration["core:enable-quota-cache"], out var enabled) || enabled;
}
CacheNotify = cacheNotify;
Cache = AscCache.Memory;
Interval = new TrustInterval();
cacheNotify.Subscribe((i) =>
{
if (i.Key == KEY_QUOTA_ROWS)
{
Interval.Expire();
}
else if (i.Key == KEY_QUOTA)
{
Cache.Remove(KEY_QUOTA);
}
}, CacheNotifyAction.Any);
}
}
class CachedQuotaService : IQuotaService
{
private const string KEY_QUOTA = "quota";
private const string KEY_QUOTA_ROWS = "quotarows";
private readonly IQuotaService service;
private readonly ICache cache;
private readonly ICacheNotify<QuotaCacheItem> cacheNotify;
private readonly TrustInterval interval;
private TimeSpan CacheExpiration { get; set; }
private QuotaServiceCache QuotaServiceCache { get; }
public TimeSpan CacheExpiration
{
get;
set;
}
public CachedQuotaService(IQuotaService service)
public CachedQuotaService(DbQuotaService service, QuotaServiceCache quotaServiceCache)
{
this.service = service ?? throw new ArgumentNullException("service");
cache = AscCache.Memory;
this.service = service ?? throw new ArgumentNullException("service");
QuotaServiceCache = quotaServiceCache;
cache = quotaServiceCache.Cache;
interval = new TrustInterval();
CacheExpiration = TimeSpan.FromMinutes(10);
cacheNotify = new KafkaCache<QuotaCacheItem>();
cacheNotify.Subscribe((i) =>
{
if (i.Key == KEY_QUOTA_ROWS)
{
interval.Expire();
}
else if (i.Key == KEY_QUOTA)
{
cache.Remove(KEY_QUOTA);
}
}, CacheNotifyAction.Any);
CacheExpiration = TimeSpan.FromMinutes(10);
cacheNotify = quotaServiceCache.CacheNotify;
}
public IEnumerable<TenantQuota> GetTenantQuotas()
{
var quotas = cache.Get<IEnumerable<TenantQuota>>(KEY_QUOTA);
var quotas = cache.Get<IEnumerable<TenantQuota>>(QuotaServiceCache.KEY_QUOTA);
if (quotas == null)
{
cache.Insert(KEY_QUOTA, quotas = service.GetTenantQuotas(), DateTime.UtcNow.Add(CacheExpiration));
{
quotas = service.GetTenantQuotas();
if (QuotaServiceCache.QuotaCacheEnabled)
{
cache.Insert(QuotaServiceCache.KEY_QUOTA, quotas, DateTime.UtcNow.Add(CacheExpiration));
}
}
return quotas;
}
@ -89,7 +119,7 @@ namespace ASC.Core.Caching
public TenantQuota SaveTenantQuota(TenantQuota quota)
{
var q = service.SaveTenantQuota(quota);
cacheNotify.Publish(new QuotaCacheItem { Key = KEY_QUOTA }, CacheNotifyAction.Any);
cacheNotify.Publish(new QuotaCacheItem { Key = QuotaServiceCache.KEY_QUOTA }, CacheNotifyAction.Any);
return q;
}
@ -102,14 +132,14 @@ namespace ASC.Core.Caching
public void SetTenantQuotaRow(TenantQuotaRow row, bool exchange)
{
service.SetTenantQuotaRow(row, exchange);
cacheNotify.Publish(new QuotaCacheItem { Key = KEY_QUOTA_ROWS }, CacheNotifyAction.Any);
cacheNotify.Publish(new QuotaCacheItem { Key = QuotaServiceCache.KEY_QUOTA_ROWS }, CacheNotifyAction.Any);
}
public IEnumerable<TenantQuotaRow> FindTenantQuotaRows(TenantQuotaRowQuery query)
{
if (query == null) throw new ArgumentNullException("query");
var rows = cache.Get<Dictionary<string, List<TenantQuotaRow>>>(KEY_QUOTA_ROWS);
var rows = cache.Get<Dictionary<string, List<TenantQuotaRow>>>(QuotaServiceCache.KEY_QUOTA_ROWS);
if (rows == null || interval.Expired)
{
var date = rows != null ? interval.StartTime : DateTime.MinValue;
@ -146,11 +176,14 @@ namespace ASC.Core.Caching
}
}
}
cache.Insert(KEY_QUOTA_ROWS, rows, DateTime.UtcNow.Add(CacheExpiration));
if (QuotaServiceCache.QuotaCacheEnabled)
{
cache.Insert(QuotaServiceCache.KEY_QUOTA_ROWS, rows, DateTime.UtcNow.Add(CacheExpiration));
}
}
var quotaRows = cache.Get<Dictionary<string, List<TenantQuotaRow>>>(KEY_QUOTA_ROWS);
var quotaRows = cache.Get<Dictionary<string, List<TenantQuotaRow>>>(QuotaServiceCache.KEY_QUOTA_ROWS);
if (quotaRows == null)
{
return Enumerable.Empty<TenantQuotaRow>();
@ -169,5 +202,17 @@ namespace ASC.Core.Caching
return list.ToList();
}
}
}
public static class QuotaConfigExtension
{
public static IServiceCollection AddQuotaService(this IServiceCollection services)
{
services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>));
services.TryAddSingleton<QuotaServiceCache>();
services.TryAddScoped<DbQuotaService>();
services.TryAddScoped<IQuotaService, CachedQuotaService>();
return services;
}
}
}

View File

@ -1,254 +1,299 @@
/*
*
* (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.Collections.Generic;
/*
*
* (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.Collections.Generic;
using System.Linq;
using ASC.Common.Caching;
namespace ASC.Core.Caching
{
public class CachedSubscriptionService : ISubscriptionService
{
private readonly ISubscriptionService service;
private readonly ICache cache;
private readonly ICacheNotify<SubscriptionRecord> notifyRecord;
private readonly ICacheNotify<SubscriptionMethodCache> notifyMethod;
public TimeSpan CacheExpiration { get; set; }
public CachedSubscriptionService(ISubscriptionService service)
using ASC.Common.Caching;
using ASC.Core.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Core.Caching
{
class SubscriptionServiceCache
{
internal ICache Cache { get; }
internal ICacheNotify<SubscriptionRecord> NotifyRecord { get; }
internal ICacheNotify<SubscriptionMethodCache> NotifyMethod { get; }
public SubscriptionServiceCache(ICacheNotify<SubscriptionRecord> notifyRecord, ICacheNotify<SubscriptionMethodCache> notifyMethod)
{
this.service = service ?? throw new ArgumentNullException("service");
cache = AscCache.Memory;
notifyRecord = new KafkaCache<SubscriptionRecord>();
notifyMethod = new KafkaCache<SubscriptionMethodCache>();
CacheExpiration = TimeSpan.FromMinutes(5);
notifyRecord.Subscribe((s) =>
{
var store = GetSubsciptionsStore(s.Tenant, s.SourceId, s.ActionId);
lock (store)
Cache = AscCache.Memory;
NotifyRecord = notifyRecord;
NotifyMethod = notifyMethod;
notifyRecord.Subscribe((s) =>
{
var store = GetSubsciptionsStore(s.Tenant, s.SourceId, s.ActionId);
if (store != null)
{
store.SaveSubscription(s);
}
lock (store)
{
store.SaveSubscription(s);
}
}
}, CacheNotifyAction.InsertOrUpdate);
notifyRecord.Subscribe((s) =>
{
var store = GetSubsciptionsStore(s.Tenant, s.SourceId, s.ActionId);
lock (store)
{
if (s.ObjectId == null)
{
store.RemoveSubscriptions();
}
else
{
store.RemoveSubscriptions(s.ObjectId);
}
}
notifyRecord.Subscribe((s) =>
{
var store = GetSubsciptionsStore(s.Tenant, s.SourceId, s.ActionId);
if (store != null)
{
lock (store)
{
if (s.ObjectId == null)
{
store.RemoveSubscriptions();
}
else
{
store.RemoveSubscriptions(s.ObjectId);
}
}
}
}, CacheNotifyAction.Remove);
notifyMethod.Subscribe((m) =>
{
var store = GetSubsciptionsStore(m.Tenant, m.SourceId, m.ActionId);
lock (store)
{
store.SetSubscriptionMethod(m);
}
}, CacheNotifyAction.Any);
}
public IEnumerable<SubscriptionRecord> GetSubscriptions(int tenant, string sourceId, string actionId)
{
var store = GetSubsciptionsStore(tenant, sourceId, actionId);
lock (store)
{
return store.GetSubscriptions();
}
}
public IEnumerable<SubscriptionRecord> GetSubscriptions(int tenant, string sourceId, string actionId, string recipientId, string objectId)
{
var store = GetSubsciptionsStore(tenant, sourceId, actionId);
lock (store)
{
return store.GetSubscriptions(recipientId, objectId);
}
}
public SubscriptionRecord GetSubscription(int tenant, string sourceId, string actionId, string recipientId, string objectId)
{
var store = GetSubsciptionsStore(tenant, sourceId, actionId);
lock (store)
{
return store.GetSubscription(recipientId, objectId);
}
}
public void SaveSubscription(SubscriptionRecord s)
{
service.SaveSubscription(s);
notifyRecord.Publish(s, CacheNotifyAction.InsertOrUpdate);
}
public void RemoveSubscriptions(int tenant, string sourceId, string actionId)
{
service.RemoveSubscriptions(tenant, sourceId, actionId);
notifyRecord.Publish(new SubscriptionRecord { Tenant = tenant, SourceId = sourceId, ActionId = actionId }, CacheNotifyAction.Remove);
}
public void RemoveSubscriptions(int tenant, string sourceId, string actionId, string objectId)
{
service.RemoveSubscriptions(tenant, sourceId, actionId, objectId);
notifyRecord.Publish(new SubscriptionRecord { Tenant = tenant, SourceId = sourceId, ActionId = actionId, ObjectId = objectId }, CacheNotifyAction.Remove);
}
public IEnumerable<SubscriptionMethod> GetSubscriptionMethods(int tenant, string sourceId, string actionId, string recipientId)
{
var store = GetSubsciptionsStore(tenant, sourceId, actionId);
lock (store)
{
return store.GetSubscriptionMethods(recipientId);
}
}
public void SetSubscriptionMethod(SubscriptionMethod m)
{
service.SetSubscriptionMethod(m);
notifyMethod.Publish(m, CacheNotifyAction.Any);
}
private SubsciptionsStore GetSubsciptionsStore(int tenant, string sourceId, string actionId)
{
var key = string.Format("sub/{0}/{1}/{2}", tenant, sourceId, actionId);
var store = cache.Get<SubsciptionsStore>(key);
if (store == null)
{
var records = service.GetSubscriptions(tenant, sourceId, actionId);
var methods = service.GetSubscriptionMethods(tenant, sourceId, actionId, null);
cache.Insert(key, store = new SubsciptionsStore(records, methods), DateTime.UtcNow.Add(CacheExpiration));
}
return store;
}
private class SubsciptionsStore
{
private readonly List<SubscriptionRecord> records;
private IDictionary<string, List<SubscriptionRecord>> recordsByRec;
private IDictionary<string, List<SubscriptionRecord>> recordsByObj;
private readonly List<SubscriptionMethod> methods;
private IDictionary<string, List<SubscriptionMethod>> methodsByRec;
public SubsciptionsStore(IEnumerable<SubscriptionRecord> records, IEnumerable<SubscriptionMethod> methods)
{
this.records = records.ToList();
this.methods = methods.ToList();
BuildSubscriptionsIndex(records);
BuildMethodsIndex(methods);
}
public IEnumerable<SubscriptionRecord> GetSubscriptions()
{
return records.ToList();
}
public IEnumerable<SubscriptionRecord> GetSubscriptions(string recipientId, string objectId)
{
return recipientId != null ?
recordsByRec.ContainsKey(recipientId) ? recordsByRec[recipientId].ToList() : new List<SubscriptionRecord>() :
recordsByObj.ContainsKey(objectId ?? string.Empty) ? recordsByObj[objectId ?? string.Empty].ToList() : new List<SubscriptionRecord>();
}
public SubscriptionRecord GetSubscription(string recipientId, string objectId)
{
return recordsByRec.ContainsKey(recipientId) ?
recordsByRec[recipientId].Where(s => s.ObjectId == objectId).FirstOrDefault() :
null;
}
public void SaveSubscription(SubscriptionRecord s)
{
var old = GetSubscription(s.RecipientId, s.ObjectId);
if (old != null)
{
old.Subscribed = s.Subscribed;
}
else
{
records.Add(s);
BuildSubscriptionsIndex(records);
}
}
public void RemoveSubscriptions()
{
records.Clear();
BuildSubscriptionsIndex(records);
}
public void RemoveSubscriptions(string objectId)
{
records.RemoveAll(s => s.ObjectId == objectId);
BuildSubscriptionsIndex(records);
}
public IEnumerable<SubscriptionMethod> GetSubscriptionMethods(string recipientId)
{
return string.IsNullOrEmpty(recipientId) ?
methods.ToList() :
methodsByRec.ContainsKey(recipientId) ? methodsByRec[recipientId].ToList() : new List<SubscriptionMethod>();
}
public void SetSubscriptionMethod(SubscriptionMethod m)
{
methods.RemoveAll(r => r.Tenant == m.Tenant && r.SourceId == m.SourceId && r.ActionId == m.ActionId && r.RecipientId == m.RecipientId);
if (m.Methods != null && 0 < m.Methods.Length)
{
methods.Add(m);
}
BuildMethodsIndex(methods);
}
private void BuildSubscriptionsIndex(IEnumerable<SubscriptionRecord> records)
{
recordsByRec = records.GroupBy(r => r.RecipientId).ToDictionary(g => g.Key, g => g.ToList());
recordsByObj = records.GroupBy(r => r.ObjectId ?? string.Empty).ToDictionary(g => g.Key, g => g.ToList());
}
private void BuildMethodsIndex(IEnumerable<SubscriptionMethod> methods)
{
methodsByRec = methods.GroupBy(r => r.RecipientId).ToDictionary(g => g.Key, g => g.ToList());
}
}
}
}
notifyMethod.Subscribe((m) =>
{
var store = GetSubsciptionsStore(m.Tenant, m.SourceId, m.ActionId);
if (store != null)
{
lock (store)
{
store.SetSubscriptionMethod(m);
}
}
}, CacheNotifyAction.Any);
}
private SubsciptionsStore GetSubsciptionsStore(int tenant, string sourceId, string actionId)
{
return Cache.Get<SubsciptionsStore>(GetKey(tenant, sourceId, actionId));
}
public static string GetKey(int tenant, string sourceId, string actionId)
{
return string.Format("sub/{0}/{1}/{2}", tenant, sourceId, actionId);
}
}
class CachedSubscriptionService : ISubscriptionService
{
private readonly ISubscriptionService service;
private readonly ICache cache;
private readonly ICacheNotify<SubscriptionRecord> notifyRecord;
private readonly ICacheNotify<SubscriptionMethodCache> notifyMethod;
private TimeSpan CacheExpiration { get; set; }
public CachedSubscriptionService(DbSubscriptionService service, SubscriptionServiceCache subscriptionServiceCache)
{
this.service = service ?? throw new ArgumentNullException("service");
cache = subscriptionServiceCache.Cache;
notifyRecord = subscriptionServiceCache.NotifyRecord;
notifyMethod = subscriptionServiceCache.NotifyMethod;
CacheExpiration = TimeSpan.FromMinutes(5);
}
public IEnumerable<SubscriptionRecord> GetSubscriptions(int tenant, string sourceId, string actionId)
{
var store = GetSubsciptionsStore(tenant, sourceId, actionId);
lock (store)
{
return store.GetSubscriptions();
}
}
public IEnumerable<SubscriptionRecord> GetSubscriptions(int tenant, string sourceId, string actionId, string recipientId, string objectId)
{
var store = GetSubsciptionsStore(tenant, sourceId, actionId);
lock (store)
{
return store.GetSubscriptions(recipientId, objectId);
}
}
public SubscriptionRecord GetSubscription(int tenant, string sourceId, string actionId, string recipientId, string objectId)
{
var store = GetSubsciptionsStore(tenant, sourceId, actionId);
lock (store)
{
return store.GetSubscription(recipientId, objectId);
}
}
public void SaveSubscription(SubscriptionRecord s)
{
service.SaveSubscription(s);
notifyRecord.Publish(s, CacheNotifyAction.InsertOrUpdate);
}
public void RemoveSubscriptions(int tenant, string sourceId, string actionId)
{
service.RemoveSubscriptions(tenant, sourceId, actionId);
notifyRecord.Publish(new SubscriptionRecord { Tenant = tenant, SourceId = sourceId, ActionId = actionId }, CacheNotifyAction.Remove);
}
public void RemoveSubscriptions(int tenant, string sourceId, string actionId, string objectId)
{
service.RemoveSubscriptions(tenant, sourceId, actionId, objectId);
notifyRecord.Publish(new SubscriptionRecord { Tenant = tenant, SourceId = sourceId, ActionId = actionId, ObjectId = objectId }, CacheNotifyAction.Remove);
}
public IEnumerable<SubscriptionMethod> GetSubscriptionMethods(int tenant, string sourceId, string actionId, string recipientId)
{
var store = GetSubsciptionsStore(tenant, sourceId, actionId);
lock (store)
{
return store.GetSubscriptionMethods(recipientId);
}
}
public void SetSubscriptionMethod(SubscriptionMethod m)
{
service.SetSubscriptionMethod(m);
notifyMethod.Publish(m, CacheNotifyAction.Any);
}
private SubsciptionsStore GetSubsciptionsStore(int tenant, string sourceId, string actionId)
{
var key = SubscriptionServiceCache.GetKey(tenant, sourceId, actionId);
var store = cache.Get<SubsciptionsStore>(key);
if (store == null)
{
var records = service.GetSubscriptions(tenant, sourceId, actionId);
var methods = service.GetSubscriptionMethods(tenant, sourceId, actionId, null);
cache.Insert(key, store = new SubsciptionsStore(records, methods), DateTime.UtcNow.Add(CacheExpiration));
}
return store;
}
}
internal class SubsciptionsStore
{
private readonly List<SubscriptionRecord> records;
private IDictionary<string, List<SubscriptionRecord>> recordsByRec;
private IDictionary<string, List<SubscriptionRecord>> recordsByObj;
private readonly List<SubscriptionMethod> methods;
private IDictionary<string, List<SubscriptionMethod>> methodsByRec;
public SubsciptionsStore(IEnumerable<SubscriptionRecord> records, IEnumerable<SubscriptionMethod> methods)
{
this.records = records.ToList();
this.methods = methods.ToList();
BuildSubscriptionsIndex(records);
BuildMethodsIndex(methods);
}
public IEnumerable<SubscriptionRecord> GetSubscriptions()
{
return records.ToList();
}
public IEnumerable<SubscriptionRecord> GetSubscriptions(string recipientId, string objectId)
{
return recipientId != null ?
recordsByRec.ContainsKey(recipientId) ? recordsByRec[recipientId].ToList() : new List<SubscriptionRecord>() :
recordsByObj.ContainsKey(objectId ?? string.Empty) ? recordsByObj[objectId ?? string.Empty].ToList() : new List<SubscriptionRecord>();
}
public SubscriptionRecord GetSubscription(string recipientId, string objectId)
{
return recordsByRec.ContainsKey(recipientId) ?
recordsByRec[recipientId].Where(s => s.ObjectId == objectId).FirstOrDefault() :
null;
}
public void SaveSubscription(SubscriptionRecord s)
{
var old = GetSubscription(s.RecipientId, s.ObjectId);
if (old != null)
{
old.Subscribed = s.Subscribed;
}
else
{
records.Add(s);
BuildSubscriptionsIndex(records);
}
}
public void RemoveSubscriptions()
{
records.Clear();
BuildSubscriptionsIndex(records);
}
public void RemoveSubscriptions(string objectId)
{
records.RemoveAll(s => s.ObjectId == objectId);
BuildSubscriptionsIndex(records);
}
public IEnumerable<SubscriptionMethod> GetSubscriptionMethods(string recipientId)
{
return string.IsNullOrEmpty(recipientId) ?
methods.ToList() :
methodsByRec.ContainsKey(recipientId) ? methodsByRec[recipientId].ToList() : new List<SubscriptionMethod>();
}
public void SetSubscriptionMethod(SubscriptionMethod m)
{
methods.RemoveAll(r => r.Tenant == m.Tenant && r.SourceId == m.SourceId && r.ActionId == m.ActionId && r.RecipientId == m.RecipientId);
if (m.Methods != null && 0 < m.Methods.Length)
{
methods.Add(m);
}
BuildMethodsIndex(methods);
}
private void BuildSubscriptionsIndex(IEnumerable<SubscriptionRecord> records)
{
recordsByRec = records.GroupBy(r => r.RecipientId).ToDictionary(g => g.Key, g => g.ToList());
recordsByObj = records.GroupBy(r => r.ObjectId ?? string.Empty).ToDictionary(g => g.Key, g => g.ToList());
}
private void BuildMethodsIndex(IEnumerable<SubscriptionMethod> methods)
{
methodsByRec = methods.GroupBy(r => r.RecipientId).ToDictionary(g => g.Key, g => g.ToList());
}
}
public static class SubscriptionConfigExtension
{
public static IServiceCollection AddSubscriptionService(this IServiceCollection services)
{
services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>));
services.TryAddScoped<DbSubscriptionService>();
services.TryAddScoped<ISubscriptionService, CachedSubscriptionService>();
services.TryAddSingleton<SubscriptionServiceCache>();
return services;
}
}
}

View File

@ -28,157 +28,54 @@ using System;
using System.Collections.Generic;
using ASC.Common.Caching;
using ASC.Core.Tenants;
using ASC.Common.Utils;
using ASC.Core.Data;
using ASC.Core.Tenants;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Core.Caching
{
public class CachedTenantService : ITenantService
{
private const string KEY = "tenants";
private readonly ITenantService service;
private readonly ICache cache;
private readonly ICacheNotify<TenantSetting> cacheNotifySettings;
private readonly ICacheNotify<TenantCacheItem> cacheNotifyItem;
public TimeSpan CacheExpiration { get; set; }
public TimeSpan SettingsExpiration { get; set; }
public CachedTenantService(ITenantService service)
{
class TenantServiceCache
{
private const string KEY = "tenants";
private TimeSpan CacheExpiration { get; set; }
internal ICache Cache { get; }
internal ICacheNotify<TenantCacheItem> CacheNotifyItem { get; }
internal ICacheNotify<TenantSetting> CacheNotifySettings { get; }
public TenantServiceCache(CoreBaseSettings coreBaseSettings, ICacheNotify<TenantCacheItem> cacheNotifyItem, ICacheNotify<TenantSetting> cacheNotifySettings)
{
this.service = service ?? throw new ArgumentNullException("service");
cache = AscCache.Memory;
CacheExpiration = TimeSpan.FromMinutes(2);
SettingsExpiration = TimeSpan.FromMinutes(2);
cacheNotifyItem = new KafkaCache<TenantCacheItem>();
cacheNotifySettings = new KafkaCache<TenantSetting>();
CacheNotifyItem = cacheNotifyItem;
CacheNotifySettings = cacheNotifySettings;
Cache = AscCache.Memory;
CacheExpiration = TimeSpan.FromMinutes(2);
cacheNotifyItem.Subscribe((t) =>
{
var tenants = GetTenantStore();
tenants.Remove(t.TenantId);
tenants.Clear();
tenants.Clear(coreBaseSettings);
}, CacheNotifyAction.InsertOrUpdate);
cacheNotifySettings.Subscribe((s) =>
{
cache.Remove(s.Key);
}, CacheNotifyAction.Remove);
}
public void ValidateDomain(string domain)
Cache.Remove(s.Key);
}, CacheNotifyAction.Remove);
}
internal TenantStore GetTenantStore()
{
service.ValidateDomain(domain);
}
public IEnumerable<Tenant> GetTenants(string login, string passwordHash)
{
return service.GetTenants(login, passwordHash);
}
public IEnumerable<Tenant> GetTenants(DateTime from, bool active = true)
{
return service.GetTenants(from, active);
}
public Tenant GetTenant(int id)
{
var tenants = GetTenantStore();
var t = tenants.Get(id);
if (t == null)
{
t = service.GetTenant(id);
if (t != null)
{
tenants.Insert(t);
}
}
return t;
}
public Tenant GetTenant(string domain)
{
var tenants = GetTenantStore();
var t = tenants.Get(domain);
if (t == null)
{
t = service.GetTenant(domain);
if (t != null)
{
tenants.Insert(t);
}
}
return t;
}
public Tenant GetTenantForStandaloneWithoutAlias(string ip)
{
var tenants = GetTenantStore();
var t = tenants.Get(ip);
if (t == null)
{
t = service.GetTenantForStandaloneWithoutAlias(ip);
if (t != null)
{
tenants.Insert(t, ip);
}
}
return t;
}
public Tenant SaveTenant(Tenant tenant)
{
tenant = service.SaveTenant(tenant);
cacheNotifyItem.Publish(new TenantCacheItem() { TenantId = tenant.TenantId }, CacheNotifyAction.InsertOrUpdate);
return tenant;
}
public void RemoveTenant(int id, bool auto = false)
{
service.RemoveTenant(id, auto);
cacheNotifyItem.Publish(new TenantCacheItem() { TenantId = id }, CacheNotifyAction.InsertOrUpdate);
}
public IEnumerable<TenantVersion> GetTenantVersions()
{
return service.GetTenantVersions();
}
public byte[] GetTenantSettings(int tenant, string key)
{
var cacheKey = string.Format("settings/{0}/{1}", tenant, key);
var data = cache.Get<byte[]>(cacheKey);
if (data == null)
{
data = service.GetTenantSettings(tenant, key);
cache.Insert(cacheKey, data ?? new byte[0], DateTime.UtcNow + SettingsExpiration);
}
return data == null ? null : data.Length == 0 ? null : data;
}
public void SetTenantSettings(int tenant, string key, byte[] data)
{
service.SetTenantSettings(tenant, key, data);
var cacheKey = string.Format("settings/{0}/{1}", tenant, key);
cacheNotifySettings.Publish(new TenantSetting { Key = cacheKey }, CacheNotifyAction.Remove);
}
private TenantStore GetTenantStore()
{
var store = cache.Get<TenantStore>(KEY);
var store = Cache.Get<TenantStore>(KEY);
if (store == null)
{
cache.Insert(KEY, store = new TenantStore(), DateTime.UtcNow.Add(CacheExpiration));
Cache.Insert(KEY, store = new TenantStore(), DateTime.UtcNow.Add(CacheExpiration));
}
return store;
}
class TenantStore
internal class TenantStore
{
private readonly Dictionary<int, Tenant> byId = new Dictionary<int, Tenant>();
private readonly Dictionary<string, Tenant> byDomain = new Dictionary<string, Tenant>();
@ -241,15 +138,150 @@ namespace ASC.Core.Caching
}
}
internal void Clear()
internal void Clear(CoreBaseSettings coreBaseSettings)
{
if (!CoreContext.Configuration.Standalone) return;
if (!coreBaseSettings.Standalone) return;
lock (locker)
{
byId.Clear();
byDomain.Clear();
}
}
}
}
class CachedTenantService : ITenantService
{
private readonly ITenantService service;
private readonly ICache cache;
private readonly ICacheNotify<TenantSetting> cacheNotifySettings;
private readonly ICacheNotify<TenantCacheItem> cacheNotifyItem;
private TimeSpan SettingsExpiration { get; set; }
private TenantServiceCache TenantServiceCache { get; }
public CachedTenantService(DbTenantService service, TenantServiceCache tenantServiceCache)
{
this.service = service ?? throw new ArgumentNullException("service");
cache = AscCache.Memory;
SettingsExpiration = TimeSpan.FromMinutes(2);
TenantServiceCache = tenantServiceCache;
cacheNotifyItem = tenantServiceCache.CacheNotifyItem;
cacheNotifySettings = tenantServiceCache.CacheNotifySettings;
}
public void ValidateDomain(string domain)
{
service.ValidateDomain(domain);
}
public IEnumerable<Tenant> GetTenants(string login, string passwordHash)
{
return service.GetTenants(login, passwordHash);
}
public IEnumerable<Tenant> GetTenants(DateTime from, bool active = true)
{
return service.GetTenants(from, active);
}
public Tenant GetTenant(int id)
{
var tenants = TenantServiceCache.GetTenantStore();
var t = tenants.Get(id);
if (t == null)
{
t = service.GetTenant(id);
if (t != null)
{
tenants.Insert(t);
}
}
return t;
}
public Tenant GetTenant(string domain)
{
var tenants = TenantServiceCache.GetTenantStore();
var t = tenants.Get(domain);
if (t == null)
{
t = service.GetTenant(domain);
if (t != null)
{
tenants.Insert(t);
}
}
return t;
}
public Tenant GetTenantForStandaloneWithoutAlias(string ip)
{
var tenants = TenantServiceCache.GetTenantStore();
var t = tenants.Get(ip);
if (t == null)
{
t = service.GetTenantForStandaloneWithoutAlias(ip);
if (t != null)
{
tenants.Insert(t, ip);
}
}
return t;
}
public Tenant SaveTenant(CoreSettings coreSettings, Tenant tenant)
{
tenant = service.SaveTenant(coreSettings, tenant);
cacheNotifyItem.Publish(new TenantCacheItem() { TenantId = tenant.TenantId }, CacheNotifyAction.InsertOrUpdate);
return tenant;
}
public void RemoveTenant(int id, bool auto = false)
{
service.RemoveTenant(id, auto);
cacheNotifyItem.Publish(new TenantCacheItem() { TenantId = id }, CacheNotifyAction.InsertOrUpdate);
}
public IEnumerable<TenantVersion> GetTenantVersions()
{
return service.GetTenantVersions();
}
public byte[] GetTenantSettings(int tenant, string key)
{
var cacheKey = string.Format("settings/{0}/{1}", tenant, key);
var data = cache.Get<byte[]>(cacheKey);
if (data == null)
{
data = service.GetTenantSettings(tenant, key);
cache.Insert(cacheKey, data ?? new byte[0], DateTime.UtcNow + SettingsExpiration);
}
return data == null ? null : data.Length == 0 ? null : data;
}
public void SetTenantSettings(int tenant, string key, byte[] data)
{
service.SetTenantSettings(tenant, key, data);
var cacheKey = string.Format("settings/{0}/{1}", tenant, key);
cacheNotifySettings.Publish(new TenantSetting { Key = cacheKey }, CacheNotifyAction.Remove);
}
}
public static class TenantConfigExtension
{
public static IServiceCollection AddTenantService(this IServiceCollection services)
{
services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>));
services.TryAddSingleton<TenantDomainValidator>();
services.TryAddSingleton<TimeZoneConverter>();
services.TryAddSingleton<TenantServiceCache>();
services.TryAddScoped<DbTenantService>();
services.TryAddScoped<ITenantService, CachedTenantService>();
return services.AddCoreBaseSettingsService();
}
}
}

View File

@ -29,56 +29,146 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using ASC.Common.Caching;
using ASC.Core.Data;
using ASC.Core.Tenants;
using ASC.Core.Users;
using ASC.Core.Users;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Core.Caching
{
public class CachedUserService : IUserService, ICachedService
class UserServiceCache
{
private const string USERS = "users";
public const string USERS = "users";
private const string GROUPS = "groups";
private const string REFS = "refs";
public const string REFS = "refs";
public TrustInterval TrustInterval { get; set; }
public ICache Cache { get; }
public CoreBaseSettings CoreBaseSettings { get; }
public ICacheNotify<UserInfoCacheItem> CacheUserInfoItem { get; }
public ICacheNotify<UserPhotoCacheItem> CacheUserPhotoItem { get; }
public ICacheNotify<GroupCacheItem> CacheGroupCacheItem { get; }
public ICacheNotify<UserGroupRefCacheItem> CacheUserGroupRefItem { get; }
public UserServiceCache(
CoreBaseSettings coreBaseSettings,
ICacheNotify<UserInfoCacheItem> cacheUserInfoItem,
ICacheNotify<UserPhotoCacheItem> cacheUserPhotoItem,
ICacheNotify<GroupCacheItem> cacheGroupCacheItem,
ICacheNotify<UserGroupRefCacheItem> cacheUserGroupRefItem)
{
TrustInterval = new TrustInterval();
Cache = AscCache.Memory;
CoreBaseSettings = coreBaseSettings;
CacheUserInfoItem = cacheUserInfoItem;
CacheUserPhotoItem = cacheUserPhotoItem;
CacheGroupCacheItem = cacheGroupCacheItem;
CacheUserGroupRefItem = cacheUserGroupRefItem;
cacheUserInfoItem.Subscribe((u) => InvalidateCache(u), CacheNotifyAction.Any);
cacheUserPhotoItem.Subscribe((p) => Cache.Remove(p.Key), CacheNotifyAction.Remove);
cacheGroupCacheItem.Subscribe((g) => InvalidateCache(), CacheNotifyAction.Any);
cacheUserGroupRefItem.Subscribe((r) => UpdateUserGroupRefCache(r, true), CacheNotifyAction.Remove);
cacheUserGroupRefItem.Subscribe((r) => UpdateUserGroupRefCache(r, false), CacheNotifyAction.InsertOrUpdate);
}
public void InvalidateCache()
{
InvalidateCache(null);
}
private void InvalidateCache(UserInfoCacheItem userInfo)
{
if (CoreBaseSettings.Personal && userInfo != null)
{
var key = GetUserCacheKeyForPersonal(userInfo.Tenant, userInfo.ID.FromByteString());
Cache.Remove(key);
}
TrustInterval.Expire();
}
private void UpdateUserGroupRefCache(UserGroupRef r, bool remove)
{
var key = GetRefCacheKey(r.Tenant);
var refs = Cache.Get<UserGroupRefStore>(key);
if (!remove && refs != null)
{
lock (refs)
{
refs[r.CreateKey()] = r;
}
}
else
{
InvalidateCache();
}
}
public static string GetUserPhotoCacheKey(int tenant, Guid userId)
{
return tenant.ToString() + "userphoto" + userId.ToString();
}
public static string GetGroupCacheKey(int tenant)
{
return tenant.ToString() + GROUPS;
}
public static string GetRefCacheKey(int tenant)
{
return tenant.ToString() + REFS;
}
public static string GetUserCacheKey(int tenant)
{
return tenant.ToString() + USERS;
}
public static string GetUserCacheKeyForPersonal(int tenant, Guid userId)
{
return tenant.ToString() + USERS + userId;
}
}
class CachedUserService : IUserService, ICachedService
{
private readonly IUserService service;
private readonly ICache cache;
private readonly ICacheNotify<UserGroupRefCacheItem> cacheUserGroupRefItem;
private readonly ICacheNotify<UserPhotoCacheItem> cacheUserPhotoItem;
private readonly ICacheNotify<UserInfoCacheItem> cacheUserInfoItem;
private readonly ICacheNotify<GroupCacheItem> cacheGroupCacheItem;
private readonly TrustInterval trustInterval;
private int getchanges;
private TimeSpan CacheExpiration { get; set; }
private TimeSpan DbExpiration { get; set; }
private TimeSpan PhotoExpiration { get; set; }
private CoreBaseSettings CoreBaseSettings { get; }
private UserServiceCache UserServiceCache { get; }
private ICacheNotify<UserInfoCacheItem> CacheUserInfoItem { get; }
private ICacheNotify<UserPhotoCacheItem> CacheUserPhotoItem { get; }
private ICacheNotify<GroupCacheItem> CacheGroupCacheItem { get; }
private ICacheNotify<UserGroupRefCacheItem> CacheUserGroupRefItem { get; }
public TimeSpan CacheExpiration { get; set; }
public TimeSpan DbExpiration { get; set; }
public TimeSpan PhotoExpiration { get; set; }
public CachedUserService(IUserService service)
{
public CachedUserService(
DbUserService service,
CoreBaseSettings coreBaseSettings,
UserServiceCache userServiceCache
)
{
this.service = service ?? throw new ArgumentNullException("service");
cache = AscCache.Memory;
trustInterval = new TrustInterval();
CoreBaseSettings = coreBaseSettings;
UserServiceCache = userServiceCache;
cache = userServiceCache.Cache;
CacheUserInfoItem = userServiceCache.CacheUserInfoItem;
CacheUserPhotoItem = userServiceCache.CacheUserPhotoItem;
CacheGroupCacheItem = userServiceCache.CacheGroupCacheItem;
CacheUserGroupRefItem = userServiceCache.CacheUserGroupRefItem;
trustInterval = userServiceCache.TrustInterval;
CacheExpiration = TimeSpan.FromMinutes(20);
DbExpiration = TimeSpan.FromMinutes(1);
PhotoExpiration = TimeSpan.FromMinutes(10);
cacheUserInfoItem = new KafkaCache<UserInfoCacheItem>();
cacheUserPhotoItem = new KafkaCache<UserPhotoCacheItem>();
cacheGroupCacheItem = new KafkaCache<GroupCacheItem>();
cacheUserGroupRefItem = new KafkaCache<UserGroupRefCacheItem>();
cacheUserInfoItem.Subscribe((u) => InvalidateCache(u), CacheNotifyAction.Any);
cacheUserPhotoItem.Subscribe((p) => cache.Remove(p.Key), CacheNotifyAction.Remove);
cacheGroupCacheItem.Subscribe((g) => InvalidateCache(), CacheNotifyAction.Any);
cacheUserGroupRefItem.Subscribe((r) => UpdateUserGroupRefCache(r, true), CacheNotifyAction.Remove);
cacheUserGroupRefItem.Subscribe((r) => UpdateUserGroupRefCache(r, false), CacheNotifyAction.InsertOrUpdate);
}
@ -89,16 +179,16 @@ namespace ASC.Core.Caching
{
return (from == default ? users.Values : users.Values.Where(u => u.LastModified >= from)).ToDictionary(u => u.ID);
}
}
public List<UserInfo> GetUsers(int tenant, bool isAdmin, EmployeeStatus? employeeStatus, List<List<Guid>> includeGroups, List<Guid> excludeGroups, EmployeeActivationStatus? activationStatus, string text, string sortBy, bool sortOrderAsc, long limit, long offset, out int total)
{
return service.GetUsers(tenant, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text, sortBy, sortOrderAsc, limit, offset, out total);
}
}
public List<UserInfo> GetUsers(int tenant, bool isAdmin, EmployeeStatus? employeeStatus, List<List<Guid>> includeGroups, List<Guid> excludeGroups, EmployeeActivationStatus? activationStatus, string text, string sortBy, bool sortOrderAsc, long limit, long offset, out int total)
{
return service.GetUsers(tenant, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text, sortBy, sortOrderAsc, limit, offset, out total);
}
public UserInfo GetUser(int tenant, Guid id)
{
if (CoreContext.Configuration.Personal)
if (CoreBaseSettings.Personal)
{
return GetUserForPersonal(tenant, id);
}
@ -119,9 +209,9 @@ namespace ASC.Core.Caching
/// <returns></returns>
private UserInfo GetUserForPersonal(int tenant, Guid id)
{
if (!CoreContext.Configuration.Personal) return GetUser(tenant, id);
if (!CoreBaseSettings.Personal) return GetUser(tenant, id);
var key = GetUserCacheKeyForPersonal(tenant, id);
var key = UserServiceCache.GetUserCacheKeyForPersonal(tenant, id);
var user = cache.Get<UserInfo>(key);
if (user == null)
@ -145,23 +235,23 @@ namespace ASC.Core.Caching
public UserInfo SaveUser(int tenant, UserInfo user)
{
user = service.SaveUser(tenant, user);
cacheUserInfoItem.Publish(new UserInfoCacheItem { ID = user.ID.ToByteString(), Tenant = tenant }, CacheNotifyAction.Any);
CacheUserInfoItem.Publish(new UserInfoCacheItem { ID = user.ID.ToByteString(), Tenant = tenant }, CacheNotifyAction.Any);
return user;
}
public void RemoveUser(int tenant, Guid id)
{
service.RemoveUser(tenant, id);
cacheUserInfoItem.Publish(new UserInfoCacheItem { Tenant = tenant, ID = id.ToByteString() }, CacheNotifyAction.Any);
CacheUserInfoItem.Publish(new UserInfoCacheItem { Tenant = tenant, ID = id.ToByteString() }, CacheNotifyAction.Any);
}
public byte[] GetUserPhoto(int tenant, Guid id)
{
var photo = cache.Get<byte[]>(GetUserPhotoCacheKey(tenant, id));
var photo = cache.Get<byte[]>(UserServiceCache.GetUserPhotoCacheKey(tenant, id));
if (photo == null)
{
photo = service.GetUserPhoto(tenant, id);
cache.Insert(GetUserPhotoCacheKey(tenant, id), photo, PhotoExpiration);
cache.Insert(UserServiceCache.GetUserPhotoCacheKey(tenant, id), photo, PhotoExpiration);
}
return photo;
}
@ -169,7 +259,7 @@ namespace ASC.Core.Caching
public void SetUserPhoto(int tenant, Guid id, byte[] photo)
{
service.SetUserPhoto(tenant, id, photo);
cacheUserPhotoItem.Publish(new UserPhotoCacheItem { Key = GetUserPhotoCacheKey(tenant, id) }, CacheNotifyAction.Remove);
CacheUserPhotoItem.Publish(new UserPhotoCacheItem { Key = UserServiceCache.GetUserPhotoCacheKey(tenant, id) }, CacheNotifyAction.Remove);
}
public string GetUserPassword(int tenant, Guid id)
@ -205,14 +295,14 @@ namespace ASC.Core.Caching
public Group SaveGroup(int tenant, Group group)
{
group = service.SaveGroup(tenant, group);
cacheGroupCacheItem.Publish(new GroupCacheItem { ID = group.Id.ToString() }, CacheNotifyAction.Any);
CacheGroupCacheItem.Publish(new GroupCacheItem { ID = group.Id.ToString() }, CacheNotifyAction.Any);
return group;
}
public void RemoveGroup(int tenant, Guid id)
{
service.RemoveGroup(tenant, id);
cacheGroupCacheItem.Publish(new GroupCacheItem { ID = id.ToString() }, CacheNotifyAction.Any);
CacheGroupCacheItem.Publish(new GroupCacheItem { ID = id.ToString() }, CacheNotifyAction.Any);
}
@ -220,7 +310,7 @@ namespace ASC.Core.Caching
{
GetChangesFromDb();
var key = GetRefCacheKey(tenant);
var key = UserServiceCache.GetRefCacheKey(tenant);
if (!(cache.Get<UserGroupRefStore>(key) is IDictionary<string, UserGroupRef> refs))
{
refs = service.GetUserGroupRefs(tenant, default);
@ -235,7 +325,7 @@ namespace ASC.Core.Caching
public UserGroupRef SaveUserGroupRef(int tenant, UserGroupRef r)
{
r = service.SaveUserGroupRef(tenant, r);
cacheUserGroupRefItem.Publish(r, CacheNotifyAction.InsertOrUpdate);
CacheUserGroupRefItem.Publish(r, CacheNotifyAction.InsertOrUpdate);
return r;
}
@ -244,23 +334,7 @@ namespace ASC.Core.Caching
service.RemoveUserGroupRef(tenant, userId, groupId, refType);
var r = new UserGroupRef(userId, groupId, refType) { Tenant = tenant };
cacheUserGroupRefItem.Publish(r, CacheNotifyAction.Remove);
}
public void InvalidateCache()
{
InvalidateCache(null);
}
private void InvalidateCache(UserInfoCacheItem userInfo)
{
if (CoreContext.Configuration.Personal && userInfo != null)
{
var key = GetUserCacheKeyForPersonal(userInfo.Tenant, userInfo.ID.FromByteString());
cache.Remove(key);
}
trustInterval.Expire();
CacheUserGroupRefItem.Publish(r, CacheNotifyAction.Remove);
}
@ -268,7 +342,7 @@ namespace ASC.Core.Caching
{
GetChangesFromDb();
var key = GetUserCacheKey(tenant);
var key = UserServiceCache.GetUserCacheKey(tenant);
var users = cache.Get<IDictionary<Guid, UserInfo>>(key);
if (users == null)
{
@ -283,7 +357,7 @@ namespace ASC.Core.Caching
{
GetChangesFromDb();
var key = GetGroupCacheKey(tenant);
var key = UserServiceCache.GetGroupCacheKey(tenant);
var groups = cache.Get<IDictionary<Guid, Group>>(key);
if (groups == null)
{
@ -321,7 +395,7 @@ namespace ASC.Core.Caching
//get and merge changes in cached tenants
foreach (var tenantGroup in service.GetUsers(Tenant.DEFAULT_TENANT, starttime).Values.GroupBy(u => u.Tenant))
{
var users = cache.Get<IDictionary<Guid, UserInfo>>(GetUserCacheKey(tenantGroup.Key));
var users = cache.Get<IDictionary<Guid, UserInfo>>(UserServiceCache.GetUserCacheKey(tenantGroup.Key));
if (users != null)
{
lock (users)
@ -336,7 +410,7 @@ namespace ASC.Core.Caching
foreach (var tenantGroup in service.GetGroups(Tenant.DEFAULT_TENANT, starttime).Values.GroupBy(g => g.Tenant))
{
var groups = cache.Get<IDictionary<Guid, Group>>(GetGroupCacheKey(tenantGroup.Key));
var groups = cache.Get<IDictionary<Guid, Group>>(UserServiceCache.GetGroupCacheKey(tenantGroup.Key));
if (groups != null)
{
lock (groups)
@ -351,7 +425,7 @@ namespace ASC.Core.Caching
foreach (var tenantGroup in service.GetUserGroupRefs(Tenant.DEFAULT_TENANT, starttime).Values.GroupBy(r => r.Tenant))
{
var refs = cache.Get<UserGroupRefStore>(GetRefCacheKey(tenantGroup.Key));
var refs = cache.Get<UserGroupRefStore>(UserServiceCache.GetRefCacheKey(tenantGroup.Key));
if (refs != null)
{
lock (refs)
@ -371,46 +445,10 @@ namespace ASC.Core.Caching
}
}
private void UpdateUserGroupRefCache(UserGroupRef r, bool remove)
{
var key = GetRefCacheKey(r.Tenant);
var refs = cache.Get<UserGroupRefStore>(key);
if (!remove && refs != null)
{
lock (refs)
{
refs[r.CreateKey()] = r;
}
}
else
{
InvalidateCache();
}
}
private static string GetUserPhotoCacheKey(int tenant, Guid userId)
public void InvalidateCache()
{
return tenant.ToString() + "userphoto" + userId.ToString();
}
private static string GetGroupCacheKey(int tenant)
{
return tenant.ToString() + GROUPS;
}
private static string GetRefCacheKey(int tenant)
{
return tenant.ToString() + REFS;
}
private static string GetUserCacheKey(int tenant)
{
return tenant.ToString() + USERS;
}
private static string GetUserCacheKeyForPersonal(int tenant, Guid userId)
{
return tenant.ToString() + USERS + userId;
UserServiceCache.InvalidateCache();
}
[Serializable]
@ -419,4 +457,16 @@ namespace ASC.Core.Caching
public string Key { get; set; }
}
}
public static class UserConfigExtension
{
public static IServiceCollection AddUserService(this IServiceCollection services)
{
services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>));
services.AddCoreSettingsService();
services.TryAddScoped<DbUserService>();
services.TryAddScoped<IUserService, CachedUserService>();
services.TryAddSingleton<UserServiceCache>();
return services;
}
}
}

View File

@ -24,16 +24,19 @@
*/
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using ASC.Common.Module;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Core.Configuration
{
public class AmiPublicDnsSyncService : IServiceController
{
{
public static IServiceProvider ServiceProvider { get; set; }
public void Start()
{
Synchronize();
@ -45,17 +48,20 @@ namespace ASC.Core.Configuration
}
public static void Synchronize()
{
if (CoreContext.Configuration.Standalone)
{
using var scope = ServiceProvider.CreateScope();
var tenantManager = scope.ServiceProvider.GetService<TenantManager>();
var coreBaseSettings = scope.ServiceProvider.GetService<CoreBaseSettings>();
if (coreBaseSettings.Standalone)
{
var tenants = CoreContext.TenantManager.GetTenants(false).Where(t => MappedDomainNotSettedByUser(t.MappedDomain));
var tenants = tenantManager.GetTenants(false).Where(t => MappedDomainNotSettedByUser(t.MappedDomain));
if (tenants.Any())
{
var dnsname = GetAmiPublicDnsName();
foreach (var tenant in tenants.Where(t => !string.IsNullOrEmpty(dnsname) && t.MappedDomain != dnsname))
{
tenant.MappedDomain = dnsname;
CoreContext.TenantManager.SaveTenant(tenant);
tenantManager.SaveTenant(tenant);
}
}
}

View File

@ -23,24 +23,19 @@
*
*/
using System;
using System.ComponentModel;
using System.Globalization;
using ASC.Common.Data;
namespace ASC.Core.Configuration
{
class ConnectionStringNameTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
{
//todo: remove
//class ConnectionStringNameTypeConverter : TypeConverter
//{
// public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
// {
// return sourceType == typeof(string);
// }
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return DbRegistry.GetConnectionString((string)value);
}
}
// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
// {
// return DbRegistry.GetConnectionString((string)value);
// }
//}
}

View File

@ -29,16 +29,14 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ASC.Common.Caching;
using ASC.Common.Utils;
using ASC.Core.Tenants;
using Autofac;
using Autofac;
using Microsoft.Extensions.Configuration;
namespace ASC.Core.Common.Configuration
{
public class Consumer : IDictionary<string, string>
{
private static readonly ICacheNotify<ConsumerCacheItem> Cache = new KafkaCache<ConsumerCacheItem>();
public bool CanSet { get; private set; }
public int Order { get; private set; }
@ -75,27 +73,55 @@ namespace ASC.Core.Common.Configuration
}
}
private static readonly bool OnlyDefault;
private readonly bool OnlyDefault;
public TenantManager TenantManager { get; set; }
public CoreBaseSettings CoreBaseSettings { get; set; }
public CoreSettings CoreSettings { get; set; }
public IConfiguration Configuration { get; }
public ICacheNotify<ConsumerCacheItem> Cache { get; }
public bool IsSet
{
get { return Props.Any() && !Props.All(r => string.IsNullOrEmpty(this[r.Key])); }
}
static Consumer()
{
OnlyDefault = ConfigurationManager.AppSettings["core:default-consumers"] == "true";
{
}
public Consumer()
{
Name = "";
Order = int.MaxValue;
{
Props = new Dictionary<string, string>();
Additional = new Dictionary<string, string>();
}
public Consumer(
TenantManager tenantManager,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings,
IConfiguration configuration,
ICacheNotify<ConsumerCacheItem> cache) : this()
{
TenantManager = tenantManager;
CoreBaseSettings = coreBaseSettings;
CoreSettings = coreSettings;
Configuration = configuration;
Cache = cache;
OnlyDefault = configuration["core:default-consumers"] == "true";
Name = "";
Order = int.MaxValue;
}
public Consumer(string name, int order, Dictionary<string, string> additional)
public Consumer(
TenantManager tenantManager,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings,
IConfiguration configuration,
ICacheNotify<ConsumerCacheItem> cache,
string name, int order, Dictionary<string, string> additional)
: this(tenantManager, coreBaseSettings, coreSettings, configuration, cache)
{
Name = name;
Order = order;
@ -103,7 +129,14 @@ namespace ASC.Core.Common.Configuration
Additional = additional;
}
public Consumer(string name, int order, Dictionary<string, string> props, Dictionary<string, string> additional)
public Consumer(
TenantManager tenantManager,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings,
IConfiguration configuration,
ICacheNotify<ConsumerCacheItem> cache,
string name, int order, Dictionary<string, string> props, Dictionary<string, string> additional)
: this(tenantManager, coreBaseSettings, coreSettings, configuration, cache)
{
Name = name;
Order = order;
@ -194,11 +227,11 @@ namespace ASC.Core.Common.Configuration
if (!OnlyDefault && CanSet)
{
var tenant = CoreContext.Configuration.Standalone
var tenant = CoreBaseSettings.Standalone
? Tenant.DEFAULT_TENANT
: CoreContext.TenantManager.GetCurrentTenant().TenantId;
: TenantManager.GetCurrentTenant().TenantId;
value = CoreContext.Configuration.GetSetting(GetSettingsKey(name), tenant);
value = CoreSettings.GetSetting(GetSettingsKey(name), tenant);
}
if (string.IsNullOrEmpty(value))
@ -232,10 +265,10 @@ namespace ASC.Core.Common.Configuration
return;
}
var tenant = CoreContext.Configuration.Standalone
var tenant = CoreBaseSettings.Standalone
? Tenant.DEFAULT_TENANT
: CoreContext.TenantManager.GetCurrentTenant().TenantId;
CoreContext.Configuration.SaveSetting(GetSettingsKey(name), value, tenant);
: TenantManager.GetCurrentTenant().TenantId;
CoreSettings.SaveSetting(GetSettingsKey(name), value, tenant);
}
protected virtual string GetSettingsKey(string name)
@ -251,20 +284,43 @@ namespace ASC.Core.Common.Configuration
public const string HandlerTypeKey = "handlerType";
public const string CdnKey = "cdn";
public DataStoreConsumer() : base()
{
}
public DataStoreConsumer()
public DataStoreConsumer(
TenantManager tenantManager,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings,
IConfiguration configuration,
ICacheNotify<ConsumerCacheItem> cache)
: base(tenantManager, coreBaseSettings, coreSettings, configuration, cache)
{
}
public DataStoreConsumer(string name, int order, Dictionary<string, string> additional)
: base(name, order, additional)
public DataStoreConsumer(
TenantManager tenantManager,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings,
IConfiguration configuration,
ICacheNotify<ConsumerCacheItem> cache,
string name, int order, Dictionary<string, string> additional)
: base(tenantManager, coreBaseSettings, coreSettings, configuration, cache, name, order, additional)
{
Init(additional);
}
public DataStoreConsumer(string name, int order, Dictionary<string, string> props, Dictionary<string, string> additional)
: base(name, order, props, additional)
public DataStoreConsumer(
TenantManager tenantManager,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings,
IConfiguration configuration,
ICacheNotify<ConsumerCacheItem> cache,
string name, int order, Dictionary<string, string> props, Dictionary<string, string> additional)
: base(tenantManager, coreBaseSettings, coreSettings, configuration, cache, name, order, props, additional)
{
Init(additional);
}
@ -300,26 +356,23 @@ namespace ASC.Core.Common.Configuration
var additional = fromConfig.AdditionalKeys.ToDictionary(prop => prop, prop => fromConfig[prop]);
additional.Add(HandlerTypeKey, HandlerType.AssemblyQualifiedName);
return new DataStoreConsumer(fromConfig.Name, fromConfig.Order, props, additional);
return new DataStoreConsumer(fromConfig.TenantManager, fromConfig.CoreBaseSettings, fromConfig.CoreSettings, fromConfig.Configuration, fromConfig.Cache, fromConfig.Name, fromConfig.Order, props, additional);
}
public object Clone()
{
return new DataStoreConsumer(Name, Order, Props.ToDictionary(r => r.Key, r => r.Value), Additional.ToDictionary(r => r.Key, r => r.Value));
return new DataStoreConsumer(TenantManager, CoreBaseSettings, CoreSettings, Configuration, Cache, Name, Order, Props.ToDictionary(r => r.Key, r => r.Value), Additional.ToDictionary(r => r.Key, r => r.Value));
}
}
public class ConsumerFactory
{
public static IEnumerable<Consumer> Consumers { get; private set; }
private static IContainer Builder { get; set; }
static ConsumerFactory()
{
var container = ConsumerConfigLoader.LoadConsumers("consumers");
Builder = container.Build();
Consumers = Builder.Resolve<IEnumerable<Consumer>>();
}
public static Consumer GetByName(string name)

View File

@ -1,95 +0,0 @@
/*
*
* (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.Configuration;
using ASC.Common.Data;
using ASC.Core.Billing;
using ASC.Core.Caching;
using ASC.Core.Data;
namespace ASC.Core
{
public static class CoreContext
{
static CoreContext()
{
ConfigureCoreContextByDefault();
}
public static CoreConfiguration Configuration { get; private set; }
public static TenantManager TenantManager { get; private set; }
public static UserManager UserManager { get; private set; }
public static AuthManager Authentication { get; private set; }
public static AuthorizationManager AuthorizationManager { get; private set; }
public static PaymentManager PaymentManager { get; private set; }
internal static SubscriptionManager SubscriptionManager { get; private set; }
private static bool QuotaCacheEnabled
{
get
{
if (ConfigurationManager.AppSettings["core.enable-quota-cache"] == null)
return true;
return !bool.TryParse(ConfigurationManager.AppSettings["core.enable-quota-cache"], out var enabled) || enabled;
}
}
private static void ConfigureCoreContextByDefault()
{
var cs = DbRegistry.GetConnectionString("core");
if (cs == null)
{
throw new ConfigurationErrorsException("Can not configure CoreContext: connection string with name core not found.");
}
var tenantService = new CachedTenantService(new DbTenantService(cs));
var userService = new CachedUserService(new DbUserService(cs));
var azService = new CachedAzService(new DbAzService(cs));
var quotaService = QuotaCacheEnabled ? (IQuotaService)new CachedQuotaService(new DbQuotaService(cs)) : new DbQuotaService(cs);
var subService = new CachedSubscriptionService(new DbSubscriptionService(cs));
var tariffService = new TariffService(cs, quotaService, tenantService);
Configuration = new CoreConfiguration(tenantService);
TenantManager = new TenantManager(tenantService, quotaService, tariffService);
PaymentManager = new PaymentManager(Configuration, quotaService, tariffService);
UserManager = new UserManager(userService);
Authentication = new AuthManager(userService);
AuthorizationManager = new AuthorizationManager(azService);
SubscriptionManager = new SubscriptionManager(subService);
}
}
}

View File

@ -27,26 +27,33 @@
using System;
using System.Linq;
using ASC.Common.Security.Authentication;
using ASC.Core.Caching;
using ASC.Core.Security.Authentication;
using ASC.Core.Tenants;
using ASC.Core.Users;
using ASC.Core.Users;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Core
{
public class AuthManager
{
private readonly IUserService userService;
public AuthManager(IUserService service)
private readonly IUserService userService;
public UserManager UserManager { get; }
public UserFormatter UserFormatter { get; }
public AuthManager(IUserService service, UserManager userManager, UserFormatter userFormatter)
{
this.userService = service;
userService = service;
UserManager = userManager;
UserFormatter = userFormatter;
}
public IUserAccount[] GetUserAccounts(Tenant tenant)
{
return CoreContext.UserManager.GetUsers(tenant, EmployeeStatus.Active).Select(u => ToAccount(tenant.TenantId, u)).ToArray();
return UserManager.GetUsers(EmployeeStatus.Active).Select(u => ToAccount(tenant.TenantId, u)).ToArray();
}
public void SetUserPassword(int tenantId, Guid userID, string password)
@ -64,14 +71,25 @@ namespace ASC.Core
var s = ASC.Core.Configuration.Constants.SystemAccounts.FirstOrDefault(a => a.ID == id);
if (s != null) return s;
var u = CoreContext.UserManager.GetUsers(tenantId, id);
var u = UserManager.GetUsers(id);
return !Constants.LostUser.Equals(u) && u.Status == EmployeeStatus.Active ? (IAccount)ToAccount(tenantId, u) : ASC.Core.Configuration.Constants.Guest;
}
private IUserAccount ToAccount(int tenantId, UserInfo u)
{
return new UserAccount(u, tenantId);
return new UserAccount(u, tenantId, UserFormatter);
}
}
}
public static class AuthManagerExtension
{
public static IServiceCollection AddAuthManager(this IServiceCollection services)
{
services.TryAddScoped<AuthManager>();
return services
.AddUserService()
.AddUserFormatter()
.AddUserManagerService();
}
}
}

View File

@ -29,23 +29,28 @@ using System.Collections.Generic;
using System.Linq;
using ASC.Common.Security;
using ASC.Common.Security.Authorizing;
using ASC.Core.Caching;
using ASC.Core.Caching;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Core
{
public class AuthorizationManager
{
private readonly IAzService service;
public AuthorizationManager(IAzService service)
private readonly IAzService service;
private TenantManager TenantManager { get; }
public AuthorizationManager(IAzService service, TenantManager tenantManager)
{
this.service = service;
this.service = service;
TenantManager = tenantManager;
}
public IEnumerable<AzRecord> GetAces(Guid subjectId, Guid actionId)
{
var aces = service.GetAces(CoreContext.TenantManager.GetCurrentTenant().TenantId, default);
var aces = service.GetAces(TenantManager.GetCurrentTenant().TenantId, default);
return aces
.Where(a => a.ActionId == actionId && (a.SubjectId == subjectId || subjectId == Guid.Empty))
.ToList();
@ -53,7 +58,7 @@ namespace ASC.Core
public IEnumerable<AzRecord> GetAces(Guid subjectId, Guid actionId, ISecurityObjectId objectId)
{
var aces = service.GetAces(CoreContext.TenantManager.GetCurrentTenant().TenantId, default);
var aces = service.GetAces(TenantManager.GetCurrentTenant().TenantId, default);
return FilterAces(aces, subjectId, actionId, objectId)
.ToList();
}
@ -66,7 +71,7 @@ namespace ASC.Core
}
var result = new List<AzRecord>();
var aces = service.GetAces(CoreContext.TenantManager.GetCurrentTenant().TenantId, default);
var aces = service.GetAces(TenantManager.GetCurrentTenant().TenantId, default);
result.AddRange(FilterAces(aces, subjectId, actionId, objectId));
var inherits = new List<AzRecord>();
@ -84,12 +89,12 @@ namespace ASC.Core
public void AddAce(AzRecord r)
{
service.SaveAce(CoreContext.TenantManager.GetCurrentTenant().TenantId, r);
service.SaveAce(TenantManager.GetCurrentTenant().TenantId, r);
}
public void RemoveAce(AzRecord r)
{
service.RemoveAce(CoreContext.TenantManager.GetCurrentTenant().TenantId, r);
service.RemoveAce(TenantManager.GetCurrentTenant().TenantId, r);
}
public void RemoveAllAces(ISecurityObjectId id)
@ -102,7 +107,7 @@ namespace ASC.Core
private IEnumerable<AzRecord> GetAcesInternal()
{
return service.GetAces(CoreContext.TenantManager.GetCurrentTenant().TenantId, default);
return service.GetAces(TenantManager.GetCurrentTenant().TenantId, default);
}
private IEnumerable<AzRecord> DistinctAces(IEnumerable<AzRecord> inheritAces)
@ -122,5 +127,16 @@ namespace ASC.Core
store.Get(objId).Where(a => (a.SubjectId == subjectId || subjectId == Guid.Empty) && (a.ActionId == actionId || actionId == Guid.Empty)) :
aces.Where(a => (a.SubjectId == subjectId || subjectId == Guid.Empty) && (a.ActionId == actionId || actionId == Guid.Empty) && a.ObjectId == objId);
}
}
public static class AuthorizationManagerConfigExtension
{
public static IServiceCollection AddAuthorizationManagerService(this IServiceCollection services)
{
services.TryAddScoped<AuthorizationManager>();
return services
.AddAzService()
.AddTenantManagerService();
}
}
}

View File

@ -26,63 +26,196 @@
using System;
using System.Text;
using ASC.Common.Utils;
using ASC.Core.Caching;
using ASC.Core.Common.Settings;
using ASC.Core.Configuration;
using ASC.Core.Tenants;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Newtonsoft.Json;
namespace ASC.Core
{
public class CoreConfiguration
public class CoreBaseSettings
{
private readonly ITenantService tenantService;
private bool? standalone;
private bool? personal;
private bool? customMode;
private long? personalMaxSpace;
private string basedomain;
public IConfiguration Configuration { get; }
public CoreConfiguration(ITenantService service)
public CoreBaseSettings(IConfiguration configuration)
{
tenantService = service;
Configuration = configuration;
}
public bool Standalone
{
get { return standalone ?? (bool)(standalone = ConfigurationManager.AppSettings["core:base-domain"] == "localhost"); }
get { return standalone ?? (bool)(standalone = Configuration["core:base-domain"] == "localhost"); }
}
public bool Personal
{
get { return personal ?? (bool)(personal = ConfigurationManager.AppSettings["core.personal"] == "true"); }
get { return personal ?? (bool)(personal = Configuration["core.personal"] == "true"); }
}
public bool CustomMode
{
get { return customMode ?? (bool)(customMode = ConfigurationManager.AppSettings["core.custom-mode"] == "true"); }
public bool CustomMode
{
get { return customMode ?? (bool)(customMode = Configuration["core.custom-mode"] == "true"); }
}
}
public long PersonalMaxSpace
public class CoreSettings
{
private string basedomain;
public string BaseDomain
{
get
{
var quotaSettings = PersonalQuotaSettings.LoadForCurrentUser();
if (basedomain == null)
{
basedomain = Configuration["core:base-domain"] ?? string.Empty;
}
if (quotaSettings.MaxSpace != long.MaxValue)
return quotaSettings.MaxSpace;
if (personalMaxSpace.HasValue)
return personalMaxSpace.Value;
if (!long.TryParse(ConfigurationManager.AppSettings["core.personal.maxspace"], out var value))
value = long.MaxValue;
personalMaxSpace = value;
return personalMaxSpace.Value;
string result;
if (CoreBaseSettings.Standalone || string.IsNullOrEmpty(basedomain))
{
result = GetSetting("BaseDomain") ?? basedomain;
}
else
{
result = basedomain;
}
return result;
}
set
{
if (CoreBaseSettings.Standalone || string.IsNullOrEmpty(basedomain))
{
SaveSetting("BaseDomain", value);
}
}
}
public ITenantService TenantService { get; }
public CoreBaseSettings CoreBaseSettings { get; }
public IConfiguration Configuration { get; }
public CoreSettings(ITenantService tenantService, CoreBaseSettings coreBaseSettings, IConfiguration configuration)
{
TenantService = tenantService;
CoreBaseSettings = coreBaseSettings;
Configuration = configuration;
}
public string GetBaseDomain(string hostedRegion)
{
var baseHost = BaseDomain;
if (string.IsNullOrEmpty(hostedRegion) || string.IsNullOrEmpty(baseHost) || !baseHost.Contains("."))
{
return baseHost;
}
var subdomain = baseHost.Remove(baseHost.IndexOf('.') + 1);
return hostedRegion.StartsWith(subdomain) ? hostedRegion : (subdomain + hostedRegion.TrimStart('.'));
}
public void SaveSetting(string key, string value, int tenant = Tenant.DEFAULT_TENANT)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException("key");
}
byte[] bytes = null;
if (value != null)
{
bytes = Crypto.GetV(Encoding.UTF8.GetBytes(value), 2, true);
}
TenantService.SetTenantSettings(tenant, key, bytes);
}
public string GetSetting(string key, int tenant = Tenant.DEFAULT_TENANT)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException("key");
}
var bytes = TenantService.GetTenantSettings(tenant, key);
var result = bytes != null ? Encoding.UTF8.GetString(Crypto.GetV(bytes, 2, false)) : null;
return result;
}
public string GetKey(int tenant)
{
if (CoreBaseSettings.Standalone)
{
var key = GetSetting("PortalId");
if (string.IsNullOrEmpty(key))
{
lock (TenantService)
{
// thread safe
key = GetSetting("PortalId");
if (string.IsNullOrEmpty(key))
{
SaveSetting("PortalId", key = Guid.NewGuid().ToString());
}
}
}
return key;
}
else
{
var t = TenantService.GetTenant(tenant);
if (t != null && !string.IsNullOrWhiteSpace(t.PaymentId))
return t.PaymentId;
return Configuration["core:payment:region"] + tenant;
}
}
public string GetAffiliateId(int tenant)
{
var t = TenantService.GetTenant(tenant);
if (t != null && !string.IsNullOrWhiteSpace(t.AffiliateId))
return t.AffiliateId;
return null;
}
}
public class CoreConfiguration
{
private long? personalMaxSpace;
public CoreConfiguration(CoreSettings coreSettings, TenantManager tenantManager, IConfiguration configuration)
{
CoreSettings = coreSettings;
TenantManager = tenantManager;
Configuration = configuration;
}
public long PersonalMaxSpace(SettingsManager settingsManager)
{
var quotaSettings = settingsManager.LoadForCurrentUser<PersonalQuotaSettings>();
if (quotaSettings.MaxSpace != long.MaxValue)
return quotaSettings.MaxSpace;
if (personalMaxSpace.HasValue)
return personalMaxSpace.Value;
if (!long.TryParse(Configuration["core.personal.maxspace"], out var value))
value = long.MaxValue;
personalMaxSpace = value;
return personalMaxSpace.Value;
}
public SmtpSettings SmtpSettings
@ -90,7 +223,7 @@ namespace ASC.Core
get
{
var isDefaultSettings = false;
var tenant = CoreContext.TenantManager.GetCurrentTenant(false);
var tenant = TenantManager.GetCurrentTenant(false);
if (tenant != null)
{
@ -114,107 +247,21 @@ namespace ASC.Core
return settings;
}
}
set { SaveSetting("SmtpSettings", value?.Serialize(), CoreContext.TenantManager.GetCurrentTenant().TenantId); }
set { SaveSetting("SmtpSettings", value?.Serialize(), TenantManager.GetCurrentTenant().TenantId); }
}
public string BaseDomain
{
get
{
if (basedomain == null)
{
basedomain = ConfigurationManager.AppSettings["core:base-domain"] ?? string.Empty;
}
string result;
if (Standalone || string.IsNullOrEmpty(basedomain))
{
result = GetSetting("BaseDomain") ?? basedomain;
}
else
{
result = basedomain;
}
return result;
}
set
{
if (Standalone || string.IsNullOrEmpty(basedomain))
{
SaveSetting("BaseDomain", value);
}
}
}
public CoreSettings CoreSettings { get; }
public TenantManager TenantManager { get; }
public IConfiguration Configuration { get; }
#region Methods Get/Save Setting
public void SaveSetting(string key, string value, int tenant = Tenant.DEFAULT_TENANT)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException("key");
}
byte[] bytes = null;
if (value != null)
{
bytes = Crypto.GetV(Encoding.UTF8.GetBytes(value), 2, true);
}
tenantService.SetTenantSettings(tenant, key, bytes);
}
public void SaveSetting(string key, string value, int tenant = Tenant.DEFAULT_TENANT) => CoreSettings.SaveSetting(key, value, tenant);
public string GetSetting(string key, int tenant = Tenant.DEFAULT_TENANT)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException("key");
}
var bytes = tenantService.GetTenantSettings(tenant, key);
var result = bytes != null ? Encoding.UTF8.GetString(Crypto.GetV(bytes, 2, false)) : null;
return result;
}
public string GetSetting(string key, int tenant = Tenant.DEFAULT_TENANT) => CoreSettings.GetSetting(key, tenant);
#endregion
public string GetKey(int tenant)
{
if (Standalone)
{
var key = GetSetting("PortalId");
if (string.IsNullOrEmpty(key))
{
lock (tenantService)
{
// thread safe
key = GetSetting("PortalId");
if (string.IsNullOrEmpty(key))
{
SaveSetting("PortalId", key = Guid.NewGuid().ToString());
}
}
}
return key;
}
else
{
var t = tenantService.GetTenant(tenant);
if (t != null && !string.IsNullOrWhiteSpace(t.PaymentId))
return t.PaymentId;
return ConfigurationManager.AppSettings["core:payment:region"] + tenant;
}
}
public string GetAffiliateId(int tenant)
{
var t = tenantService.GetTenant(tenant);
if (t != null && !string.IsNullOrWhiteSpace(t.AffiliateId))
return t.AffiliateId;
return null;
}
#region Methods Get/Set Section
public T GetSection<T>() where T : class
@ -229,7 +276,7 @@ namespace ASC.Core
public T GetSection<T>(string sectionName) where T : class
{
return GetSection<T>(CoreContext.TenantManager.GetCurrentTenant().TenantId, sectionName);
return GetSection<T>(TenantManager.GetCurrentTenant().TenantId, sectionName);
}
public T GetSection<T>(int tenantId, string sectionName) where T : class
@ -244,7 +291,7 @@ namespace ASC.Core
public void SaveSection<T>(string sectionName, T section) where T : class
{
SaveSection(CoreContext.TenantManager.GetCurrentTenant().TenantId, sectionName, section);
SaveSection(TenantManager.GetCurrentTenant().TenantId, sectionName, section);
}
public void SaveSection<T>(T section) where T : class
@ -265,4 +312,28 @@ namespace ASC.Core
#endregion
}
public static class CoreSettingsConfigExtension
{
public static IServiceCollection AddCoreBaseSettingsService(this IServiceCollection services)
{
services.TryAddSingleton<CoreBaseSettings>();
return services;
}
public static IServiceCollection AddCoreSettingsService(this IServiceCollection services)
{
services.TryAddScoped<CoreSettings>();
services.TryAddScoped<CoreConfiguration>();
return services
.AddCoreBaseSettingsService()
.AddTenantService();
}
public static IServiceCollection AddCoreConfigurationService(this IServiceCollection services)
{
services.TryAddScoped<CoreConfiguration>();
return services
.AddCoreSettingsService();
}
}
}

View File

@ -35,10 +35,13 @@ using System.Text;
using System.Threading;
using System.Web;
using System.Xml.Linq;
using ASC.Common.Utils;
using ASC.Core.Billing;
using ASC.Core.Caching;
using ASC.Core.Users;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Newtonsoft.Json;
@ -46,20 +49,24 @@ namespace ASC.Core
{
public class PaymentManager
{
private readonly CoreConfiguration config;
private readonly CoreSettings config;
private readonly IQuotaService quotaService;
private readonly ITariffService tariffService;
private readonly string partnerUrl;
private readonly string partnerKey;
public PaymentManager(CoreConfiguration config, IQuotaService quotaService, ITariffService tariffService)
private readonly string partnerKey;
public TenantManager TenantManager { get; }
public IConfiguration Configuration { get; }
public PaymentManager(CoreSettings config, TenantManager tenantManager, IQuotaService quotaService, ITariffService tariffService, IConfiguration configuration)
{
this.config = config;
this.config = config;
TenantManager = tenantManager;
this.quotaService = quotaService;
this.tariffService = tariffService;
partnerUrl = (ConfigurationManager.AppSettings["core:payment:partners"] ?? "https://partners.onlyoffice.com/api").TrimEnd('/');
partnerKey = (ConfigurationManager.AppSettings["core:machinekey"] ?? "C5C1F4E85A3A43F5B3202C24D97351DF");
this.tariffService = tariffService;
Configuration = configuration;
partnerUrl = (Configuration["core:payment:partners"] ?? "https://partners.onlyoffice.com/api").TrimEnd('/');
partnerKey = (Configuration["core:machinekey"] ?? "C5C1F4E85A3A43F5B3202C24D97351DF");
}
@ -100,7 +107,7 @@ namespace ASC.Core
public Uri GetShoppingUri(int quotaId, bool forCurrentTenant = true, string affiliateId = null, string currency = null, string language = null, string customerId = null)
{
return tariffService.GetShoppingUri(forCurrentTenant ? CoreContext.TenantManager.GetCurrentTenant().TenantId : (int?)null, quotaId, affiliateId, currency, language, customerId);
return tariffService.GetShoppingUri(forCurrentTenant ? TenantManager.GetCurrentTenant().TenantId : (int?)null, quotaId, affiliateId, currency, language, customerId);
}
public Uri GetShoppingUri(int quotaId, string affiliateId, string currency = null, string language = null, string customerId = null)
@ -113,7 +120,7 @@ namespace ASC.Core
var trial = quotaService.GetTenantQuotas().FirstOrDefault(q => q.Trial);
if (trial != null)
{
var uri = ConfigurationManager.AppSettings["core:payment:request"] ?? "http://billing.onlyoffice.com/avangate/requestatrialversion.aspx";
var uri = Configuration["core:payment:request"] ?? "http://billing.onlyoffice.com/avangate/requestatrialversion.aspx";
uri += uri.Contains('?') ? "&" : "?";
uri += "FIRSTNAME=" + HttpUtility.UrlEncode(user.FirstName) +
"&LASTNAME=" + HttpUtility.UrlEncode(user.FirstName) +
@ -151,7 +158,7 @@ namespace ASC.Core
}
var now = DateTime.UtcNow;
var actionUrl = "/partnerapi/ActivateKey?code=" + HttpUtility.UrlEncode(key) + "&portal=" + HttpUtility.UrlEncode(CoreContext.TenantManager.GetCurrentTenant().TenantAlias);
var actionUrl = "/partnerapi/ActivateKey?code=" + HttpUtility.UrlEncode(key) + "&portal=" + HttpUtility.UrlEncode(TenantManager.GetCurrentTenant().TenantAlias);
using var webClient = new WebClient();
webClient.Headers.Add("Authorization", GetPartnerAuthHeader(actionUrl));
try
@ -167,7 +174,7 @@ namespace ASC.Core
}
throw;
}
tariffService.ClearCache(CoreContext.TenantManager.GetCurrentTenant().TenantId);
tariffService.ClearCache(TenantManager.GetCurrentTenant().TenantId);
var timeout = DateTime.UtcNow - now - TimeSpan.FromSeconds(5);
if (TimeSpan.Zero < timeout)
@ -175,7 +182,7 @@ namespace ASC.Core
// clear tenant cache
Thread.Sleep(timeout);
}
CoreContext.TenantManager.GetTenant(CoreContext.TenantManager.GetCurrentTenant().TenantId);
TenantManager.GetTenant(TenantManager.GetCurrentTenant().TenantId);
}
private string GetPartnerAuthHeader(string url)
@ -209,5 +216,19 @@ namespace ASC.Core
public string exceptionType = null;
public string stackTrace = null;
}
}
public static class PaymentManagerExtension
{
public static IServiceCollection AddPaymentManagerService(this IServiceCollection services)
{
services.TryAddScoped<PaymentManager>();
return services
.AddCoreSettingsService()
.AddTenantManagerService()
.AddQuotaService()
.AddTariffService();
}
}
}

View File

@ -25,18 +25,23 @@
using System;
using System.Linq;
using System.Linq;
using ASC.Core.Caching;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Core
{
public class SubscriptionManager
{
private readonly ISubscriptionService service;
private TenantManager TenantManager { get; }
public SubscriptionManager(ISubscriptionService service)
public SubscriptionManager(ISubscriptionService service, TenantManager tenantManager)
{
this.service = service ?? throw new ArgumentNullException("subscriptionManager");
TenantManager = tenantManager;
}
@ -140,7 +145,18 @@ namespace ASC.Core
private int GetTenant()
{
return CoreContext.TenantManager.GetCurrentTenant().TenantId;
return TenantManager.GetCurrentTenant().TenantId;
}
}
public static class SubscriptionConfigExtension
{
public static IServiceCollection AddSubscriptionManagerService(this IServiceCollection services)
{
services.TryAddScoped<SubscriptionManager>();
return services
.AddSubscriptionService()
.AddTenantManagerService();
}
}
}

View File

@ -34,9 +34,12 @@ using System.Web;
using ASC.Common.Notify.Engine;
using ASC.Core.Billing;
using ASC.Core.Caching;
using ASC.Core.Tenants;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Core
{
public class TenantManager
@ -45,15 +48,14 @@ namespace ASC.Core
private readonly ITenantService tenantService;
private readonly IQuotaService quotaService;
private readonly ITariffService tariffService;
private readonly List<string> thisCompAddresses = new List<string>();
public TenantManager(ITenantService tenantService, IQuotaService quotaService, ITariffService tariffService)
{
this.tenantService = tenantService;
this.quotaService = quotaService;
this.tariffService = tariffService;
private static List<string> thisCompAddresses = new List<string>();
private HttpContext HttpContext { get; }
private CoreBaseSettings CoreBaseSettings { get; }
private CoreSettings CoreSettings { get; }
static TenantManager()
{
thisCompAddresses.Add("localhost");
thisCompAddresses.Add(Dns.GetHostName().ToLowerInvariant());
thisCompAddresses.AddRange(Dns.GetHostAddresses("localhost").Select(a => a.ToString()));
@ -64,7 +66,23 @@ namespace ASC.Core
catch
{
// ignore
}
}
}
public TenantManager(
ITenantService tenantService,
IQuotaService quotaService,
ITariffService tariffService,
IHttpContextAccessor httpContextAccessor,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings)
{
this.tenantService = tenantService;
this.quotaService = quotaService;
this.tariffService = tariffService;
CoreBaseSettings = coreBaseSettings;
CoreSettings = coreSettings;
HttpContext = httpContextAccessor?.HttpContext;
}
@ -90,7 +108,7 @@ namespace ASC.Core
var isAlias = false;
if (t == null)
{
var baseUrl = CoreContext.Configuration.BaseDomain;
var baseUrl = CoreSettings.BaseDomain;
if (!string.IsNullOrEmpty(baseUrl) && domain.EndsWith("." + baseUrl, StringComparison.InvariantCultureIgnoreCase))
{
isAlias = true;
@ -101,7 +119,7 @@ namespace ASC.Core
{
t = tenantService.GetTenant(domain);
}
if (t == null && CoreContext.Configuration.Standalone && !isAlias)
if (t == null && CoreBaseSettings.Standalone && !isAlias)
{
t = tenantService.GetTenantForStandaloneWithoutAlias(domain);
}
@ -125,7 +143,7 @@ namespace ASC.Core
public Tenant SaveTenant(Tenant tenant)
{
var newTenant = tenantService.SaveTenant(tenant);
var newTenant = tenantService.SaveTenant(CoreSettings, tenant);
if (CallContext.GetData(CURRENT_TENANT) is Tenant) SetCurrentTenant(newTenant);
return newTenant;
@ -171,7 +189,7 @@ namespace ASC.Core
public Tenant GetCurrentTenant(bool throwIfNotFound)
{
return GetCurrentTenant(throwIfNotFound, ASC.Common.HttpContext.Current);
return GetCurrentTenant(throwIfNotFound, HttpContext);
}
public void SetCurrentTenant(Tenant tenant)
@ -179,9 +197,9 @@ namespace ASC.Core
if (tenant != null)
{
CallContext.SetData(CURRENT_TENANT, tenant);
if (ASC.Common.HttpContext.Current != null)
if (HttpContext != null)
{
ASC.Common.HttpContext.Current.Items[CURRENT_TENANT] = tenant;
HttpContext.Items[CURRENT_TENANT] = tenant;
}
Thread.CurrentThread.CurrentCulture = tenant.GetCulture();
Thread.CurrentThread.CurrentUICulture = tenant.GetCulture();
@ -263,5 +281,21 @@ namespace ASC.Core
{
return quotaService.FindTenantQuotaRows(query).ToList();
}
}
}
public static class TenantManagerConfigExtension
{
public static IServiceCollection AddTenantManagerService(this IServiceCollection services)
{
services.TryAddScoped<TenantManager>();
return services
.AddHttpContextAccessor()
.AddTenantService()
.AddQuotaService()
.AddTariffService()
.AddCoreBaseSettingsService()
.AddCoreSettingsService();
}
}
}

View File

@ -27,137 +27,158 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ASC.Collections;
using ASC.Common.DependencyInjection;
using ASC.Collections;
using ASC.Core.Caching;
using ASC.Core.Tenants;
using ASC.Core.Users;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Core
{
public class UserManagerConstants
{
public IDictionary<Guid, UserInfo> SystemUsers { get; }
public Constants Constants { get; }
public UserManagerConstants(Constants constants)
{
SystemUsers = Configuration.Constants.SystemAccounts.ToDictionary(a => a.ID, a => new UserInfo { ID = a.ID, LastName = a.Name });
SystemUsers[Constants.LostUser.ID] = Constants.LostUser;
SystemUsers[Constants.OutsideUser.ID] = Constants.OutsideUser;
SystemUsers[constants.NamingPoster.ID] = constants.NamingPoster;
Constants = constants;
}
}
public class UserManager
{
private readonly IUserService userService;
private IDictionary<Guid, UserInfo> SystemUsers { get => UserManagerConstants.SystemUsers; }
private readonly IDictionary<Guid, UserInfo> systemUsers;
private readonly IHttpContextAccessor Accessor;
private IHttpContextAccessor Accessor { get; }
private IUserService UserService { get; }
private TenantManager TenantManager { get; }
private PermissionContext PermissionContext { get; }
private UserManagerConstants UserManagerConstants { get; }
private Constants Constants { get; }
private Tenant tenant;
private Tenant Tenant { get { return tenant ?? (tenant = TenantManager.GetCurrentTenant()); } }
public UserManager(IUserService service)
public UserManager(
IUserService service,
IHttpContextAccessor httpContextAccessor,
TenantManager tenantManager,
PermissionContext permissionContext,
UserManagerConstants userManagerConstants)
{
userService = service;
systemUsers = Configuration.Constants.SystemAccounts.ToDictionary(a => a.ID, a => new UserInfo { ID = a.ID, LastName = a.Name });
systemUsers[Constants.LostUser.ID] = Constants.LostUser;
systemUsers[Constants.OutsideUser.ID] = Constants.OutsideUser;
systemUsers[Constants.NamingPoster.ID] = Constants.NamingPoster;
Accessor = CommonServiceProvider.GetService<IHttpContextAccessor>();
UserService = service;
Accessor = httpContextAccessor;
TenantManager = tenantManager;
PermissionContext = permissionContext;
UserManagerConstants = userManagerConstants;
Constants = UserManagerConstants.Constants;
}
public void ClearCache()
{
if (userService is ICachedService)
if (UserService is ICachedService)
{
((ICachedService)userService).InvalidateCache();
((ICachedService)UserService).InvalidateCache();
}
}
#region Users
public UserInfo[] GetUsers(Tenant tenant)
public UserInfo[] GetUsers()
{
return GetUsers(tenant, EmployeeStatus.Default);
return GetUsers(EmployeeStatus.Default);
}
public UserInfo[] GetUsers(Tenant tenant, EmployeeStatus status)
public UserInfo[] GetUsers(EmployeeStatus status)
{
return GetUsers(tenant, status, EmployeeType.All);
return GetUsers(status, EmployeeType.All);
}
public UserInfo[] GetUsers(Tenant tenant, EmployeeStatus status, EmployeeType type)
public UserInfo[] GetUsers(EmployeeStatus status, EmployeeType type)
{
var users = GetUsersInternal(tenant.TenantId).Where(u => (u.Status & status) == u.Status);
var users = GetUsersInternal().Where(u => (u.Status & status) == u.Status);
switch (type)
{
case EmployeeType.User:
users = users.Where(u => !u.IsVisitor(tenant));
users = users.Where(u => !u.IsVisitor(this));
break;
case EmployeeType.Visitor:
users = users.Where(u => u.IsVisitor(tenant));
users = users.Where(u => u.IsVisitor(this));
break;
}
return users.ToArray();
}
public List<UserInfo> GetUsers(int tenantId, bool isAdmin, EmployeeStatus? employeeStatus, List<List<Guid>> includeGroups, List<Guid> excludeGroups, EmployeeActivationStatus? activationStatus, string text, string sortBy, bool sortOrderAsc, long limit, long offset, out int total)
{
return userService.GetUsers(tenantId, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text, sortBy, sortOrderAsc, limit, offset, out total);
}
}
public DateTime GetMaxUsersLastModified(int tenantId)
public List<UserInfo> GetUsers(bool isAdmin, EmployeeStatus? employeeStatus, List<List<Guid>> includeGroups, List<Guid> excludeGroups, EmployeeActivationStatus? activationStatus, string text, string sortBy, bool sortOrderAsc, long limit, long offset, out int total)
{
return userService.GetUsers(tenantId, default)
return UserService.GetUsers(Tenant.TenantId, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text, sortBy, sortOrderAsc, limit, offset, out total);
}
public DateTime GetMaxUsersLastModified()
{
return UserService.GetUsers(Tenant.TenantId, default)
.Values
.Select(g => g.LastModified)
.DefaultIfEmpty()
.Max();
}
public string[] GetUserNames(Tenant tenant, EmployeeStatus status)
public string[] GetUserNames(EmployeeStatus status)
{
return GetUsers(tenant, status)
return GetUsers(status)
.Select(u => u.UserName)
.Where(s => !string.IsNullOrEmpty(s))
.ToArray();
}
public UserInfo GetUserByUserName(int tenantId, string username)
public UserInfo GetUserByUserName(string username)
{
return GetUsersInternal(tenantId)
return GetUsersInternal()
.FirstOrDefault(u => string.Compare(u.UserName, username, StringComparison.CurrentCultureIgnoreCase) == 0) ?? Constants.LostUser;
}
public UserInfo GetUserBySid(int tenantId, string sid)
public UserInfo GetUserBySid(string sid)
{
return GetUsersInternal(tenantId)
return GetUsersInternal()
.FirstOrDefault(u => u.Sid != null && string.Compare(u.Sid, sid, StringComparison.CurrentCultureIgnoreCase) == 0) ?? Constants.LostUser;
}
public UserInfo GetSsoUserByNameId(int tenantId, string nameId)
public UserInfo GetSsoUserByNameId(string nameId)
{
return GetUsersInternal(tenantId)
return GetUsersInternal()
.FirstOrDefault(u => !string.IsNullOrEmpty(u.SsoNameId) && string.Compare(u.SsoNameId, nameId, StringComparison.CurrentCultureIgnoreCase) == 0) ?? Constants.LostUser;
}
public bool IsUserNameExists(Tenant tenant, string username)
public bool IsUserNameExists(string username)
{
return GetUserNames(tenant, EmployeeStatus.All)
return GetUserNames(EmployeeStatus.All)
.Contains(username, StringComparer.CurrentCultureIgnoreCase);
}
public UserInfo GetUsers(Guid id)
{
return GetUsers(CoreContext.TenantManager.GetCurrentTenant().TenantId, id);
}
public UserInfo GetUsers(int tenantId, Guid id)
{
if (IsSystemUser(id)) return systemUsers[id];
var u = userService.GetUser(tenantId, id);
if (IsSystemUser(id)) return SystemUsers[id];
var u = UserService.GetUser(Tenant.TenantId, id);
return u != null && !u.Removed ? u : Constants.LostUser;
}
public UserInfo GetUsers(int tenant, string login, string passwordHash)
{
var u = userService.GetUser(tenant, login, passwordHash);
var u = UserService.GetUser(tenant, login, passwordHash);
return u != null && !u.Removed ? u : Constants.LostUser;
}
public bool UserExists(int tenantId, Guid id)
public bool UserExists(Guid id)
{
return UserExists(GetUsers(tenantId, id));
return UserExists(GetUsers(id));
}
public bool UserExists(UserInfo user)
@ -167,23 +188,23 @@ namespace ASC.Core
public bool IsSystemUser(Guid id)
{
return systemUsers.ContainsKey(id);
return SystemUsers.ContainsKey(id);
}
public UserInfo GetUserByEmail(int tenantId, string email)
public UserInfo GetUserByEmail(string email)
{
if (string.IsNullOrEmpty(email)) return Constants.LostUser;
return GetUsersInternal(tenantId)
return GetUsersInternal()
.FirstOrDefault(u => string.Compare(u.Email, email, StringComparison.CurrentCultureIgnoreCase) == 0) ?? Constants.LostUser;
}
public UserInfo[] Search(Tenant tenant, string text, EmployeeStatus status)
public UserInfo[] Search(string text, EmployeeStatus status)
{
return Search(tenant, text, status, Guid.Empty);
return Search(text, status, Guid.Empty);
}
public UserInfo[] Search(Tenant tenant, string text, EmployeeStatus status, Guid groupId)
public UserInfo[] Search(string text, EmployeeStatus status, Guid groupId)
{
if (text == null || text.Trim() == string.Empty) return new UserInfo[0];
@ -191,8 +212,8 @@ namespace ASC.Core
if (words.Length == 0) return new UserInfo[0];
var users = groupId == Guid.Empty ?
GetUsers(tenant, status) :
GetUsersByGroup(tenant, groupId).Where(u => (u.Status & status) == status);
GetUsers(status) :
GetUsersByGroup(groupId).Where(u => (u.Status & status) == status);
var findUsers = new List<UserInfo>();
foreach (var user in users)
@ -213,90 +234,90 @@ namespace ASC.Core
return findUsers.ToArray();
}
public UserInfo SaveUserInfo(Tenant tenant, UserInfo u, bool isVisitor = false)
public UserInfo SaveUserInfo(UserInfo u, bool isVisitor = false)
{
if (IsSystemUser(u.ID)) return systemUsers[u.ID];
if (u.ID == Guid.Empty) SecurityContext.DemandPermissions(tenant, Constants.Action_AddRemoveUser);
else SecurityContext.DemandPermissions(tenant, new UserSecurityProvider(u.ID), Constants.Action_EditUser);
if (IsSystemUser(u.ID)) return SystemUsers[u.ID];
if (u.ID == Guid.Empty) PermissionContext.DemandPermissions(Constants.Action_AddRemoveUser);
else PermissionContext.DemandPermissions(new UserSecurityProvider(u.ID), Constants.Action_EditUser);
if (Constants.MaxEveryoneCount <= GetUsersByGroup(tenant, Constants.GroupEveryone.ID).Length)
if (Constants.MaxEveryoneCount <= GetUsersByGroup(Constants.GroupEveryone.ID).Length)
{
throw new TenantQuotaException("Maximum number of users exceeded");
}
if (u.Status == EmployeeStatus.Active)
{
var q = CoreContext.TenantManager.GetTenantQuota(tenant.TenantId);
if (q.ActiveUsers < GetUsersByGroup(tenant, Constants.GroupUser.ID).Length)
var q = TenantManager.GetTenantQuota(Tenant.TenantId);
if (q.ActiveUsers < GetUsersByGroup(Constants.GroupUser.ID).Length)
{
throw new TenantQuotaException(string.Format("Exceeds the maximum active users ({0})", q.ActiveUsers));
}
}
var newUser = userService.SaveUser(tenant.TenantId, u);
var newUser = UserService.SaveUser(Tenant.TenantId, u);
return newUser;
}
public void DeleteUser(Tenant tenant, Guid id)
public void DeleteUser(Guid id)
{
if (IsSystemUser(id)) return;
SecurityContext.DemandPermissions(tenant, Constants.Action_AddRemoveUser);
if (id == tenant.OwnerId)
PermissionContext.DemandPermissions(Constants.Action_AddRemoveUser);
if (id == Tenant.OwnerId)
{
throw new InvalidOperationException("Can not remove tenant owner.");
}
userService.RemoveUser(tenant.TenantId, id);
UserService.RemoveUser(Tenant.TenantId, id);
}
public void SaveUserPhoto(Tenant tenant, Guid id, byte[] photo)
public void SaveUserPhoto(Guid id, byte[] photo)
{
if (IsSystemUser(id)) return;
SecurityContext.DemandPermissions(tenant, new UserSecurityProvider(id), Constants.Action_EditUser);
PermissionContext.DemandPermissions(new UserSecurityProvider(id), Constants.Action_EditUser);
userService.SetUserPhoto(tenant.TenantId, id, photo);
UserService.SetUserPhoto(Tenant.TenantId, id, photo);
}
public byte[] GetUserPhoto(int tenantId, Guid id)
public byte[] GetUserPhoto(Guid id)
{
if (IsSystemUser(id)) return null;
return userService.GetUserPhoto(tenantId, id);
return UserService.GetUserPhoto(Tenant.TenantId, id);
}
public IEnumerable<Guid> GetUserGroupsId(int tenantId, Guid id)
public IEnumerable<Guid> GetUserGroupsId(Guid id)
{
return GetUsers(tenantId, id).GetUserGroupsId(tenantId);
return GetUserGroupsGuids(id);
}
public List<GroupInfo> GetUserGroups(Tenant tenant, Guid id)
public List<GroupInfo> GetUserGroups(Guid id)
{
return GetUsers(tenant.TenantId, id).GetGroups(tenant, IncludeType.Distinct, Guid.Empty);
return GetUserGroups(id, IncludeType.Distinct, Guid.Empty);
}
public List<GroupInfo> GetUserGroups(Tenant tenant, Guid id, Guid categoryID)
public List<GroupInfo> GetUserGroups(Guid id, Guid categoryID)
{
return GetUsers(tenant.TenantId, id).GetGroups(tenant, IncludeType.Distinct, categoryID);
return GetUserGroups(id, IncludeType.Distinct, categoryID);
}
public List<GroupInfo> GetUserGroups(Tenant tenant, Guid userID, IncludeType includeType)
public List<GroupInfo> GetUserGroups(Guid userID, IncludeType includeType)
{
return GetUsers(tenant.TenantId, userID).GetGroups(tenant, includeType, null);
}
internal List<GroupInfo> GetUserGroups(Tenant tenant, Guid userID, IncludeType includeType, Guid? categoryId)
{
var httpRequestDictionary = new HttpRequestDictionary<List<GroupInfo>>(Accessor?.HttpContext, "GroupInfo");
var fromCache = httpRequestDictionary.Get(userID.ToString());
if (fromCache != null)
{
return fromCache;
}
return GetUserGroups(userID, includeType, null);
}
internal List<GroupInfo> GetUserGroups(Guid userID, IncludeType includeType, Guid? categoryId)
{
var httpRequestDictionary = new HttpRequestDictionary<List<GroupInfo>>(Accessor?.HttpContext, "GroupInfo");
var fromCache = httpRequestDictionary.Get(userID.ToString());
if (fromCache != null)
{
return fromCache;
}
var result = new List<GroupInfo>();
var distinctUserGroups = new List<GroupInfo>();
var refs = GetRefsInternal(tenant.TenantId);
var refs = GetRefsInternal();
IEnumerable<UserGroupRef> userRefs = null;
if (refs is UserGroupRefStore store)
{
@ -305,9 +326,9 @@ namespace ASC.Core
var userRefsContainsNotRemoved = userRefs?.Where(r => !r.Removed && r.RefType == UserGroupRefType.Contains).ToList();
foreach (var g in GetGroupsInternal(tenant.TenantId).Where(g => !categoryId.HasValue || g.CategoryID == categoryId))
foreach (var g in GetGroupsInternal().Where(g => !categoryId.HasValue || g.CategoryID == categoryId))
{
if (((g.CategoryID == Constants.SysGroupCategoryId || userRefs == null) && IsUserInGroupInternal(tenant, userID, g.ID, refs)) ||
if (((g.CategoryID == Constants.SysGroupCategoryId || userRefs == null) && IsUserInGroupInternal(userID, g.ID, refs)) ||
(userRefsContainsNotRemoved != null && userRefsContainsNotRemoved.Any(r => r.GroupId == g.ID)))
{
distinctUserGroups.Add(g);
@ -319,25 +340,30 @@ namespace ASC.Core
result.AddRange(distinctUserGroups);
}
if (categoryId.HasValue)
{
result = result.Where(r => r.CategoryID.Equals(categoryId.Value)).ToList();
}
result.Sort((group1, group2) => string.Compare(group1.Name, group2.Name, StringComparison.Ordinal));
httpRequestDictionary.Add(userID.ToString(), result);
httpRequestDictionary.Add(userID.ToString(), result);
return result;
}
internal IEnumerable<Guid> GetUserGroupsGuids(int tenantId, Guid userID)
{
var httpRequestDictionary = new HttpRequestDictionary<List<Guid>>(Accessor?.HttpContext, "GroupInfoID");
var fromCache = httpRequestDictionary.Get(userID.ToString());
if (fromCache != null)
{
return fromCache;
}
internal IEnumerable<Guid> GetUserGroupsGuids(Guid userID)
{
var httpRequestDictionary = new HttpRequestDictionary<List<Guid>>(Accessor?.HttpContext, "GroupInfoID");
var fromCache = httpRequestDictionary.Get(userID.ToString());
if (fromCache != null)
{
return fromCache;
}
var result = new List<Guid>();
var refs = GetRefsInternal(tenantId);
var refs = GetRefsInternal();
if (refs is UserGroupRefStore store)
{
@ -345,104 +371,104 @@ namespace ASC.Core
if (userRefs != null)
{
var toAdd = userRefs.Where(r => !r.Removed &&
r.RefType == UserGroupRefType.Contains &&
var toAdd = userRefs.Where(r => !r.Removed &&
r.RefType == UserGroupRefType.Contains &&
!Constants.BuildinGroups.Any(g => g.ID.Equals(r.GroupId)))
.Select(r => r.GroupId);
result.AddRange(toAdd);
}
}
}
httpRequestDictionary.Add(userID.ToString(), result);
httpRequestDictionary.Add(userID.ToString(), result);
return result;
}
public bool IsUserInGroup(Tenant tenant, Guid userId, Guid groupId)
public bool IsUserInGroup(Guid userId, Guid groupId)
{
return IsUserInGroupInternal(tenant, userId, groupId, GetRefsInternal(tenant.TenantId));
return IsUserInGroupInternal(userId, groupId, GetRefsInternal());
}
public UserInfo[] GetUsersByGroup(Tenant tenant, Guid groupId, EmployeeStatus employeeStatus = EmployeeStatus.Default)
public UserInfo[] GetUsersByGroup(Guid groupId, EmployeeStatus employeeStatus = EmployeeStatus.Default)
{
var refs = GetRefsInternal(tenant.TenantId);
return GetUsers(tenant, employeeStatus).Where(u => IsUserInGroupInternal(tenant, u.ID, groupId, refs)).ToArray();
var refs = GetRefsInternal();
return GetUsers(employeeStatus).Where(u => IsUserInGroupInternal(u.ID, groupId, refs)).ToArray();
}
public void AddUserIntoGroup(Tenant tenant, Guid userId, Guid groupId)
public void AddUserIntoGroup(Guid userId, Guid groupId)
{
if (Constants.LostUser.ID == userId || Constants.LostGroupInfo.ID == groupId)
{
return;
}
SecurityContext.DemandPermissions(tenant, Constants.Action_EditGroups);
PermissionContext.DemandPermissions(Constants.Action_EditGroups);
userService.SaveUserGroupRef(tenant.TenantId, new UserGroupRef(userId, groupId, UserGroupRefType.Contains));
UserService.SaveUserGroupRef(Tenant.TenantId, new UserGroupRef(userId, groupId, UserGroupRefType.Contains));
ResetGroupCache(userId);
}
public void RemoveUserFromGroup(Tenant tenant, Guid userId, Guid groupId)
public void RemoveUserFromGroup(Guid userId, Guid groupId)
{
if (Constants.LostUser.ID == userId || Constants.LostGroupInfo.ID == groupId) return;
SecurityContext.DemandPermissions(tenant, Constants.Action_EditGroups);
PermissionContext.DemandPermissions(Constants.Action_EditGroups);
userService.RemoveUserGroupRef(tenant.TenantId, userId, groupId, UserGroupRefType.Contains);
UserService.RemoveUserGroupRef(Tenant.TenantId, userId, groupId, UserGroupRefType.Contains);
ResetGroupCache(userId);
}
internal void ResetGroupCache(Guid userID)
{
new HttpRequestDictionary<List<GroupInfo>>(Accessor?.HttpContext, "GroupInfo").Reset(userID.ToString());
new HttpRequestDictionary<List<Guid>>(Accessor?.HttpContext, "GroupInfoID").Reset(userID.ToString());
}
#endregion Users
#region Company
public GroupInfo[] GetDepartments(int tenantId)
{
return GetGroups(tenantId);
}
public Guid GetDepartmentManager(int tenantId, Guid deparmentID)
internal void ResetGroupCache(Guid userID)
{
return GetRefsInternal(tenantId)
new HttpRequestDictionary<List<GroupInfo>>(Accessor?.HttpContext, "GroupInfo").Reset(userID.ToString());
new HttpRequestDictionary<List<Guid>>(Accessor?.HttpContext, "GroupInfoID").Reset(userID.ToString());
}
#endregion Users
#region Company
public GroupInfo[] GetDepartments()
{
return GetGroups();
}
public Guid GetDepartmentManager(Guid deparmentID)
{
return GetRefsInternal()
.Values
.Where(r => r.RefType == UserGroupRefType.Manager && r.GroupId == deparmentID && !r.Removed)
.Select(r => r.UserId)
.SingleOrDefault();
}
public void SetDepartmentManager(int tenantId, Guid deparmentID, Guid userID)
public void SetDepartmentManager(Guid deparmentID, Guid userID)
{
var managerId = GetDepartmentManager(tenantId, deparmentID);
var managerId = GetDepartmentManager(deparmentID);
if (managerId != Guid.Empty)
{
userService.RemoveUserGroupRef(
tenantId,
UserService.RemoveUserGroupRef(
Tenant.TenantId,
managerId, deparmentID, UserGroupRefType.Manager);
}
if (userID != Guid.Empty)
{
userService.SaveUserGroupRef(
tenantId,
UserService.SaveUserGroupRef(
Tenant.TenantId,
new UserGroupRef(userID, deparmentID, UserGroupRefType.Manager));
}
}
public UserInfo GetCompanyCEO(int tenantId)
public UserInfo GetCompanyCEO()
{
var id = GetDepartmentManager(tenantId, Guid.Empty);
return id != Guid.Empty ? GetUsers(tenantId, id) : null;
var id = GetDepartmentManager(Guid.Empty);
return id != Guid.Empty ? GetUsers(id) : null;
}
public void SetCompanyCEO(int tenantId, Guid userId)
public void SetCompanyCEO(Guid userId)
{
SetDepartmentManager(tenantId, Guid.Empty, userId);
SetDepartmentManager(Guid.Empty, userId);
}
#endregion Company
@ -450,56 +476,56 @@ namespace ASC.Core
#region Groups
public GroupInfo[] GetGroups(int tenantId)
public GroupInfo[] GetGroups()
{
return GetGroups(tenantId, Guid.Empty);
return GetGroups(Guid.Empty);
}
public GroupInfo[] GetGroups(int tenantId, Guid categoryID)
public GroupInfo[] GetGroups(Guid categoryID)
{
return GetGroupsInternal(tenantId)
return GetGroupsInternal()
.Where(g => g.CategoryID == categoryID)
.ToArray();
}
public GroupInfo GetGroupInfo(int tenantId, Guid groupID)
public GroupInfo GetGroupInfo(Guid groupID)
{
return GetGroupsInternal(tenantId)
return GetGroupsInternal()
.SingleOrDefault(g => g.ID == groupID) ?? Constants.LostGroupInfo;
}
public GroupInfo GetGroupInfoBySid(int tenantId, string sid)
public GroupInfo GetGroupInfoBySid(string sid)
{
return GetGroupsInternal(tenantId)
return GetGroupsInternal()
.SingleOrDefault(g => g.Sid == sid) ?? Constants.LostGroupInfo;
}
public DateTime GetMaxGroupsLastModified(int tenantId)
public DateTime GetMaxGroupsLastModified()
{
return userService.GetGroups(tenantId, default)
return UserService.GetGroups(Tenant.TenantId, default)
.Values
.Select(g => g.LastModified)
.DefaultIfEmpty()
.Max();
}
public GroupInfo SaveGroupInfo(Tenant tenant, GroupInfo g)
public GroupInfo SaveGroupInfo(GroupInfo g)
{
if (Constants.LostGroupInfo.Equals(g)) return Constants.LostGroupInfo;
if (Constants.BuildinGroups.Any(b => b.ID == g.ID)) return Constants.BuildinGroups.Single(b => b.ID == g.ID);
SecurityContext.DemandPermissions(tenant, Constants.Action_EditGroups);
PermissionContext.DemandPermissions(Constants.Action_EditGroups);
var newGroup = userService.SaveGroup(tenant.TenantId, ToGroup(g));
var newGroup = UserService.SaveGroup(Tenant.TenantId, ToGroup(g));
return new GroupInfo(newGroup.CategoryId) { ID = newGroup.Id, Name = newGroup.Name, Sid = newGroup.Sid };
}
public void DeleteGroup(Tenant tenant, Guid id)
public void DeleteGroup(Guid id)
{
if (Constants.LostGroupInfo.Equals(id)) return;
if (Constants.BuildinGroups.Any(b => b.ID == id)) return;
SecurityContext.DemandPermissions(tenant, Constants.Action_EditGroups);
PermissionContext.DemandPermissions(Constants.Action_EditGroups);
userService.RemoveGroup(tenant.TenantId, id);
UserService.RemoveGroup(Tenant.TenantId, id);
}
#endregion Groups
@ -521,34 +547,34 @@ namespace ASC.Core
}
private IEnumerable<UserInfo> GetUsersInternal(int tenantId)
private IEnumerable<UserInfo> GetUsersInternal()
{
return userService.GetUsers(tenantId, default)
return UserService.GetUsers(Tenant.TenantId, default)
.Values
.Where(u => !u.Removed);
}
private IEnumerable<GroupInfo> GetGroupsInternal(int tenantId)
private IEnumerable<GroupInfo> GetGroupsInternal()
{
return userService.GetGroups(tenantId, default)
return UserService.GetGroups(Tenant.TenantId, default)
.Values
.Where(g => !g.Removed)
.Select(g => new GroupInfo(g.CategoryId) { ID = g.Id, Name = g.Name, Sid = g.Sid })
.Concat(Constants.BuildinGroups);
}
private IDictionary<string, UserGroupRef> GetRefsInternal(int tenantId)
private IDictionary<string, UserGroupRef> GetRefsInternal()
{
return userService.GetUserGroupRefs(tenantId, default);
return UserService.GetUserGroupRefs(Tenant.TenantId, default);
}
private bool IsUserInGroupInternal(Tenant tenant, Guid userId, Guid groupId, IDictionary<string, UserGroupRef> refs)
private bool IsUserInGroupInternal(Guid userId, Guid groupId, IDictionary<string, UserGroupRef> refs)
{
if (groupId == Constants.GroupEveryone.ID)
{
return true;
}
if (groupId == Constants.GroupAdmin.ID && (tenant.OwnerId == userId || userId == Configuration.Constants.CoreSystem.ID || userId == Constants.NamingPoster.ID))
if (groupId == Constants.GroupAdmin.ID && (Tenant.OwnerId == userId || userId == Configuration.Constants.CoreSystem.ID || userId == Constants.NamingPoster.ID))
{
return true;
}
@ -560,7 +586,7 @@ namespace ASC.Core
UserGroupRef r;
if (groupId == Constants.GroupUser.ID || groupId == Constants.GroupVisitor.ID)
{
var visitor = refs.TryGetValue(UserGroupRef.CreateKey(tenant.TenantId, userId, Constants.GroupVisitor.ID, UserGroupRefType.Contains), out r) && !r.Removed;
var visitor = refs.TryGetValue(UserGroupRef.CreateKey(Tenant.TenantId, userId, Constants.GroupVisitor.ID, UserGroupRefType.Contains), out r) && !r.Removed;
if (groupId == Constants.GroupVisitor.ID)
{
return visitor;
@ -570,7 +596,7 @@ namespace ASC.Core
return !visitor;
}
}
return refs.TryGetValue(UserGroupRef.CreateKey(tenant.TenantId, userId, groupId, UserGroupRefType.Contains), out r) && !r.Removed;
return refs.TryGetValue(UserGroupRef.CreateKey(Tenant.TenantId, userId, groupId, UserGroupRefType.Contains), out r) && !r.Removed;
}
private Group ToGroup(GroupInfo g)
@ -586,4 +612,20 @@ namespace ASC.Core
};
}
}
public static class UserManagerConfigExtension
{
public static IServiceCollection AddUserManagerService(this IServiceCollection services)
{
services.TryAddSingleton<UserManagerConstants>();
services.TryAddScoped<UserManager>();
return services
.AddUserService()
.AddHttpContextAccessor()
.AddTenantManagerService()
.AddConstantsService()
.AddPermissionContextService();
}
}
}

View File

@ -32,7 +32,6 @@ using System.Security.Authentication;
using System.Security.Claims;
using System.Threading;
using System.Web;
using ASC.Common;
using ASC.Common.Logging;
using ASC.Common.Security;
using ASC.Common.Security.Authentication;
@ -44,46 +43,73 @@ using ASC.Core.Security.Authorizing;
using ASC.Core.Tenants;
using ASC.Core.Users;
using ASC.Security.Cryptography;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace ASC.Core
{
public static class SecurityContext
public class SecurityContext
{
private static readonly ILog log = LogManager.GetLogger("ASC.Core");
private readonly ILog log;
public static IAccount CurrentAccount
public IAccount CurrentAccount
{
get { return Principal?.Identity is IAccount ? (IAccount)Principal.Identity : Configuration.Constants.Guest; }
get => AuthContext.CurrentAccount;
}
public static bool IsAuthenticated
public bool IsAuthenticated
{
get { return CurrentAccount.IsAuthenticated; }
get => AuthContext.IsAuthenticated;
}
public static IPermissionResolver PermissionResolver { get; private set; }
private UserManager UserManager { get; }
private AuthManager Authentication { get; }
private AuthContext AuthContext { get; }
private TenantManager TenantManager { get; }
private UserFormatter UserFormatter { get; }
private CookieStorage CookieStorage { get; }
public TenantCookieSettingsHelper TenantCookieSettingsHelper { get; }
private IHttpContextAccessor HttpContextAccessor { get; }
static SecurityContext()
public SecurityContext(
IHttpContextAccessor httpContextAccessor,
UserManager userManager,
AuthManager authentication,
AuthContext authContext,
TenantManager tenantManager,
UserFormatter userFormatter,
CookieStorage cookieStorage,
TenantCookieSettingsHelper tenantCookieSettingsHelper,
IOptionsMonitor<ILog> options
)
{
var azManager = new AzManager(new RoleProvider(), new PermissionProvider());
PermissionResolver = new PermissionResolver(azManager);
log = options.CurrentValue;
UserManager = userManager;
Authentication = authentication;
AuthContext = authContext;
TenantManager = tenantManager;
UserFormatter = userFormatter;
CookieStorage = cookieStorage;
TenantCookieSettingsHelper = tenantCookieSettingsHelper;
HttpContextAccessor = httpContextAccessor;
}
public static string AuthenticateMe(string login, string password)
public string AuthenticateMe(string login, string password)
{
if (login == null) throw new ArgumentNullException("login");
if (password == null) throw new ArgumentNullException("password");
var tenantid = CoreContext.TenantManager.GetCurrentTenant().TenantId;
var u = CoreContext.UserManager.GetUsers(tenantid, login, Hasher.Base64Hash(password, HashAlg.SHA256));
var tenantid = TenantManager.GetCurrentTenant().TenantId;
var u = UserManager.GetUsers(tenantid, login, Hasher.Base64Hash(password, HashAlg.SHA256));
return AuthenticateMe(new UserAccount(u, tenantid));
return AuthenticateMe(new UserAccount(u, tenantid, UserFormatter));
}
public static bool AuthenticateMe(string cookie)
public bool AuthenticateMe(string cookie)
{
if (!string.IsNullOrEmpty(cookie))
{
@ -92,9 +118,9 @@ namespace ASC.Core
{
var ipFrom = string.Empty;
var address = string.Empty;
if (HttpContext.Current != null)
if (HttpContextAccessor?.HttpContext != null)
{
var request = HttpContext.Current.Request;
var request = HttpContextAccessor?.HttpContext.Request;
ipFrom = "from " + (request.Headers["X-Forwarded-For"].ToString() ?? request.GetUserHostAddress());
address = "for " + request.GetUrlRewriter();
}
@ -102,12 +128,12 @@ namespace ASC.Core
}
else if (CookieStorage.DecryptCookie(cookie, out var tenant, out var userid, out var login, out var password, out var indexTenant, out var expire, out var indexUser))
{
if (tenant != CoreContext.TenantManager.GetCurrentTenant().TenantId)
if (tenant != TenantManager.GetCurrentTenant().TenantId)
{
return false;
}
var settingsTenant = TenantCookieSettings.GetForTenant(tenant);
var settingsTenant = TenantCookieSettingsHelper.GetForTenant(tenant);
if (indexTenant != settingsTenant.Index)
{
return false;
@ -122,13 +148,13 @@ namespace ASC.Core
{
if (userid != Guid.Empty)
{
var settingsUser = TenantCookieSettings.GetForUser(userid);
var settingsUser = TenantCookieSettingsHelper.GetForUser(userid);
if (indexUser != settingsUser.Index)
{
return false;
}
AuthenticateMe(new UserAccount(new UserInfo { ID = userid }, tenant));
AuthenticateMe(new UserAccount(new UserInfo { ID = userid }, tenant, UserFormatter));
}
else
{
@ -156,9 +182,9 @@ namespace ASC.Core
{
var ipFrom = string.Empty;
var address = string.Empty;
if (HttpContext.Current != null)
if (HttpContextAccessor?.HttpContext != null)
{
var request = HttpContext.Current.Request;
var request = HttpContextAccessor?.HttpContext.Request;
address = "for " + request.GetUrlRewriter();
ipFrom = "from " + (request.Headers["X-Forwarded-For"].ToString() ?? request.GetUserHostAddress());
}
@ -168,7 +194,7 @@ namespace ASC.Core
return false;
}
public static string AuthenticateMe(IAccount account, List<Claim> additionalClaims = null)
public string AuthenticateMe(IAccount account, List<Claim> additionalClaims = null)
{
if (account == null || account.Equals(Configuration.Constants.Guest)) throw new InvalidCredentialException("account");
@ -183,9 +209,9 @@ namespace ASC.Core
if (account is IUserAccount)
{
var tenant = CoreContext.TenantManager.GetCurrentTenant();
var tenant = TenantManager.GetCurrentTenant();
var u = CoreContext.UserManager.GetUsers(tenant.TenantId, account.ID);
var u = UserManager.GetUsers(account.ID);
if (u.ID == Users.Constants.LostUser.ID)
{
@ -199,19 +225,19 @@ namespace ASC.Core
// for LDAP users only
if (u.Sid != null)
{
if (!CoreContext.TenantManager.GetTenantQuota(tenant.TenantId).Ldap)
if (!TenantManager.GetTenantQuota(tenant.TenantId).Ldap)
{
throw new BillingException("Your tariff plan does not support this option.", "Ldap");
}
}
if (CoreContext.UserManager.IsUserInGroup(tenant, u.ID, Users.Constants.GroupAdmin.ID))
if (UserManager.IsUserInGroup(u.ID, Users.Constants.GroupAdmin.ID))
{
roles.Add(Role.Administrators);
}
roles.Add(Role.Users);
account = new UserAccount(u, CoreContext.TenantManager.GetCurrentTenant().TenantId);
cookie = CookieStorage.EncryptCookie(CoreContext.TenantManager.GetCurrentTenant().TenantId, account.ID);
account = new UserAccount(u, TenantManager.GetCurrentTenant().TenantId, UserFormatter);
cookie = CookieStorage.EncryptCookie(TenantManager.GetCurrentTenant().TenantId, account.ID);
}
var claims = new List<Claim>
@ -225,67 +251,129 @@ namespace ASC.Core
{
claims.AddRange(additionalClaims);
}
Principal = new CustomClaimsPrincipal(new ClaimsIdentity(account, claims), account);
AuthContext.Principal = new CustomClaimsPrincipal(new ClaimsIdentity(account, claims), account);
return cookie;
}
public static string AuthenticateMe(int tenantId, Guid userId, List<Claim> additionalClaims = null)
public string AuthenticateMe(Guid userId, List<Claim> additionalClaims = null)
{
return AuthenticateMe(CoreContext.Authentication.GetAccountByID(tenantId, userId), additionalClaims);
return AuthenticateMe(Authentication.GetAccountByID(TenantManager.GetCurrentTenant().TenantId, userId), additionalClaims);
}
public static void Logout()
public void Logout()
{
Principal = null;
AuthContext.Principal = null;
}
public static void SetUserPassword(int tenantId, Guid userID, string password)
public void SetUserPassword(Guid userID, string password)
{
CoreContext.Authentication.SetUserPassword(tenantId, userID, password);
Authentication.SetUserPassword(TenantManager.GetCurrentTenant().TenantId, userID, password);
}
}
public class PermissionContext
{
public IPermissionResolver PermissionResolver { get; private set; }
public AuthContext AuthContext { get; }
public PermissionContext(IPermissionResolver permissionResolver, AuthContext authContext)
{
PermissionResolver = permissionResolver;
AuthContext = authContext;
}
public static bool CheckPermissions(Tenant tenant, params IAction[] actions)
public bool CheckPermissions(params IAction[] actions)
{
return PermissionResolver.Check(tenant, CurrentAccount, actions);
return PermissionResolver.Check(AuthContext.CurrentAccount, actions);
}
public static bool CheckPermissions(Tenant tenant, ISecurityObject securityObject, params IAction[] actions)
public bool CheckPermissions(ISecurityObject securityObject, params IAction[] actions)
{
return CheckPermissions(tenant, securityObject, null, actions);
return CheckPermissions(securityObject, null, actions);
}
public static bool CheckPermissions(Tenant tenant, ISecurityObjectId objectId, ISecurityObjectProvider securityObjProvider, params IAction[] actions)
public bool CheckPermissions(ISecurityObjectId objectId, ISecurityObjectProvider securityObjProvider, params IAction[] actions)
{
return PermissionResolver.Check(tenant, CurrentAccount, objectId, securityObjProvider, actions);
return PermissionResolver.Check(AuthContext.CurrentAccount, objectId, securityObjProvider, actions);
}
public static void DemandPermissions(Tenant tenant, params IAction[] actions)
public void DemandPermissions(params IAction[] actions)
{
PermissionResolver.Demand(tenant, CurrentAccount, actions);
PermissionResolver.Demand(AuthContext.CurrentAccount, actions);
}
public static void DemandPermissions(Tenant tenant, ISecurityObject securityObject, params IAction[] actions)
public void DemandPermissions(ISecurityObject securityObject, params IAction[] actions)
{
DemandPermissions(tenant, securityObject, null, actions);
DemandPermissions(securityObject, null, actions);
}
public static void DemandPermissions(Tenant tenant, ISecurityObjectId objectId, ISecurityObjectProvider securityObjProvider, params IAction[] actions)
public void DemandPermissions(ISecurityObjectId objectId, ISecurityObjectProvider securityObjProvider, params IAction[] actions)
{
PermissionResolver.Demand(tenant, CurrentAccount, objectId, securityObjProvider, actions);
PermissionResolver.Demand(AuthContext.CurrentAccount, objectId, securityObjProvider, actions);
}
}
public class AuthContext
{
private IHttpContextAccessor HttpContextAccessor { get; }
public AuthContext(IHttpContextAccessor httpContextAccessor)
{
HttpContextAccessor = httpContextAccessor;
}
private static ClaimsPrincipal Principal
public IAccount CurrentAccount
{
get => Thread.CurrentPrincipal as ClaimsPrincipal ?? HttpContext.Current?.User;
get { return Principal?.Identity is IAccount ? (IAccount)Principal.Identity : Configuration.Constants.Guest; }
}
public bool IsAuthenticated
{
get { return CurrentAccount.IsAuthenticated; }
}
internal ClaimsPrincipal Principal
{
get => Thread.CurrentPrincipal as ClaimsPrincipal ?? HttpContextAccessor?.HttpContext?.User;
set
{
Thread.CurrentPrincipal = value;
if (HttpContext.Current != null) HttpContext.Current.User = value;
if (HttpContextAccessor?.HttpContext != null) HttpContextAccessor.HttpContext.User = value;
}
}
}
public static class AuthContextConfigExtension
{
public static IServiceCollection AddSecurityContextService(this IServiceCollection services)
{
services.TryAddScoped<SecurityContext>();
return services
.AddCookieStorageService()
.AddTenantCookieSettingsService()
.AddAuthManager()
.AddUserFormatter()
.AddAuthContextService()
.AddUserManagerService()
.AddTenantManagerService()
.AddHttpContextAccessor();
}
public static IServiceCollection AddAuthContextService(this IServiceCollection services)
{
services.TryAddScoped<AuthContext>();
return services
.AddHttpContextAccessor();
}
public static IServiceCollection AddPermissionContextService(this IServiceCollection services)
{
services.TryAddScoped<PermissionContext>();
return services
.AddAuthContextService()
.AddPermissionResolverService();
}
}
}

View File

@ -26,14 +26,17 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using ASC.Common.Utils;
using System.Reflection;
using ASC.Common.Caching;
using ASC.Common.Logging;
using ASC.Core.Notify;
using ASC.Core.Notify.Senders;
using ASC.Core.Tenants;
using ASC.Notify.Engine;
using ASC.Notify.Engine;
using ASC.Notify.Messages;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Constants = ASC.Core.Configuration.Constants;
using NotifyContext = ASC.Notify.Context;
@ -43,19 +46,11 @@ namespace ASC.Core
{
private static readonly object syncRoot = new object();
private static bool notifyStarted;
private static NotifyContext notifyContext;
private static bool? ismono;
private static string monoversion;
public static NotifyContext NotifyContext
{
get
{
NotifyStartUp();
return notifyContext;
}
}
public static NotifyContext NotifyContext { get; private set; }
public static string[] DefaultClientSenders
{
@ -94,61 +89,76 @@ namespace ASC.Core
}
private static void NotifyStartUp()
public static void NotifyStartUp(IServiceProvider serviceProvider)
{
if (notifyStarted) return;
lock (syncRoot)
{
if (notifyStarted) return;
notifyContext = new NotifyContext();
var configuration = serviceProvider.GetService<IConfiguration>();
var cacheNotify = serviceProvider.GetService<ICacheNotify<NotifyMessage>>();
var options = serviceProvider.GetService<IOptionsMonitor<ILog>>();
INotifySender jabberSender = new NotifyServiceSender();
INotifySender emailSender = new NotifyServiceSender();
NotifyContext = new NotifyContext(serviceProvider);
var postman = ConfigurationManager.AppSettings["core:notify:postman"];
INotifySender jabberSender = new NotifyServiceSender(cacheNotify);
INotifySender emailSender = new NotifyServiceSender(cacheNotify);
var postman = configuration["core:notify:postman"];
if ("ases".Equals(postman, StringComparison.InvariantCultureIgnoreCase) || "smtp".Equals(postman, StringComparison.InvariantCultureIgnoreCase))
{
jabberSender = new JabberSender();
jabberSender = new JabberSender(serviceProvider);
var properties = new Dictionary<string, string>
{
["useCoreSettings"] = "true"
var properties = new Dictionary<string, string>
{
["useCoreSettings"] = "true"
};
if ("ases".Equals(postman, StringComparison.InvariantCultureIgnoreCase))
{
emailSender = new AWSSender();
properties["accessKey"] = ConfigurationManager.AppSettings["ses:accessKey"];
properties["secretKey"] = ConfigurationManager.AppSettings["ses:secretKey"];
properties["refreshTimeout"] = ConfigurationManager.AppSettings["ses:refreshTimeout"];
emailSender = new AWSSender(serviceProvider, options);
properties["accessKey"] = configuration["ses:accessKey"];
properties["secretKey"] = configuration["ses:secretKey"];
properties["refreshTimeout"] = configuration["ses:refreshTimeout"];
}
else
{
emailSender = new SmtpSender();
emailSender = new SmtpSender(serviceProvider, options);
}
emailSender.Init(properties);
}
notifyContext.NotifyService.RegisterSender(Constants.NotifyEMailSenderSysName, new EmailSenderSink(emailSender));
notifyContext.NotifyService.RegisterSender(Constants.NotifyMessengerSenderSysName, new JabberSenderSink(jabberSender));
NotifyContext.NotifyService.RegisterSender(Constants.NotifyEMailSenderSysName, new EmailSenderSink(emailSender, serviceProvider));
NotifyContext.NotifyService.RegisterSender(Constants.NotifyMessengerSenderSysName, new JabberSenderSink(jabberSender, serviceProvider));
notifyContext.NotifyEngine.BeforeTransferRequest += NotifyEngine_BeforeTransferRequest;
notifyContext.NotifyEngine.AfterTransferRequest += NotifyEngine_AfterTransferRequest;
NotifyContext.NotifyEngine.BeforeTransferRequest += NotifyEngine_BeforeTransferRequest;
NotifyContext.NotifyEngine.AfterTransferRequest += NotifyEngine_AfterTransferRequest;
notifyStarted = true;
}
}
private static void NotifyEngine_BeforeTransferRequest(NotifyEngine sender, NotifyRequest request)
public static void RegisterSendMethod(Action<DateTime> method, string cron)
{
request.Properties.Add("Tenant", CoreContext.TenantManager.GetCurrentTenant(false));
NotifyContext.NotifyEngine.RegisterSendMethod(method, cron);
}
private static void NotifyEngine_AfterTransferRequest(NotifyEngine sender, NotifyRequest request)
public static void UnregisterSendMethod(Action<DateTime> method)
{
NotifyContext.NotifyEngine.UnregisterSendMethod(method);
}
private static void NotifyEngine_BeforeTransferRequest(NotifyEngine sender, NotifyRequest request, IServiceScope serviceScope)
{
request.Properties.Add("Tenant", serviceScope.ServiceProvider.GetService<TenantManager>().GetCurrentTenant(false));
}
private static void NotifyEngine_AfterTransferRequest(NotifyEngine sender, NotifyRequest request, IServiceScope scope)
{
if ((request.Properties.Contains("Tenant") ? request.Properties["Tenant"] : null) is Tenant tenant)
{
CoreContext.TenantManager.SetCurrentTenant(tenant);
var tenantManager = scope.ServiceProvider.GetService<TenantManager>();
tenantManager.SetCurrentTenant(tenant);
}
}
}

View File

@ -35,42 +35,47 @@ using System.Resources;
using System.Runtime.Caching;
using System.Text.RegularExpressions;
using System.Web;
using ASC.Common;
using ASC.Common.Data;
using ASC.Common.Data.Sql;
using ASC.Common.Data.Sql.Expressions;
using ASC.Common.Logging;
using ASC.Common.Utils;
using ASC.Core;
using ASC.Core;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace TMResourceData
{
public class DBResourceManager : ResourceManager
{
public static bool WhiteLableEnabled = false;
public static bool ResourcesFromDataBase { get; private set; }
private static readonly ILog log = LogManager.GetLogger("ASC.Resources");
private readonly ConcurrentDictionary<string, ResourceSet> resourceSets = new ConcurrentDictionary<string, ResourceSet>();
static DBResourceManager()
{
ResourcesFromDataBase = string.Equals(ConfigurationManager.AppSettings["resources:from-db"], "true");
}
public DBResourceManager(string filename, Assembly assembly)
: base(filename, assembly)
{
}
public static void PatchAssemblies()
public DBResourceManager(IConfiguration configuration, IOptionsMonitor<ILog> option, DbOptionsManager optionsDbManager, string filename, Assembly assembly)
: base(filename, assembly)
{
AppDomain.CurrentDomain.AssemblyLoad += (_, a) => PatchAssembly(a.LoadedAssembly);
Array.ForEach(AppDomain.CurrentDomain.GetAssemblies(), a => PatchAssembly(a));
Configuration = configuration;
Option = option;
OptionsDbManager = optionsDbManager;
}
public static void PatchAssembly(Assembly a, bool onlyAsc = true)
public static void PatchAssemblies(IOptionsMonitor<ILog> option)
{
AppDomain.CurrentDomain.AssemblyLoad += (_, a) => PatchAssembly(option, a.LoadedAssembly);
Array.ForEach(AppDomain.CurrentDomain.GetAssemblies(), a => PatchAssembly(option, a));
}
public static void PatchAssembly(IOptionsMonitor<ILog> option, Assembly a, bool onlyAsc = true)
{
var log = option.CurrentValue;
if (!onlyAsc || Accept(a))
{
var types = new Type[0];
@ -123,6 +128,9 @@ namespace TMResourceData
get { return typeof(DBResourceSet); }
}
public IConfiguration Configuration { get; }
public IOptionsMonitor<ILog> Option { get; }
public DbOptionsManager OptionsDbManager { get; }
protected override ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
{
@ -130,7 +138,7 @@ namespace TMResourceData
if (set == null)
{
var invariant = culture == CultureInfo.InvariantCulture ? base.InternalGetResourceSet(CultureInfo.InvariantCulture, true, true) : null;
set = new DBResourceSet(invariant, culture, BaseName);
set = new DBResourceSet(Configuration, Option, OptionsDbManager, invariant, culture, BaseName);
resourceSets.AddOrUpdate(culture.Name, set, (k, v) => set);
}
return set;
@ -141,29 +149,19 @@ namespace TMResourceData
{
private const string NEUTRAL_CULTURE = "Neutral";
private static readonly TimeSpan cacheTimeout = TimeSpan.FromMinutes(120); // for performance
private readonly TimeSpan cacheTimeout = TimeSpan.FromMinutes(120); // for performance
private readonly object locker = new object();
private readonly MemoryCache cache;
private readonly ResourceSet invariant;
private readonly string culture;
private readonly string filename;
private readonly ILog log;
public IConfiguration Configuration { get; }
public IOptionsMonitor<ILog> Option { get; }
public DbOptionsManager OptionsDbManager { get; }
static DBResourceSet()
{
try
{
var defaultValue = ((int)cacheTimeout.TotalMinutes).ToString();
cacheTimeout = TimeSpan.FromMinutes(Convert.ToInt32(ConfigurationManager.AppSettings["resources:cache-timeout"] ?? defaultValue));
}
catch (Exception err)
{
log.Error(err);
}
}
public DBResourceSet(ResourceSet invariant, CultureInfo culture, string filename)
public DBResourceSet(IConfiguration configuration, IOptionsMonitor<ILog> option, DbOptionsManager optionsDbManager, ResourceSet invariant, CultureInfo culture, string filename)
{
if (culture == null)
{
@ -174,6 +172,21 @@ namespace TMResourceData
throw new ArgumentNullException("filename");
}
Configuration = configuration;
Option = option;
OptionsDbManager = optionsDbManager;
log = option.CurrentValue;
try
{
var defaultValue = ((int)cacheTimeout.TotalMinutes).ToString();
cacheTimeout = TimeSpan.FromMinutes(Convert.ToInt32(configuration["resources:cache-timeout"] ?? defaultValue));
}
catch (Exception err)
{
log.Error(err);
}
this.invariant = invariant;
this.culture = invariant != null ? NEUTRAL_CULTURE : culture.Name;
this.filename = filename.Split('.').Last() + ".resx";
@ -198,11 +211,6 @@ namespace TMResourceData
result = invariant.GetString(name, ignoreCase);
}
if (WhiteLableEnabled)
{
result = WhiteLabelHelper.ReplaceLogo(name, result);
}
return result;
}
@ -249,15 +257,15 @@ namespace TMResourceData
return dic;
}
private static Dictionary<string, string> LoadResourceSet(string filename, string culture)
private Dictionary<string, string> LoadResourceSet(string filename, string culture)
{
using var dbManager = DbManager.FromHttpContext("tmresource");
var q = new SqlQuery("res_data d")
.Select("d.title", "d.textvalue")
.InnerJoin("res_files f", Exp.EqColumns("f.id", "d.fileid"))
.Where("f.resname", filename)
.Where("d.culturetitle", culture);
return dbManager.ExecuteList(q)
var dbManager = OptionsDbManager.Get("tmresource");
var q = new SqlQuery("res_data d")
.Select("d.title", "d.textvalue")
.InnerJoin("res_files f", Exp.EqColumns("f.id", "d.fileid"))
.Where("f.resname", filename)
.Where("d.culturetitle", culture);
return dbManager.ExecuteList(q)
.ToDictionary(r => (string)r[0], r => (string)r[1], StringComparer.InvariantCultureIgnoreCase);
}
}
@ -265,13 +273,21 @@ namespace TMResourceData
public class WhiteLabelHelper
{
private static readonly ILog log = LogManager.GetLogger("ASC.Resources");
private static readonly ConcurrentDictionary<int, string> whiteLabelDictionary = new ConcurrentDictionary<int, string>();
private static readonly string replPattern = ConfigurationManager.AppSettings["resources:whitelabel-text.replacement.pattern"] ?? "(?<=[^@/\\\\]|^)({0})(?!\\.com)";
public static string DefaultLogoText = "";
private readonly ILog log;
private readonly ConcurrentDictionary<int, string> whiteLabelDictionary;
public string DefaultLogoText;
public IConfiguration Configuration { get; }
public static void SetNewText(int tenantId, string newText)
public WhiteLabelHelper(IConfiguration configuration, IOptionsMonitor<ILog> option)
{
log = option.Get("ASC.Resources");
whiteLabelDictionary = new ConcurrentDictionary<int, string>();
DefaultLogoText = "";
Configuration = configuration;
}
public void SetNewText(int tenantId, string newText)
{
try
{
@ -283,7 +299,7 @@ namespace TMResourceData
}
}
public static void RestoreOldText(int tenantId)
public void RestoreOldText(int tenantId)
{
try
{
@ -295,18 +311,22 @@ namespace TMResourceData
}
}
internal static string ReplaceLogo(string resourceName, string resourceValue)
internal string ReplaceLogo(TenantManager tenantManager, IHttpContextAccessor httpContextAccessor, string resourceName, string resourceValue)
{
if (string.IsNullOrEmpty(resourceValue))
{
return resourceValue;
}
if (!DBResourceManager.WhiteLableEnabled)
{
return resourceValue;
}
if (HttpContext.Current != null) //if in Notify Service or other process without HttpContext
if (httpContextAccessor.HttpContext != null) //if in Notify Service or other process without HttpContext
{
try
{
var tenant = CoreContext.TenantManager.GetCurrentTenant(false);
var tenant = tenantManager.GetCurrentTenant(false);
if (tenant == null) return resourceValue;
if (whiteLabelDictionary.TryGetValue(tenant.TenantId, out var newText))
@ -321,8 +341,9 @@ namespace TMResourceData
{
//Hack for string which used in string.Format
newTextReplacement = newTextReplacement.Replace("{", "{{").Replace("}", "}}");
}
}
var replPattern = Configuration["resources:whitelabel-text.replacement.pattern"] ?? "(?<=[^@/\\\\]|^)({0})(?!\\.com)";
var pattern = string.Format(replPattern, DefaultLogoText);
//Hack for resource strings with mails looked like ...@onlyoffice... or with website http://www.onlyoffice.com link or with the https://www.facebook.com/pages/OnlyOffice/833032526736775
@ -338,4 +359,13 @@ namespace TMResourceData
return resourceValue;
}
}
public static class WhiteLabelHelperExtension
{
public static IServiceCollection AddWhiteLabelHelperService(this IServiceCollection services)
{
services.TryAddSingleton<WhiteLabelHelper>();
return services;
}
}
}

View File

@ -44,7 +44,7 @@ namespace ASC.Core
Tenant GetTenantForStandaloneWithoutAlias(string ip);
Tenant SaveTenant(Tenant tenant);
Tenant SaveTenant(CoreSettings coreSettings, Tenant tenant);
void RemoveTenant(int id, bool auto = false);

View File

@ -27,9 +27,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using ASC.Core.Tenants;
using ASC.Notify.Recipients;
namespace ASC.Core.Users
@ -146,23 +144,6 @@ namespace ASC.Core.Users
}
internal List<GroupInfo> GetGroups(Tenant tenant, IncludeType includeType, Guid? categoryId)
{
var groups = CoreContext.UserManager.GetUserGroups(tenant, ID, IncludeType.Distinct, null);
if (categoryId.HasValue)
{
return groups.Where(r => r.CategoryID.Equals(categoryId.Value)).ToList();
}
return groups;
}
internal IEnumerable<Guid> GetUserGroupsId(int tenantId)
{
return CoreContext.UserManager.GetUserGroupsGuids(tenantId, ID);
}
internal string ContactsToString()
{
if (Contacts == null || Contacts.Count == 0) return null;

View File

@ -26,7 +26,6 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using ASC.Common.Data;
using ASC.Common.Data.Sql;
@ -36,10 +35,10 @@ using ASC.Core.Tenants;
namespace ASC.Core.Data
{
public class DbAzService : DbBaseService, IAzService
class DbAzService : DbBaseService, IAzService
{
public DbAzService(ConnectionStringSettings connectionString)
: base(connectionString, "tenant")
public DbAzService(DbOptionsManager dbOptionsManager)
: base(dbOptionsManager, "tenant")
{
}
@ -82,7 +81,7 @@ namespace ASC.Core.Data
public AzRecord SaveAce(int tenant, AzRecord r)
{
r.Tenant = tenant;
using (var db = GetDb())
var db = GetDb();
using (var tx = db.BeginTransaction())
{
if (!ExistEscapeRecord(db, r))
@ -103,7 +102,7 @@ namespace ASC.Core.Data
public void RemoveAce(int tenant, AzRecord r)
{
r.Tenant = tenant;
using var db = GetDb();
var db = GetDb();
using var tx = db.BeginTransaction();
if (ExistEscapeRecord(db, r))
{

View File

@ -25,26 +25,25 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Collections.Generic;
using ASC.Common.Data;
using ASC.Common.Data.Sql;
namespace ASC.Core.Data
{
public abstract class DbBaseService
{
private readonly string dbid;
{
private DbOptionsManager DbOptionsManager { get; }
protected string TenantColumn
{
get;
private set;
}
}
protected DbBaseService(ConnectionStringSettings connectionString, string tenantColumn)
{
dbid = connectionString.Name;
protected DbBaseService(DbOptionsManager dbOptionsManager, string tenantColumn)
{
DbOptionsManager = dbOptionsManager;
TenantColumn = tenantColumn;
}
@ -75,7 +74,7 @@ namespace ASC.Core.Data
protected IDbManager GetDb()
{
return DbManager.FromHttpContext(dbid);
return DbOptionsManager?.Value;
}
protected SqlQuery Query(string table, int tenant)
@ -106,7 +105,7 @@ namespace ASC.Core.Data
private T Execute<T>(Func<IDbManager, T> action)
{
using var db = GetDb();
var db = GetDb();
return action(db);
}
}

View File

@ -26,22 +26,21 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using ASC.Common.Data;
using ASC.Common.Data.Sql;
using ASC.Common.Data.Sql.Expressions;
using ASC.Core.Tenants;
namespace ASC.Core.Data
{
public class DbQuotaService : DbBaseService, IQuotaService
class DbQuotaService : DbBaseService, IQuotaService
{
private const string tenants_quota = "tenants_quota";
internal const string tenants_quotarow = "tenants_quotarow";
public DbQuotaService(ConnectionStringSettings connectionString)
: base(connectionString, "tenant")
public DbQuotaService(DbOptionsManager dbOptionsManager)
: base(dbOptionsManager, "tenant")
{
}
@ -109,7 +108,7 @@ namespace ASC.Core.Data
{
if (row == null) throw new ArgumentNullException("row");
using var db = GetDb();
var db = GetDb();
using var tx = db.BeginTransaction();
var counter = db.ExecuteScalar<long>(Query(tenants_quotarow, row.Tenant)
.Select("counter")

View File

@ -37,64 +37,97 @@ using ASC.Common.Caching;
using ASC.Common.Data;
using ASC.Common.Data.Sql;
using ASC.Common.Logging;
using ASC.Core.Common.Settings;
using ASC.Core.Common.Settings;
using ASC.Core.Tenants;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace ASC.Core.Data
{
internal class DbSettingsManager
{
private static readonly ILog log = LogManager.GetLogger("ASC");
{
public class DbSettingsManagerCache
{
public ICache Cache { get; }
public ICacheNotify<SettingsCacheItem> Notify { get; }
public DbSettingsManagerCache(ICacheNotify<SettingsCacheItem> notify)
{
Cache = AscCache.Memory;
Notify = notify;
Notify.Subscribe((i) => Cache.Remove(i.Key), CacheNotifyAction.Remove);
}
public void Remove(string key)
{
Notify.Publish(new SettingsCacheItem { Key = key }, CacheNotifyAction.Remove);
}
}
private static readonly ICache cache = AscCache.Memory;
private static readonly ICacheNotify<SettingsCacheItem> notify;
public class DbSettingsManager
{
private readonly ILog log;
private readonly TimeSpan expirationTimeout = TimeSpan.FromMinutes(5);
private readonly IDictionary<Type, DataContractJsonSerializer> jsonSerializers = new Dictionary<Type, DataContractJsonSerializer>();
private readonly string dbId;
private readonly string dbId;
private ICache Cache { get; }
private IServiceProvider ServiceProvider { get; }
private DbSettingsManagerCache DbSettingsManagerCache { get; }
public AuthContext AuthContext { get; }
public TenantManager TenantManager { get; }
private DbManager DbManager { get; }
public DbSettingsManager(
IServiceProvider serviceProvider,
DbSettingsManagerCache dbSettingsManagerCache,
DbOptionsManager optionsDbManager,
IOptionsMonitor<ILog> option,
AuthContext authContext, TenantManager tenantManager) : this(null)
{
ServiceProvider = serviceProvider;
DbSettingsManagerCache = dbSettingsManagerCache;
AuthContext = authContext;
TenantManager = tenantManager;
Cache = dbSettingsManagerCache.Cache;
DbManager = optionsDbManager.Value;
log = option.CurrentValue;
}
public DbSettingsManager(ConnectionStringSettings connectionString)
{
dbId = connectionString != null ? connectionString.Name : "default";
}
private int TenantID
{
get { return TenantManager.GetCurrentTenant().TenantId; }
}
static DbSettingsManager()
{
notify = new KafkaCache<SettingsCacheItem>();
notify.Subscribe((i) => cache.Remove(i.Key), CacheNotifyAction.Remove);
}
//
private Guid CurrentUserID
{
get { return AuthContext.CurrentAccount.ID; }
}
public bool SaveSettings<T>(T settings, int tenantId) where T : ISettings
{
return SaveSettingsFor(settings, tenantId, Guid.Empty);
}
public bool SaveSettingsFor<T>(T settings, Guid userId) where T : class, ISettings
{
return SaveSettingsFor(settings, CoreContext.TenantManager.GetCurrentTenant().TenantId, userId);
}
public T LoadSettings<T>(int tenantId) where T : class, ISettings
{
return LoadSettingsFor<T>(tenantId, Guid.Empty);
}
public T LoadSettingsFor<T>(Guid userId) where T : class, ISettings
public void ClearCache<T>(int tenantId) where T : class, ISettings
{
return LoadSettingsFor<T>(CoreContext.TenantManager.GetCurrentTenant().TenantId, userId);
}
public void ClearCache<T>() where T : class, ISettings
{
var tenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId;
var settings = LoadSettings<T>(tenantId);
var key = settings.ID.ToString() + tenantId + Guid.Empty;
notify.Publish(new SettingsCacheItem { Key = key }, CacheNotifyAction.Remove);
var key = settings.ID.ToString() + tenantId + Guid.Empty;
DbSettingsManagerCache.Remove(key);
}
private bool SaveSettingsFor<T>(T settings, int tenantId, Guid userId) where T : ISettings
public bool SaveSettingsFor<T>(T settings, int tenantId, Guid userId) where T : ISettings
{
if (settings == null) throw new ArgumentNullException("settings");
try
@ -102,32 +135,34 @@ namespace ASC.Core.Data
var key = settings.ID.ToString() + tenantId + userId;
var data = Serialize(settings);
using (var db = GetDbManager())
var db = DbManager;
var def = (T)settings.GetDefault(ServiceProvider);
var defaultData = Serialize(def);
ISqlInstruction i;
if (data.SequenceEqual(defaultData))
{
var defaultData = Serialize(settings.GetDefault());
ISqlInstruction i;
if (data.SequenceEqual(defaultData))
{
// remove default settings
i = new SqlDelete("webstudio_settings")
.Where("id", settings.ID.ToString())
.Where("tenantid", tenantId)
.Where("userid", userId.ToString());
}
else
{
i = new SqlInsert("webstudio_settings", true)
.InColumnValue("id", settings.ID.ToString())
.InColumnValue("userid", userId.ToString())
.InColumnValue("tenantid", tenantId)
.InColumnValue("data", data);
}
notify.Publish(new SettingsCacheItem { Key = key }, CacheNotifyAction.Remove);
db.ExecuteNonQuery(i);
// remove default settings
i = new SqlDelete("webstudio_settings")
.Where("id", settings.ID.ToString())
.Where("tenantid", tenantId)
.Where("userid", userId.ToString());
}
else
{
i = new SqlInsert("webstudio_settings", true)
.InColumnValue("id", settings.ID.ToString())
.InColumnValue("userid", userId.ToString())
.InColumnValue("tenantid", tenantId)
.InColumnValue("data", data);
}
DbSettingsManagerCache.Remove(key);
cache.Insert(key, settings, expirationTimeout);
db.ExecuteNonQuery(i);
Cache.Insert(key, settings, expirationTimeout);
return true;
}
catch (Exception ex)
@ -139,50 +174,104 @@ namespace ASC.Core.Data
internal T LoadSettingsFor<T>(int tenantId, Guid userId) where T : class, ISettings
{
var settingsInstance = (ISettings)Activator.CreateInstance<T>();
var settingsInstance = Activator.CreateInstance<T>();
var key = settingsInstance.ID.ToString() + tenantId + userId;
var def = (T)settingsInstance.GetDefault(ServiceProvider);
try
{
var settings = cache.Get<T>(key);
if (settings != null) return settings;
var settings = Cache.Get<T>(key);
if (settings != null) return settings;
using (var db = GetDbManager())
var db = DbManager;
var q = new SqlQuery("webstudio_settings")
.Select("data")
.Where("id", settingsInstance.ID.ToString())
.Where("tenantid", tenantId)
.Where("userid", userId.ToString());
var result = db.ExecuteScalar<object>(q);
if (result != null)
{
var q = new SqlQuery("webstudio_settings")
.Select("data")
.Where("id", settingsInstance.ID.ToString())
.Where("tenantid", tenantId)
.Where("userid", userId.ToString());
var result = db.ExecuteScalar<object>(q);
if (result != null)
{
var data = result is string ? Encoding.UTF8.GetBytes((string)result) : (byte[])result;
settings = Deserialize<T>(data);
}
else
{
settings = (T)settingsInstance.GetDefault();
}
var data = result is string ? Encoding.UTF8.GetBytes((string)result) : (byte[])result;
settings = Deserialize<T>(data);
}
else
{
settings = def;
}
cache.Insert(key, settings, expirationTimeout);
Cache.Insert(key, settings, expirationTimeout);
return settings;
}
catch (Exception ex)
{
log.Error(ex);
}
return (T)settingsInstance.GetDefault();
return def;
}
public T Load<T>() where T : class, ISettings
{
return LoadSettings<T>(TenantID);
}
public T LoadForCurrentUser<T>() where T : class, ISettings
{
return LoadForUser<T>(CurrentUserID);
}
public T LoadForUser<T>(Guid userId) where T : class, ISettings
{
return LoadSettingsFor<T>(TenantID, userId);
}
public T LoadForDefaultTenant<T>() where T : class, ISettings
{
return LoadForTenant<T>(Tenant.DEFAULT_TENANT);
}
public T LoadForTenant<T>(int tenantId) where T : class, ISettings
{
return LoadSettings<T>(tenantId);
}
public virtual bool Save<T>(T data) where T : class, ISettings
{
return SaveSettings(data, TenantID);
}
public bool SaveForCurrentUser<T>(T data) where T : class, ISettings
{
return SaveForUser<T>(data, CurrentUserID);
}
public bool SaveForUser<T>(T data, Guid userId) where T : class, ISettings
{
return SaveSettingsFor(data, TenantID, userId);
}
public bool SaveForDefaultTenant<T>(T data) where T : class, ISettings
{
return SaveForTenant<T>(data, Tenant.DEFAULT_TENANT);
}
public bool SaveForTenant<T>(T data, int tenantId) where T : class, ISettings
{
return SaveSettings<T>(data, tenantId);
}
public void ClearCache<T>() where T : class, ISettings
{
ClearCache<T>(TenantID);
}
private T Deserialize<T>(byte[] data)
{
using var stream = new MemoryStream(data);
var settings = data[0] == 0
? new BinaryFormatter().Deserialize(stream)
: GetJsonSerializer(typeof(T)).ReadObject(stream);
? new BinaryFormatter().Deserialize(stream)
: GetJsonSerializer(typeof(T)).ReadObject(stream);
return (T)settings;
}
@ -193,11 +282,6 @@ namespace ASC.Core.Data
return stream.ToArray();
}
private IDbManager GetDbManager()
{
return DbManager.FromHttpContext(dbId);
}
private DataContractJsonSerializer GetJsonSerializer(Type type)
{
lock (jsonSerializers)
@ -209,5 +293,16 @@ namespace ASC.Core.Data
return jsonSerializers[type];
}
}
}
public static class DbSettingsManagerExtension
{
public static IServiceCollection AddDbSettingsManagerService(this IServiceCollection services)
{
services.TryAddSingleton<DbSettingsManagerCache>();
services.TryAddScoped<DbSettingsManager>();
return services.AddDbManagerService();
}
}
}

View File

@ -26,18 +26,18 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using ASC.Common.Data;
using ASC.Common.Data.Sql;
using ASC.Common.Data.Sql.Expressions;
using ASC.Core.Tenants;
namespace ASC.Core.Data
{
public class DbSubscriptionService : DbBaseService, ISubscriptionService
class DbSubscriptionService : DbBaseService, ISubscriptionService
{
public DbSubscriptionService(ConnectionStringSettings connectionString)
: base(connectionString, "tenant")
public DbSubscriptionService(DbOptionsManager dbOptionsManager)
: base(dbOptionsManager, "tenant")
{
}

View File

@ -26,10 +26,10 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using ASC.Common.Data;
using ASC.Common.Data.Sql;
using ASC.Common.Data.Sql.Expressions;
@ -42,18 +42,22 @@ namespace ASC.Core.Data
public class DbTenantService : DbBaseService, ITenantService
{
private static TimeZoneInfo defaultTimeZone;
private List<string> forbiddenDomains;
public DbTenantService(ConnectionStringSettings connectionString)
: base(connectionString, null)
{
private List<string> forbiddenDomains;
private TenantDomainValidator TenantDomainValidator { get; }
private TimeZoneConverter TimeZoneConverter { get; }
public DbTenantService(DbOptionsManager dbOptionsManager, TenantDomainValidator tenantDomainValidator, TimeZoneConverter timeZoneConverter)
: base(dbOptionsManager, null)
{
TenantDomainValidator = tenantDomainValidator;
TimeZoneConverter = timeZoneConverter;
}
public void ValidateDomain(string domain)
{
using var db = GetDb();
var db = GetDb();
ValidateDomain(db, domain, Tenant.DEFAULT_TENANT, true);
}
@ -113,16 +117,16 @@ namespace ASC.Core.Data
.FirstOrDefault();
}
public Tenant SaveTenant(Tenant t)
public Tenant SaveTenant(CoreSettings coreSettings, Tenant t)
{
if (t == null) throw new ArgumentNullException("tenant");
using (var db = GetDb())
var db = GetDb();
using (var tx = db.BeginTransaction())
{
if (!string.IsNullOrEmpty(t.MappedDomain))
{
var baseUrl = TenantUtil.GetBaseDomain(t.HostedRegion);
var baseUrl = coreSettings.GetBaseDomain(t.HostedRegion);
if (baseUrl != null && t.MappedDomain.EndsWith("." + baseUrl, StringComparison.InvariantCultureIgnoreCase))
{
@ -218,7 +222,7 @@ namespace ASC.Core.Data
{
var postfix = auto ? "_auto_deleted" : "_deleted";
using var db = GetDb();
var db = GetDb();
using var tx = db.BeginTransaction();
var alias = db.ExecuteScalar<string>(new SqlQuery("tenants_tenants").Select("alias").Where("id", id));
var count = db.ExecuteScalar<int>(new SqlQuery("tenants_tenants").SelectCount().Where(Exp.Like("alias", alias + postfix, SqlLike.StartWith)));

View File

@ -26,9 +26,8 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Linq;
using ASC.Common.Data;
using ASC.Common.Data.Sql;
using ASC.Common.Data.Sql.Expressions;
using ASC.Core.Tenants;
@ -37,10 +36,10 @@ using ASC.Security.Cryptography;
namespace ASC.Core.Data
{
public class DbUserService : DbBaseService, IUserService
class DbUserService : DbBaseService, IUserService
{
public DbUserService(ConnectionStringSettings connectionString)
: base(connectionString, "tenant")
public DbUserService(DbOptionsManager dbOptionsManager)
: base(dbOptionsManager, "tenant")
{
}
@ -213,7 +212,7 @@ namespace ASC.Core.Data
user.LastModified = DateTime.UtcNow;
user.Tenant = tenant;
using (var db = GetDb())
var db = GetDb();
using (var tx = db.BeginTransaction())
{
user.UserName = user.UserName.Trim();

View File

@ -28,7 +28,10 @@ using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Security;
using System.Security;
using ASC.Common.Data;
using ASC.Common.Logging;
using ASC.Common.Utils;
using ASC.Core.Billing;
using ASC.Core.Data;
using ASC.Core.Security.Authentication;
@ -36,6 +39,9 @@ using ASC.Core.Tenants;
using ASC.Core.Users;
using ASC.Security.Cryptography;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
namespace ASC.Core
{
public class HostedSolution
@ -46,6 +52,7 @@ namespace ASC.Core
private readonly ITariffService tariffService;
private readonly TenantManager clientTenantManager;
private readonly DbSettingsManager settingsManager;
private readonly CoreSettings coreSettings;
public string Region
{
@ -59,18 +66,37 @@ namespace ASC.Core
private set;
}
public HostedSolution(ConnectionStringSettings connectionString)
: this(connectionString, null)
public HostedSolution(
IConfiguration configuration,
TenantDomainValidator tenantDomainValidator,
TimeZoneConverter timeZoneConverter,
DbRegistry dbRegistry,
ConnectionStringSettings connectionString,
TariffServiceStorage tariffServiceStorage,
IOptionsMonitor<ILog> options)
: this(configuration, tenantDomainValidator, timeZoneConverter, dbRegistry, connectionString, tariffServiceStorage, options, null)
{
}
public HostedSolution(ConnectionStringSettings connectionString, string region)
//TODO:fix
public HostedSolution(
IConfiguration configuration,
TenantDomainValidator tenantDomainValidator,
TimeZoneConverter timeZoneConverter,
DbRegistry dbRegistry,
ConnectionStringSettings connectionString,
TariffServiceStorage tariffServiceStorage,
IOptionsMonitor<ILog> options,
string region)
{
tenantService = new DbTenantService(connectionString);
userService = new DbUserService(connectionString);
quotaService = new DbQuotaService(connectionString);
tariffService = new TariffService(connectionString, quotaService, tenantService);
clientTenantManager = new TenantManager(tenantService, quotaService, tariffService);
tenantService = new DbTenantService(null, null, null);
var baseSettings = new CoreBaseSettings(configuration);
coreSettings = new CoreSettings(tenantService, baseSettings, configuration);
userService = new DbUserService(null);
quotaService = new DbQuotaService(null);
tariffService = new TariffService(quotaService, tenantService, baseSettings, coreSettings, configuration, null, tariffServiceStorage, options);
clientTenantManager = new TenantManager(tenantService, quotaService, tariffService, null, baseSettings, coreSettings);
settingsManager = new DbSettingsManager(connectionString);
Region = region ?? string.Empty;
DbId = connectionString.Name;
@ -112,7 +138,7 @@ namespace ASC.Core
}
public void RegisterTenant(TenantRegistrationInfo ri, out Tenant tenant)
{
{
if (ri == null) throw new ArgumentNullException("registrationInfo");
if (string.IsNullOrEmpty(ri.Address)) throw new Exception("Address can not be empty");
@ -137,7 +163,7 @@ namespace ASC.Core
Calls = ri.Calls
};
tenant = tenantService.SaveTenant(tenant);
tenant = tenantService.SaveTenant(coreSettings, tenant);
// create user
var user = new UserInfo
@ -156,14 +182,14 @@ namespace ASC.Core
// save tenant owner
tenant.OwnerId = user.ID;
tenant = tenantService.SaveTenant(tenant);
tenant = tenantService.SaveTenant(coreSettings, tenant);
settingsManager.SaveSettings(new TenantAnalyticsSettings { Analytics = ri.Analytics }, tenant.TenantId);
settingsManager.SaveSettings(new TenantAnalyticsSettings() { Analytics = ri.Analytics }, tenant.TenantId);
}
public Tenant SaveTenant(Tenant tenant)
{
return tenantService.SaveTenant(tenant);
return tenantService.SaveTenant(coreSettings, tenant);
}
public void RemoveTenant(Tenant tenant)
@ -171,27 +197,27 @@ namespace ASC.Core
tenantService.RemoveTenant(tenant.TenantId);
}
public string CreateAuthenticationCookie(int tenantId, string login, string password)
public string CreateAuthenticationCookie(CookieStorage cookieStorage, int tenantId, string login, string password)
{
var passwordhash = Hasher.Base64Hash(password, HashAlg.SHA256);
var u = userService.GetUser(tenantId, login, passwordhash);
return u != null ? CreateAuthenticationCookie(tenantId, u.ID, login, passwordhash) : null;
return u != null ? CreateAuthenticationCookie(cookieStorage, tenantId, u.ID, login, passwordhash) : null;
}
public string CreateAuthenticationCookie(int tenantId, Guid userId)
public string CreateAuthenticationCookie(CookieStorage cookieStorage, int tenantId, Guid userId)
{
var u = userService.GetUser(tenantId, userId);
var password = userService.GetUserPassword(tenantId, userId);
var passwordhash = Hasher.Base64Hash(password, HashAlg.SHA256);
return u != null ? CreateAuthenticationCookie(tenantId, userId, u.Email, passwordhash) : null;
return u != null ? CreateAuthenticationCookie(cookieStorage, tenantId, userId, u.Email, passwordhash) : null;
}
private string CreateAuthenticationCookie(int tenantId, Guid userId, string login, string passwordhash)
private string CreateAuthenticationCookie(CookieStorage cookieStorage, int tenantId, Guid userId, string login, string passwordhash)
{
var tenantSettings = settingsManager.LoadSettingsFor<TenantCookieSettings>(tenantId, Guid.Empty);
var expires = tenantSettings.IsDefault() ? DateTime.UtcNow.AddYears(1) : DateTime.UtcNow.AddMinutes(tenantSettings.LifeTime);
var userSettings = settingsManager.LoadSettingsFor<TenantCookieSettings>(tenantId, userId);
return CookieStorage.EncryptCookie(tenantId, userId, login, passwordhash, tenantSettings.Index, expires, userSettings.Index);
return cookieStorage.EncryptCookie(tenantId, userId, login, passwordhash, tenantSettings.Index, expires, userSettings.Index);
}
public Tariff GetTariff(int tenant, bool withRequestToPaymentSystem = true)

View File

@ -28,25 +28,53 @@ using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Security;
using System.Security;
using ASC.Common.Data;
using ASC.Common.Data.Sql;
using ASC.Common.Logging;
using ASC.Common.Utils;
using ASC.Core.Billing;
using ASC.Core.Security.Authentication;
using ASC.Core.Tenants;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
namespace ASC.Core
{
public class MultiRegionHostedSolution
{
private readonly object locker = new object();
private readonly Dictionary<string, HostedSolution> regions = new Dictionary<string, HostedSolution>();
private readonly string dbid;
private volatile bool initialized = false;
public MultiRegionHostedSolution(string dbid)
public IConfiguration Configuraion { get; }
public TenantDomainValidator TenantDomainValidator { get; }
public TimeZoneConverter TimeZoneConverter { get; }
public CookieStorage CookieStorage { get; }
public DbRegistry DbRegistry { get; }
public DbOptionsManager DbOptions { get; }
public TariffServiceStorage TariffServiceStorage { get; }
public IOptionsMonitor<ILog> Options { get; }
public MultiRegionHostedSolution(string dbid,
IConfiguration configuraion,
TenantDomainValidator tenantDomainValidator,
TimeZoneConverter timeZoneConverter,
CookieStorage cookieStorage,
DbRegistry dbRegistry,
DbOptionsManager dbOptions,
TariffServiceStorage tariffServiceStorage,
IOptionsMonitor<ILog> options)
{
this.dbid = dbid;
Configuraion = configuraion;
TenantDomainValidator = tenantDomainValidator;
TimeZoneConverter = timeZoneConverter;
CookieStorage = cookieStorage;
DbRegistry = dbRegistry;
DbOptions = dbOptions;
TariffServiceStorage = tariffServiceStorage;
Options = options;
Initialize();
}
public List<Tenant> GetTenants(DateTime from)
@ -116,12 +144,12 @@ namespace ASC.Core
public string CreateAuthenticationCookie(string region, int tenantId, string login, string password)
{
return GetRegionService(region).CreateAuthenticationCookie(tenantId, login, password);
return GetRegionService(region).CreateAuthenticationCookie(CookieStorage, tenantId, login, password);
}
public string CreateAuthenticationCookie(string region, int tenantId, Guid userId)
{
return GetRegionService(region).CreateAuthenticationCookie(tenantId, userId);
return GetRegionService(region).CreateAuthenticationCookie(CookieStorage, tenantId, userId);
}
@ -165,12 +193,12 @@ namespace ASC.Core
public IDbManager GetRegionDb(string region)
{
return new DbManager(GetRegionService(region).DbId);
return DbOptions.Get(GetRegionService(region).DbId);
}
public IDbManager GetMultiRegionDb()
{
return new MultiRegionalDbManager(GetRegions().Select(r => new DbManager(GetRegionService(r).DbId)));
return new MultiRegionalDbManager(GetRegions().Select(r => DbOptions.Get(GetRegionService(r).DbId)));
}
public System.Configuration.ConnectionStringSettings GetRegionConnectionString(string region)
@ -181,99 +209,87 @@ namespace ASC.Core
private IEnumerable<HostedSolution> GetRegionServices()
{
Initialize();
return regions.Where(x => !string.IsNullOrEmpty(x.Key))
.Select(x => x.Value);
}
private HostedSolution GetRegionService(string region)
{
Initialize();
return regions[region];
}
private void Initialize()
{
if (!initialized)
var connectionStrings = Configuraion.GetConnectionStrings();
var dbConnectionStrings = Configuraion.GetConnectionStrings(dbid);
if (Convert.ToBoolean(Configuraion["core.multi-hosted.config-only"] ?? "false"))
{
lock (locker)
foreach (var cs in Configuraion.GetConnectionStrings())
{
if (!initialized)
if (cs.Name.StartsWith(dbid + "."))
{
initialized = true;
var name = cs.Name.Substring(dbid.Length + 1);
regions[name] = new HostedSolution(Configuraion, TenantDomainValidator, TimeZoneConverter, DbRegistry, cs, TariffServiceStorage, Options, name);
}
}
regions[dbid] = new HostedSolution(Configuraion, TenantDomainValidator, TimeZoneConverter, DbRegistry, dbConnectionStrings, TariffServiceStorage, Options);
if (!regions.ContainsKey(string.Empty))
{
regions[string.Empty] = new HostedSolution(Configuraion, TenantDomainValidator, TimeZoneConverter, DbRegistry, dbConnectionStrings, TariffServiceStorage, Options);
}
}
else
{
if (Convert.ToBoolean(ConfigurationManager.AppSettings["core.multi-hosted.config-only"] ?? "false"))
var find = false;
foreach (var cs in connectionStrings)
{
if (cs.Name.StartsWith(dbid + "."))
{
var name = cs.Name.Substring(dbid.Length + 1);
regions[name] = new HostedSolution(Configuraion, TenantDomainValidator, TimeZoneConverter, DbRegistry, cs, TariffServiceStorage, Options, name);
find = true;
}
}
if (find)
{
regions[dbid] = new HostedSolution(Configuraion, TenantDomainValidator, TimeZoneConverter, DbRegistry, dbConnectionStrings, TariffServiceStorage, Options);
if (!regions.ContainsKey(string.Empty))
{
regions[string.Empty] = new HostedSolution(Configuraion, TenantDomainValidator, TimeZoneConverter, DbRegistry, dbConnectionStrings, TariffServiceStorage, Options);
}
}
else
{
foreach (var connectionString in connectionStrings)
{
try
{
foreach (var cs in ConfigurationManager.ConnectionStrings)
{
if (cs.Name.StartsWith(dbid + "."))
using var db = DbOptions.Get(connectionString.Name);
var q = new SqlQuery("regions")
.Select("region")
.Select("connection_string")
.Select("provider");
db.ExecuteList(q)
.ForEach(r =>
{
var name = cs.Name.Substring(dbid.Length + 1);
regions[name] = new HostedSolution(cs, name);
}
}
regions[dbid] = new HostedSolution(ConfigurationManager.ConnectionStrings[dbid]);
if (!regions.ContainsKey(string.Empty))
{
regions[string.Empty] = new HostedSolution(ConfigurationManager.ConnectionStrings[dbid]);
}
}
else
{
var find = false;
foreach (var cs in ConfigurationManager.ConnectionStrings)
{
if (cs.Name.StartsWith(dbid + "."))
{
var name = cs.Name.Substring(dbid.Length + 1);
regions[name] = new HostedSolution(cs, name);
find = true;
}
}
if (find)
{
regions[dbid] = new HostedSolution(ConfigurationManager.ConnectionStrings[dbid]);
if (!regions.ContainsKey(string.Empty))
{
regions[string.Empty] = new HostedSolution(ConfigurationManager.ConnectionStrings[dbid]);
}
}
else
{
foreach (var connectionString in ConfigurationManager.ConnectionStrings)
{
try
var cs = new System.Configuration.ConnectionStringSettings((string)r[0], (string)r[1], (string)r[2]);
if (!DbRegistry.IsDatabaseRegistered(cs.Name))
{
using var db = new DbManager(connectionString.Name);
var q = new SqlQuery("regions")
.Select("region")
.Select("connection_string")
.Select("provider");
db.ExecuteList(q)
.ForEach(r =>
{
var cs = new System.Configuration.ConnectionStringSettings((string)r[0], (string)r[1], (string)r[2]);
if (!DbRegistry.IsDatabaseRegistered(cs.Name))
{
DbRegistry.RegisterDatabase(cs.Name, cs);
}
if (!regions.ContainsKey(string.Empty))
{
regions[string.Empty] = new HostedSolution(cs, cs.Name);
}
regions[cs.Name] = new HostedSolution(cs, cs.Name);
});
DbRegistry.RegisterDatabase(cs.Name, cs);
}
catch (DbException) { }
}
}
if (!regions.ContainsKey(string.Empty))
{
regions[string.Empty] = new HostedSolution(Configuraion, TenantDomainValidator, TimeZoneConverter, DbRegistry, cs, TariffServiceStorage, Options, cs.Name);
}
regions[cs.Name] = new HostedSolution(Configuraion, TenantDomainValidator, TimeZoneConverter, DbRegistry, cs, TariffServiceStorage, Options, cs.Name);
});
}
catch (DbException) { }
}
}
}

View File

@ -31,7 +31,10 @@ using ASC.Common.Logging;
using ASC.Notify.Channels;
using ASC.Notify.Engine;
using ASC.Notify.Model;
using ASC.Notify.Sinks;
using ASC.Notify.Sinks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace ASC.Notify
{
@ -65,11 +68,14 @@ namespace ASC.Notify
public event Action<Context, INotifyClient> NotifyClientRegistration;
private ILog Log { get; set; }
public Context()
public Context(IServiceProvider serviceProvider)
{
NotifyEngine = new NotifyEngine(this);
DispatchEngine = new DispatchEngine(this);
var options = serviceProvider.GetService<IOptionsMonitor<ILog>>();
Log = options.CurrentValue;
NotifyEngine = new NotifyEngine(this, serviceProvider);
DispatchEngine = new DispatchEngine(this, serviceProvider.GetService<IConfiguration>(), options);
}
@ -98,10 +104,10 @@ namespace ASC.Notify
}
}
INotifyClient INotifyRegistry.RegisterClient(INotifySource source)
INotifyClient INotifyRegistry.RegisterClient(INotifySource source, IServiceScope serviceScope)
{
//ValidateNotifySource(source);
var client = new NotifyClientImpl(this, source);
var client = new NotifyClientImpl(this, source, serviceScope);
NotifyClientRegistration?.Invoke(this, client);
return client;
}
@ -128,7 +134,7 @@ namespace ASC.Notify
}
catch (Exception error)
{
LogManager.GetLogger("ASC.Notify").ErrorFormat("Source: {0}, action: {1}, sender: {2}, error: {3}", source.ID, a.ID, s, error);
Log.ErrorFormat("Source: {0}, action: {1}, sender: {2}, error: {3}", source.ID, a.ID, s, error);
}
}
}

View File

@ -26,7 +26,6 @@
using System;
using System.Linq;
using ASC.Core.Tenants;
using ASC.Notify.Model;
using ASC.Notify.Recipients;
@ -48,7 +47,7 @@ namespace ASC.Core.Notify
}
public object GetSubscriptionRecord(Tenant tenant, INotifyAction action, IRecipient recipient, string objectID)
public object GetSubscriptionRecord(INotifyAction action, IRecipient recipient, string objectID)
{
if (action == null) throw new ArgumentNullException("action");
if (recipient == null) throw new ArgumentNullException("recipient");
@ -56,7 +55,7 @@ namespace ASC.Core.Notify
return subscriptionManager.GetSubscriptionRecord(sourceID, action.ID, recipient.ID, objectID);
}
public string[] GetSubscriptions(Tenant tenant, INotifyAction action, IRecipient recipient, bool checkSubscribe = true)
public string[] GetSubscriptions(INotifyAction action, IRecipient recipient, bool checkSubscribe = true)
{
if (action == null) throw new ArgumentNullException("action");
if (recipient == null) throw new ArgumentNullException("recipient");
@ -64,17 +63,17 @@ namespace ASC.Core.Notify
return subscriptionManager.GetSubscriptions(sourceID, action.ID, recipient.ID, checkSubscribe);
}
public IRecipient[] GetRecipients(int tenantId, INotifyAction action, string objectID)
public IRecipient[] GetRecipients(INotifyAction action, string objectID)
{
if (action == null) throw new ArgumentNullException("action");
return subscriptionManager.GetRecipients(sourceID, action.ID, objectID)
.Select(r => recipientProvider.GetRecipient(tenantId, r))
.Select(r => recipientProvider.GetRecipient(r))
.Where(r => r != null)
.ToArray();
}
public string[] GetSubscriptionMethod(Tenant tenant, INotifyAction action, IRecipient recipient)
public string[] GetSubscriptionMethod(INotifyAction action, IRecipient recipient)
{
if (action == null) throw new ArgumentNullException("action");
if (recipient == null) throw new ArgumentNullException("recipient");
@ -128,7 +127,7 @@ namespace ASC.Core.Notify
}
[Obsolete("Use UnSubscribe(INotifyAction, string, IRecipient)", true)]
public void UnSubscribe(Tenant tenant, INotifyAction action, IRecipient recipient)
public void UnSubscribe(INotifyAction action, IRecipient recipient)
{
throw new NotSupportedException("use UnSubscribe(INotifyAction, string, IRecipient )");
}

View File

@ -26,13 +26,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using ASC.Common.Logging;
using ASC.Common.Utils;
using ASC.Core.Notify.Senders;
using ASC.Core.Tenants;
using ASC.Notify.Messages;
using ASC.Notify.Sinks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace ASC.Core.Notify
{
@ -42,11 +44,13 @@ namespace ASC.Core.Notify
private readonly INotifySender sender;
public EmailSenderSink(INotifySender sender)
{
public EmailSenderSink(INotifySender sender, IServiceProvider serviceProvider)
{
this.sender = sender ?? throw new ArgumentNullException("sender");
ServiceProvider = serviceProvider;
}
public IServiceProvider ServiceProvider { get; }
public override SendResponse ProcessMessage(INoticeMessage message)
{
@ -61,13 +65,13 @@ namespace ASC.Core.Notify
var m = CreateNotifyMessage(message);
var result = sender.Send(m);
responce.Result = result switch
{
responce.Result = result switch
{
NoticeSendResult.TryOnceAgain => SendResult.Inprogress,
NoticeSendResult.MessageIncorrect => SendResult.IncorrectRecipient,
NoticeSendResult.SendingImpossible => SendResult.Impossible,
_ => SendResult.OK,
};
_ => SendResult.OK,
};
return responce;
}
catch (Exception e)
@ -88,12 +92,16 @@ namespace ASC.Core.Notify
CreationDate = DateTime.UtcNow.Ticks,
};
var tenant = CoreContext.TenantManager.GetCurrentTenant(false);
using var scope = ServiceProvider.CreateScope();
var tenantManager = scope.ServiceProvider.GetService<TenantManager>();
var configuration = scope.ServiceProvider.GetService<CoreConfiguration>();
var tenant = tenantManager.GetCurrentTenant(false);
m.Tenant = tenant == null ? Tenant.DEFAULT_TENANT : tenant.TenantId;
var from = MailAddressUtils.Create(CoreContext.Configuration.SmtpSettings.SenderAddress, CoreContext.Configuration.SmtpSettings.SenderDisplayName);
var from = MailAddressUtils.Create(configuration.SmtpSettings.SenderAddress, configuration.SmtpSettings.SenderDisplayName);
var fromTag = message.Arguments.FirstOrDefault(x => x.Tag.Equals("MessageFrom"));
if ((CoreContext.Configuration.SmtpSettings.IsDefaultSettings || string.IsNullOrEmpty(CoreContext.Configuration.SmtpSettings.SenderDisplayName)) &&
if ((configuration.SmtpSettings.IsDefaultSettings || string.IsNullOrEmpty(configuration.SmtpSettings.SenderDisplayName)) &&
fromTag != null && fromTag.Value != null)
{
try
@ -120,7 +128,7 @@ namespace ASC.Core.Notify
}
catch (Exception e)
{
LogManager.GetLogger("ASC.Notify").Error("Error creating reply to tag for: " + replyTag.Value, e);
ServiceProvider.GetService<IOptionsMonitor<ILog>>().Get("ASC.Notify").Error("Error creating reply to tag for: " + replyTag.Value, e);
}
}

View File

@ -27,24 +27,27 @@
using System;
using ASC.Common.Logging;
using ASC.Common.Utils;
using ASC.Notify.Messages;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
namespace ASC.Notify.Engine
{
public class DispatchEngine
{
private static readonly ILog log = LogManager.GetLogger("ASC.Notify");
private static readonly ILog logMessages = LogManager.GetLogger("ASC.Notify.Messages");
private readonly ILog log;
private readonly ILog logMessages;
private readonly Context context;
private readonly bool logOnly;
public DispatchEngine(Context context)
{
public DispatchEngine(Context context, IConfiguration configuration, IOptionsMonitor<ILog> options)
{
log = options.Get("ASC.Notify");
logMessages = options.Get("ASC.Notify.Messages");
this.context = context ?? throw new ArgumentNullException("context");
logOnly = "log".Equals(ConfigurationManager.AppSettings["core:notify:postman"], StringComparison.InvariantCultureIgnoreCase);
logOnly = "log".Equals(configuration["core:notify:postman"], StringComparison.InvariantCultureIgnoreCase);
log.DebugFormat("LogOnly: {0}", logOnly);
}
@ -67,8 +70,8 @@ namespace ASC.Notify.Engine
}
LogMessage(message, senderName);
return response;
}
}
private void LogResponce(INoticeMessage message, SendResponse response, string senderName)
{
var logmsg = string.Format("[{0}] sended to [{1}] over {2}, status: {3} ", message.Subject, message.Recipient, senderName, response.Result);
@ -84,8 +87,8 @@ namespace ASC.Notify.Engine
{
log.Debug(logmsg);
}
}
}
private void LogMessage(INoticeMessage message, string senderName)
{
try

View File

@ -24,16 +24,17 @@
*/
using System;
using System;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Notify.Engine
{
interface INotifyEngine
{
void QueueRequest(NotifyRequest request);
void QueueRequest(NotifyRequest request, IServiceScope serviceScope);
event Action<NotifyEngine, NotifyRequest> AfterTransferRequest;
event Action<NotifyEngine, NotifyRequest, IServiceScope> AfterTransferRequest;
event Action<NotifyEngine, NotifyRequest> BeforeTransferRequest;
event Action<NotifyEngine, NotifyRequest, IServiceScope> BeforeTransferRequest;
}
}

View File

@ -31,19 +31,19 @@ using System.Threading;
using System.Threading.Tasks;
using ASC.Common.Logging;
using ASC.Common.Notify.Patterns;
using ASC.Core;
using ASC.Core.Tenants;
using ASC.Notify.Channels;
using ASC.Notify.Cron;
using ASC.Notify.Messages;
using ASC.Notify.Patterns;
using ASC.Notify.Recipients;
using ASC.Notify.Recipients;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace ASC.Notify.Engine
{
public class NotifyEngine : INotifyEngine
{
private static readonly ILog log = LogManager.GetLogger("ASC.Notify");
private readonly ILog log;
private readonly Context context;
@ -65,24 +65,26 @@ namespace ASC.Notify.Engine
private readonly TimeSpan defaultSleep = TimeSpan.FromSeconds(10);
public IServiceProvider ServiceProvider { get; }
public event Action<NotifyEngine, NotifyRequest, IServiceScope> BeforeTransferRequest;
public event Action<NotifyEngine, NotifyRequest, IServiceScope> AfterTransferRequest;
public event Action<NotifyEngine, NotifyRequest> BeforeTransferRequest;
public event Action<NotifyEngine, NotifyRequest> AfterTransferRequest;
public NotifyEngine(Context context)
{
public NotifyEngine(Context context, IServiceProvider serviceProvider)
{
this.context = context ?? throw new ArgumentNullException("context");
log = serviceProvider.GetService<IOptionsMonitor<ILog>>().Get("ASC.Notify");
ServiceProvider = serviceProvider;
notifyScheduler = new Thread(NotifyScheduler) { IsBackground = true, Name = "NotifyScheduler" };
notifySender = new Thread(NotifySender) { IsBackground = true, Name = "NotifySender" };
}
public virtual void QueueRequest(NotifyRequest request)
{
BeforeTransferRequest?.Invoke(this, request);
public virtual void QueueRequest(NotifyRequest request, IServiceScope serviceScope)
{
BeforeTransferRequest?.Invoke(this, request, serviceScope);
lock (requests)
{
if (!notifySender.IsAlive)
@ -101,7 +103,7 @@ namespace ASC.Notify.Engine
if (method == null) throw new ArgumentNullException("method");
if (string.IsNullOrEmpty(cron)) throw new ArgumentNullException("cron");
var w = new SendMethodWrapper(method, cron);
var w = new SendMethodWrapper(method, cron, log);
lock (sendMethods)
{
if (!notifyScheduler.IsAlive)
@ -121,7 +123,7 @@ namespace ASC.Notify.Engine
lock (sendMethods)
{
sendMethods.Remove(new SendMethodWrapper(method, null));
sendMethods.Remove(new SendMethodWrapper(method, null, log));
}
}
@ -207,11 +209,12 @@ namespace ASC.Notify.Engine
}
}
if (request != null)
{
AfterTransferRequest?.Invoke(this, request);
{
using var scope = ServiceProvider.CreateScope();
AfterTransferRequest?.Invoke(this, request, scope);
try
{
SendNotify(CoreContext.TenantManager.GetCurrentTenant(), request);
SendNotify(request, scope);
}
catch (Exception e)
{
@ -235,18 +238,18 @@ namespace ASC.Notify.Engine
}
private NotifyResult SendNotify(Tenant tenant, NotifyRequest request)
private NotifyResult SendNotify(NotifyRequest request, IServiceScope serviceScope)
{
var sendResponces = new List<SendResponse>();
var response = CheckPreventInterceptors(request, InterceptorPlace.Prepare, null);
var response = CheckPreventInterceptors(request, InterceptorPlace.Prepare, serviceScope, null);
if (response != null)
{
sendResponces.Add(response);
}
else
{
sendResponces.AddRange(SendGroupNotify(tenant, request));
sendResponces.AddRange(SendGroupNotify(request, serviceScope));
}
NotifyResult result = null;
@ -262,19 +265,19 @@ namespace ASC.Notify.Engine
return result;
}
private SendResponse CheckPreventInterceptors(NotifyRequest request, InterceptorPlace place, string sender)
private SendResponse CheckPreventInterceptors(NotifyRequest request, InterceptorPlace place, IServiceScope serviceScope, string sender)
{
return request.Intercept(place) ? new SendResponse(request.NotifyAction, sender, request.Recipient, SendResult.Prevented) : null;
return request.Intercept(place, serviceScope) ? new SendResponse(request.NotifyAction, sender, request.Recipient, SendResult.Prevented) : null;
}
private List<SendResponse> SendGroupNotify(Tenant tenant, NotifyRequest request)
private List<SendResponse> SendGroupNotify(NotifyRequest request, IServiceScope serviceScope)
{
var responces = new List<SendResponse>();
SendGroupNotify(tenant, request, responces);
SendGroupNotify(request, responces, serviceScope);
return responces;
}
private void SendGroupNotify(Tenant tenant, NotifyRequest request, List<SendResponse> responces)
private void SendGroupNotify(NotifyRequest request, List<SendResponse> responces, IServiceScope serviceScope)
{
if (request.Recipient is IDirectRecipient)
{
@ -284,7 +287,7 @@ namespace ASC.Notify.Engine
var directresponses = new List<SendResponse>(1);
try
{
directresponses = SendDirectNotify(tenant, request);
directresponses = SendDirectNotify(request, serviceScope);
}
catch (Exception exc)
{
@ -297,7 +300,7 @@ namespace ASC.Notify.Engine
{
if (request.Recipient is IRecipientsGroup)
{
var checkresp = CheckPreventInterceptors(request, InterceptorPlace.GroupSend, null);
var checkresp = CheckPreventInterceptors(request, InterceptorPlace.GroupSend, serviceScope, null);
if (checkresp != null)
{
responces.Add(checkresp);
@ -308,13 +311,13 @@ namespace ASC.Notify.Engine
try
{
var recipients = recipientProvider.GetGroupEntries(tenant, request.Recipient as IRecipientsGroup) ?? new IRecipient[0];
var recipients = recipientProvider.GetGroupEntries(request.Recipient as IRecipientsGroup) ?? new IRecipient[0];
foreach (var recipient in recipients)
{
try
{
var newRequest = request.Split(recipient);
SendGroupNotify(tenant, newRequest, responces);
SendGroupNotify(newRequest, responces, serviceScope);
}
catch (Exception exc)
{
@ -339,12 +342,12 @@ namespace ASC.Notify.Engine
}
}
private List<SendResponse> SendDirectNotify(Tenant tenant, NotifyRequest request)
private List<SendResponse> SendDirectNotify(NotifyRequest request, IServiceScope serviceScope)
{
if (!(request.Recipient is IDirectRecipient)) throw new ArgumentException("request.Recipient not IDirectRecipient", "request");
var responses = new List<SendResponse>();
var response = CheckPreventInterceptors(request, InterceptorPlace.DirectSend, null);
var response = CheckPreventInterceptors(request, InterceptorPlace.DirectSend, serviceScope, null);
if (response != null)
{
responses.Add(response);
@ -353,7 +356,7 @@ namespace ASC.Notify.Engine
try
{
PrepareRequestFillSenders(tenant, request);
PrepareRequestFillSenders(request);
PrepareRequestFillPatterns(request);
PrepareRequestFillTags(request);
}
@ -371,7 +374,7 @@ namespace ASC.Notify.Engine
{
try
{
response = SendDirectNotify(tenant.TenantId, request, channel);
response = SendDirectNotify(request, channel, serviceScope);
}
catch (Exception exc)
{
@ -393,17 +396,17 @@ namespace ASC.Notify.Engine
return responses;
}
private SendResponse SendDirectNotify(int tenantId, NotifyRequest request, ISenderChannel channel)
{
private SendResponse SendDirectNotify(NotifyRequest request, ISenderChannel channel, IServiceScope serviceScope)
{
if (!(request.Recipient is IDirectRecipient)) throw new ArgumentException("request.Recipient not IDirectRecipient", "request");
request.CurrentSender = channel.SenderName;
var oops = CreateNoticeMessageFromNotifyRequest(tenantId, request, channel.SenderName, out var noticeMessage);
var oops = CreateNoticeMessageFromNotifyRequest(request, channel.SenderName, serviceScope, out var noticeMessage);
if (oops != null) return oops;
request.CurrentMessage = noticeMessage;
var preventresponse = CheckPreventInterceptors(request, InterceptorPlace.MessageSend, channel.SenderName);
var preventresponse = CheckPreventInterceptors(request, InterceptorPlace.MessageSend, serviceScope, channel.SenderName);
if (preventresponse != null) return preventresponse;
channel.SendAsync(noticeMessage);
@ -411,7 +414,7 @@ namespace ASC.Notify.Engine
return new SendResponse(noticeMessage, channel.SenderName, SendResult.Inprogress);
}
private SendResponse CreateNoticeMessageFromNotifyRequest(int tenantId, NotifyRequest request, string sender, out NoticeMessage noticeMessage)
private SendResponse CreateNoticeMessageFromNotifyRequest(NotifyRequest request, string sender, IServiceScope serviceScope, out NoticeMessage noticeMessage)
{
if (request == null) throw new ArgumentNullException("request");
@ -421,11 +424,11 @@ namespace ASC.Notify.Engine
var addresses = recipient.Addresses;
if (addresses == null || !addresses.Any())
{
addresses = recipientProvider.GetRecipientAddresses(tenantId, request.Recipient as IDirectRecipient, sender);
addresses = recipientProvider.GetRecipientAddresses(request.Recipient as IDirectRecipient, sender);
recipient = new DirectRecipient(request.Recipient.ID, request.Recipient.Name, addresses);
}
recipient = recipientProvider.FilterRecipientAddresses(tenantId, recipient);
recipient = recipientProvider.FilterRecipientAddresses(recipient);
noticeMessage = request.CreateMessage(recipient);
addresses = recipient.Addresses;
@ -465,7 +468,7 @@ namespace ASC.Notify.Engine
if (!string.IsNullOrEmpty(pattern.Styler))
{
//We need to run through styler before templating
StyleMessage(noticeMessage);
StyleMessage(serviceScope, noticeMessage);
}
}
catch (Exception exc)
@ -475,13 +478,13 @@ namespace ASC.Notify.Engine
return null;
}
private void StyleMessage(NoticeMessage message)
private void StyleMessage(IServiceScope scope, NoticeMessage message)
{
try
{
if (!stylers.ContainsKey(message.Pattern.Styler))
{
if (Activator.CreateInstance(Type.GetType(message.Pattern.Styler, true)) is IPatternStyler styler)
if (scope.ServiceProvider.GetService(Type.GetType(message.Pattern.Styler, true)) is IPatternStyler styler)
{
stylers.Add(message.Pattern.Styler, styler);
}
@ -494,14 +497,14 @@ namespace ASC.Notify.Engine
}
}
private void PrepareRequestFillSenders(Tenant tenant, NotifyRequest request)
private void PrepareRequestFillSenders(NotifyRequest request)
{
if (request.SenderNames == null)
{
var subscriptionProvider = request.NotifySource.GetSubscriptionProvider();
var senderNames = new List<string>();
senderNames.AddRange(subscriptionProvider.GetSubscriptionMethod(tenant, request.NotifyAction, request.Recipient) ?? new string[0]);
senderNames.AddRange(subscriptionProvider.GetSubscriptionMethod(request.NotifyAction, request.Recipient) ?? new string[0]);
senderNames.AddRange(request.Arguments.OfType<AdditionalSenderTag>().Select(tag => (string)tag.Value));
request.SenderNames = senderNames.ToArray();
@ -527,8 +530,8 @@ namespace ASC.Notify.Engine
if (pattern == null)
{
pattern = apProvider.GetPattern(request.NotifyAction, senderName);
}
}
request.Patterns[i] = pattern ?? throw new NotifyException(string.Format("For action \"{0}\" by sender \"{1}\" no one patterns getted.", request.NotifyAction.ID, senderName));
}
}
@ -576,10 +579,12 @@ namespace ASC.Notify.Engine
private readonly Action<DateTime> method;
public DateTime? ScheduleDate { get; private set; }
public ILog Log { get; }
public SendMethodWrapper(Action<DateTime> method, string cron)
public SendMethodWrapper(Action<DateTime> method, string cron, ILog log)
{
this.method = method;
Log = log;
if (!string.IsNullOrEmpty(cron))
{
this.cronExpression = new CronExpression(cron);
@ -598,24 +603,24 @@ namespace ASC.Notify.Engine
}
catch (Exception e)
{
log.Error(e);
Log.Error(e);
}
}
public void InvokeSendMethod(DateTime d)
{
lock (locker)
{
Task.Run(() =>
{
try
{
method(d);
}
catch (Exception e)
{
log.Error(e);
}
{
Task.Run(() =>
{
try
{
method(d);
}
catch (Exception e)
{
Log.Error(e);
}
}).Wait();
}
}

View File

@ -26,12 +26,14 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Generic;
using ASC.Common.Logging;
using ASC.Notify.Messages;
using ASC.Notify.Model;
using ASC.Notify.Patterns;
using ASC.Notify.Recipients;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace ASC.Notify.Engine
{
@ -65,7 +67,7 @@ namespace ASC.Notify.Engine
public NotifyRequest(INotifySource notifySource, INotifyAction action, string objectID, IRecipient recipient)
{
{
Properties = new Hashtable();
Arguments = new List<ITagValue>();
RequaredTags = new List<string>();
@ -79,7 +81,7 @@ namespace ASC.Notify.Engine
IsNeedCheckSubscriptions = true;
}
internal bool Intercept(InterceptorPlace place)
internal bool Intercept(InterceptorPlace place, IServiceScope serviceScope)
{
var result = false;
foreach (var interceptor in Interceptors)
@ -88,14 +90,14 @@ namespace ASC.Notify.Engine
{
try
{
if (interceptor.PreventSend(this, place))
if (interceptor.PreventSend(this, place, serviceScope))
{
result = true;
}
}
catch (Exception err)
{
LogManager.GetLogger("ASC.Notify").ErrorFormat("{0} {1} {2}: {3}", interceptor.Name, NotifyAction, Recipient, err);
serviceScope.ServiceProvider.GetService<IOptionsMonitor<ILog>>().Get("ASC.Notify").ErrorFormat("{0} {1} {2}: {3}", interceptor.Name, NotifyAction, Recipient, err);
}
}
}
@ -122,14 +124,14 @@ namespace ASC.Notify.Engine
internal NotifyRequest Split(IRecipient recipient)
{
if (recipient == null) throw new ArgumentNullException("recipient");
var newRequest = new NotifyRequest(NotifySource, NotifyAction, ObjectID, recipient)
{
SenderNames = SenderNames,
Patterns = Patterns,
Arguments = new List<ITagValue>(Arguments),
RequaredTags = RequaredTags,
CurrentSender = CurrentSender,
CurrentMessage = CurrentMessage
var newRequest = new NotifyRequest(NotifySource, NotifyAction, ObjectID, recipient)
{
SenderNames = SenderNames,
Patterns = Patterns,
Arguments = new List<ITagValue>(Arguments),
RequaredTags = RequaredTags,
CurrentSender = CurrentSender,
CurrentMessage = CurrentMessage
};
newRequest.Interceptors.AddRange(Interceptors);
return newRequest;

View File

@ -24,13 +24,14 @@
*/
using System;
using System;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Notify.Engine
{
public class SendInterceptorSkeleton : ISendInterceptor
{
private readonly Func<NotifyRequest, InterceptorPlace, bool> method;
private readonly Func<NotifyRequest, InterceptorPlace, IServiceScope, bool> method;
public string Name { get; internal set; }
@ -40,7 +41,7 @@ namespace ASC.Notify.Engine
public InterceptorLifetime Lifetime { get; internal set; }
public SendInterceptorSkeleton(string name, InterceptorPlace preventPlace, InterceptorLifetime lifetime, Func<NotifyRequest, InterceptorPlace, bool> sendInterceptor)
public SendInterceptorSkeleton(string name, InterceptorPlace preventPlace, InterceptorLifetime lifetime, Func<NotifyRequest, InterceptorPlace, IServiceScope, bool> sendInterceptor)
{
if (string.IsNullOrEmpty("name")) throw new ArgumentNullException("name");
if (string.IsNullOrEmpty("sendInterceptor")) throw new ArgumentNullException("sendInterceptor");
@ -51,9 +52,9 @@ namespace ASC.Notify.Engine
Lifetime = lifetime;
}
public bool PreventSend(NotifyRequest request, InterceptorPlace place)
public bool PreventSend(NotifyRequest request, InterceptorPlace place, IServiceScope serviceScope)
{
return method(request, place);
return method(request, place, serviceScope);
}
}
}

View File

@ -26,8 +26,9 @@
using System;
using System.Collections.Generic;
using ASC.Notify.Recipients;
using ASC.Notify.Recipients;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Notify.Engine
{
class SingleRecipientInterceptor : ISendInterceptor
@ -49,7 +50,7 @@ namespace ASC.Notify.Engine
Name = name;
}
public bool PreventSend(NotifyRequest request, InterceptorPlace place)
public bool PreventSend(NotifyRequest request, InterceptorPlace place, IServiceScope serviceScope)
{
var sendTo = request.Recipient;
if (!sendedTo.Exists(rec => Equals(rec, sendTo)))

View File

@ -24,7 +24,6 @@
*/
using System;
using ASC.Notify.Model;
using ASC.Notify.Patterns;
using ASC.Notify.Recipients;
@ -51,11 +50,6 @@ namespace ASC.Notify
void SendNoticeToAsync(INotifyAction action, string objectID, IRecipient[] recipients, string[] senderNames, bool checkSubsciption, params ITagValue[] args);
INotifyClient RegisterSendMethod(Action<DateTime> method, string cron);
INotifyClient UnregisterSendMethod(Action<DateTime> method);
void BeginSingleRecipientEvent(string name);
void EndSingleRecipientEvent(string name);

View File

@ -26,8 +26,9 @@
using ASC.Notify.Channels;
using ASC.Notify.Model;
using ASC.Notify.Sinks;
using ASC.Notify.Sinks;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Notify
{
public interface INotifyRegistry
@ -38,6 +39,6 @@ namespace ASC.Notify
ISenderChannel GetSender(string senderName);
INotifyClient RegisterClient(INotifySource source);
INotifyClient RegisterClient(INotifySource source, IServiceScope serviceScope);
}
}

View File

@ -23,21 +23,18 @@
*
*/
using ASC.Core.Tenants;
namespace ASC.Notify.Recipients
{
public interface IRecipientProvider
{
IRecipient GetRecipient(int tenantId, string id);
IRecipient GetRecipient(string id);
IRecipient[] GetGroupEntries(Tenant tenant, IRecipientsGroup group);
IRecipient[] GetGroupEntries(IRecipientsGroup group);
IRecipientsGroup[] GetGroups(Tenant tenant, IRecipient recipient);
IRecipientsGroup[] GetGroups(IRecipient recipient);
string[] GetRecipientAddresses(int tenantId, IDirectRecipient recipient, string senderName);
string[] GetRecipientAddresses(IDirectRecipient recipient, string senderName);
IDirectRecipient FilterRecipientAddresses(int tenantId, IDirectRecipient recipient);
IDirectRecipient FilterRecipientAddresses(IDirectRecipient recipient);
}
}

View File

@ -24,8 +24,9 @@
*/
using ASC.Notify.Engine;
using ASC.Notify.Engine;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Notify
{
public interface ISendInterceptor
@ -36,6 +37,6 @@ namespace ASC.Notify
InterceptorLifetime Lifetime { get; }
bool PreventSend(NotifyRequest request, InterceptorPlace place);
bool PreventSend(NotifyRequest request, InterceptorPlace place, IServiceScope serviceScope);
}
}

View File

@ -34,7 +34,7 @@ namespace ASC.Notify
{
public InitiatorInterceptor(params IRecipient[] initiators)
: base("Sys.InitiatorInterceptor", InterceptorPlace.GroupSend | InterceptorPlace.DirectSend, InterceptorLifetime.Call,
(r, p) => (initiators ?? Enumerable.Empty<IRecipient>()).Any(recipient => r.Recipient.Equals(recipient)))
(r, p, scope) => (initiators ?? Enumerable.Empty<IRecipient>()).Any(recipient => r.Recipient.Equals(recipient)))
{
}
}

Some files were not shown because too many files have changed in this diff Show More