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:
commit
506704c54a
3
.gitignore
vendored
3
.gitignore
vendored
@ -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/
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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 && \
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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")
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
|
@ -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" />
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ using System.Data.Common;
|
||||
|
||||
namespace ASC.Common.Data.AdoProxy
|
||||
{
|
||||
class ProxyContext
|
||||
internal class ProxyContext
|
||||
{
|
||||
private readonly Action<ExecutedEventArgs> executedEvent;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ namespace ASC.Security.Cryptography
|
||||
HashAlg.SHA1 => SHA1.Create(),
|
||||
HashAlg.SHA256 => SHA256.Create(),
|
||||
HashAlg.SHA512 => SHA512.Create(),
|
||||
_ => SHA256.Create(),
|
||||
_ => SHA256.Create()
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
|
@ -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" />
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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")
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -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)));
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 )");
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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)))
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
Loading…
Reference in New Issue
Block a user