Merge branch 'feature/backend-refactor' into feature/asc-elasticsearch-refactor
This commit is contained in:
commit
b67d3cc11c
2
.gitignore
vendored
2
.gitignore
vendored
@ -10,7 +10,7 @@
|
||||
/build/deploy
|
||||
*.log
|
||||
/products/ASC.People/Data/
|
||||
Data/
|
||||
/Data
|
||||
Logs/
|
||||
**/.DS_Store
|
||||
.eslintcache
|
||||
|
@ -1,107 +1,111 @@
|
||||
using SecurityContext = ASC.Core.SecurityContext;
|
||||
|
||||
namespace ASC.Api.Core.Auth
|
||||
namespace ASC.Api.Core.Auth;
|
||||
|
||||
[Scope(Additional = typeof(ConfirmAuthHandlerExtension))]
|
||||
public class ConfirmAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||
{
|
||||
[Scope(Additional = typeof(ConfirmAuthHandlerExtension))]
|
||||
public class ConfirmAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||
private readonly SecurityContext _securityContext;
|
||||
private readonly UserManager _userManager;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
|
||||
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,
|
||||
UserManager userManager,
|
||||
IServiceScopeFactory serviceScopeFactory) :
|
||||
base(options, logger, encoder, clock)
|
||||
{
|
||||
public ConfirmAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
|
||||
_securityContext = securityContext;
|
||||
_userManager = userManager;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
}
|
||||
|
||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
|
||||
var emailValidationKeyHelper = scope.ServiceProvider.GetService<EmailValidationKeyModelHelper>();
|
||||
var emailValidationKeyModel = emailValidationKeyHelper.GetModel();
|
||||
|
||||
if (!emailValidationKeyModel.Type.HasValue)
|
||||
{
|
||||
}
|
||||
public ConfirmAuthHandler(
|
||||
IOptionsMonitor<AuthenticationSchemeOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
ISystemClock clock,
|
||||
SecurityContext securityContext,
|
||||
UserManager userManager,
|
||||
IServiceProvider serviceProvider) :
|
||||
base(options, logger, encoder, clock)
|
||||
{
|
||||
SecurityContext = securityContext;
|
||||
UserManager = userManager;
|
||||
ServiceProvider = serviceProvider;
|
||||
return _securityContext.IsAuthenticated
|
||||
? Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(Context.User, new AuthenticationProperties(), Scheme.Name)))
|
||||
: Task.FromResult(AuthenticateResult.Fail(new AuthenticationException(nameof(HttpStatusCode.Unauthorized))));
|
||||
}
|
||||
|
||||
private SecurityContext SecurityContext { get; }
|
||||
private UserManager UserManager { get; }
|
||||
private IServiceProvider ServiceProvider { get; }
|
||||
|
||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
EmailValidationKeyProvider.ValidationResult checkKeyResult;
|
||||
try
|
||||
{
|
||||
using var scope = ServiceProvider.CreateScope();
|
||||
checkKeyResult = emailValidationKeyHelper.Validate(emailValidationKeyModel);
|
||||
}
|
||||
catch (ArgumentNullException)
|
||||
{
|
||||
checkKeyResult = EmailValidationKeyProvider.ValidationResult.Invalid;
|
||||
}
|
||||
|
||||
var emailValidationKeyHelper = scope.ServiceProvider.GetService<EmailValidationKeyModelHelper>();
|
||||
var emailValidationKeyModel = emailValidationKeyHelper.GetModel();
|
||||
|
||||
if (!emailValidationKeyModel.Type.HasValue)
|
||||
{
|
||||
return SecurityContext.IsAuthenticated
|
||||
? Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(Context.User, new AuthenticationProperties(), Scheme.Name)))
|
||||
: Task.FromResult(AuthenticateResult.Fail(new AuthenticationException(HttpStatusCode.Unauthorized.ToString())));
|
||||
}
|
||||
|
||||
EmailValidationKeyProvider.ValidationResult checkKeyResult;
|
||||
try
|
||||
{
|
||||
checkKeyResult = emailValidationKeyHelper.Validate(emailValidationKeyModel);
|
||||
}
|
||||
catch (ArgumentNullException)
|
||||
{
|
||||
checkKeyResult = EmailValidationKeyProvider.ValidationResult.Invalid;
|
||||
}
|
||||
|
||||
var claims = new List<Claim>()
|
||||
{
|
||||
var claims = new List<Claim>()
|
||||
{
|
||||
new Claim(ClaimTypes.Role, emailValidationKeyModel.Type.ToString())
|
||||
};
|
||||
};
|
||||
|
||||
if (checkKeyResult == EmailValidationKeyProvider.ValidationResult.Ok)
|
||||
if (checkKeyResult == EmailValidationKeyProvider.ValidationResult.Ok)
|
||||
{
|
||||
Guid userId;
|
||||
if (!_securityContext.IsAuthenticated)
|
||||
{
|
||||
Guid userId;
|
||||
if (!SecurityContext.IsAuthenticated)
|
||||
if (emailValidationKeyModel.UiD.HasValue && !emailValidationKeyModel.UiD.Equals(Guid.Empty))
|
||||
{
|
||||
if (emailValidationKeyModel.UiD.HasValue && !emailValidationKeyModel.UiD.Equals(Guid.Empty))
|
||||
{
|
||||
userId = emailValidationKeyModel.UiD.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(emailValidationKeyModel.Type == Web.Studio.Utility.ConfirmType.EmailActivation ||
|
||||
emailValidationKeyModel.Type == Web.Studio.Utility.ConfirmType.EmpInvite ||
|
||||
emailValidationKeyModel.Type == Web.Studio.Utility.ConfirmType.LinkInvite)
|
||||
{
|
||||
userId = ASC.Core.Configuration.Constants.CoreSystem.ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
userId = UserManager.GetUserByEmail(emailValidationKeyModel.Email).ID;
|
||||
}
|
||||
}
|
||||
userId = emailValidationKeyModel.UiD.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
userId = SecurityContext.CurrentAccount.ID;
|
||||
if (emailValidationKeyModel.Type == ConfirmType.EmailActivation
|
||||
|| emailValidationKeyModel.Type == ConfirmType.EmpInvite
|
||||
|| emailValidationKeyModel.Type == ConfirmType.LinkInvite)
|
||||
{
|
||||
userId = ASC.Core.Configuration.Constants.CoreSystem.ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
userId = _userManager.GetUserByEmail(emailValidationKeyModel.Email).ID;
|
||||
}
|
||||
}
|
||||
|
||||
SecurityContext.AuthenticateMeWithoutCookie(userId, claims);
|
||||
}
|
||||
else
|
||||
{
|
||||
userId = _securityContext.CurrentAccount.ID;
|
||||
}
|
||||
|
||||
var result = checkKeyResult switch
|
||||
{
|
||||
EmailValidationKeyProvider.ValidationResult.Ok => AuthenticateResult.Success(new AuthenticationTicket(Context.User, new AuthenticationProperties(), Scheme.Name)),
|
||||
_ => AuthenticateResult.Fail(new AuthenticationException(HttpStatusCode.Unauthorized.ToString()))
|
||||
};
|
||||
|
||||
return Task.FromResult(result);
|
||||
_securityContext.AuthenticateMeWithoutCookie(userId, claims);
|
||||
}
|
||||
}
|
||||
|
||||
public class ConfirmAuthHandlerExtension
|
||||
{
|
||||
public static void Register(DIHelper services)
|
||||
var result = checkKeyResult switch
|
||||
{
|
||||
services.TryAdd<EmailValidationKeyModelHelper>();
|
||||
}
|
||||
EmailValidationKeyProvider.ValidationResult.Ok => AuthenticateResult.Success(new AuthenticationTicket(Context.User, new AuthenticationProperties(), Scheme.Name)),
|
||||
_ => AuthenticateResult.Fail(new AuthenticationException(nameof(HttpStatusCode.Unauthorized)))
|
||||
};
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConfirmAuthHandlerExtension
|
||||
{
|
||||
public static void Register(DIHelper services)
|
||||
{
|
||||
services.TryAdd<EmailValidationKeyModelHelper>();
|
||||
}
|
||||
}
|
@ -1,45 +1,46 @@
|
||||
using SecurityContext = ASC.Core.SecurityContext;
|
||||
|
||||
namespace ASC.Api.Core.Auth
|
||||
namespace ASC.Api.Core.Auth;
|
||||
|
||||
[Scope]
|
||||
public class CookieAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||
{
|
||||
[Scope]
|
||||
public class CookieAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||
private readonly AuthorizationHelper _authorizationHelper;
|
||||
private readonly SecurityContext _securityContext;
|
||||
private readonly CookiesManager _cookiesManager;
|
||||
|
||||
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,
|
||||
AuthorizationHelper authorizationHelper,
|
||||
SecurityContext securityContext,
|
||||
CookiesManager cookiesManager)
|
||||
: this(options, logger, encoder, clock)
|
||||
{
|
||||
private AuthorizationHelper AuthorizationHelper { get; }
|
||||
private SecurityContext SecurityContext { get; }
|
||||
private CookiesManager CookiesManager { get; }
|
||||
|
||||
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,
|
||||
AuthorizationHelper authorizationHelper,
|
||||
SecurityContext securityContext,
|
||||
CookiesManager cookiesManager)
|
||||
: this(options, logger, encoder, clock)
|
||||
{
|
||||
AuthorizationHelper = authorizationHelper;
|
||||
SecurityContext = securityContext;
|
||||
CookiesManager = cookiesManager;
|
||||
}
|
||||
|
||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
var result = AuthorizationHelper.ProcessBasicAuthorization(out _);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
SecurityContext.Logout();
|
||||
CookiesManager.ClearCookies(CookiesType.AuthKey);
|
||||
CookiesManager.ClearCookies(CookiesType.SocketIO);
|
||||
}
|
||||
|
||||
return Task.FromResult(
|
||||
result ?
|
||||
AuthenticateResult.Success(new AuthenticationTicket(Context.User, new AuthenticationProperties(), Scheme.Name)) :
|
||||
AuthenticateResult.Fail(new AuthenticationException(HttpStatusCode.Unauthorized.ToString()))
|
||||
);
|
||||
}
|
||||
_authorizationHelper = authorizationHelper;
|
||||
_securityContext = securityContext;
|
||||
_cookiesManager = cookiesManager;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
var result = _authorizationHelper.ProcessBasicAuthorization(out _);
|
||||
if (!result)
|
||||
{
|
||||
_securityContext.Logout();
|
||||
_cookiesManager.ClearCookies(CookiesType.AuthKey);
|
||||
_cookiesManager.ClearCookies(CookiesType.SocketIO);
|
||||
}
|
||||
|
||||
return Task.FromResult(
|
||||
result ?
|
||||
AuthenticateResult.Success(new AuthenticationTicket(Context.User, new AuthenticationProperties(), Scheme.Name)) :
|
||||
AuthenticateResult.Fail(new AuthenticationException(nameof(HttpStatusCode.Unauthorized))));
|
||||
}
|
||||
}
|
@ -1,25 +1,24 @@
|
||||
namespace ASC.Api.Core.Convention
|
||||
namespace ASC.Api.Core.Convention;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ControllerNameAttribute : Attribute
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ControllerNameAttribute : Attribute
|
||||
{
|
||||
public string Name { get; }
|
||||
public string Name { get; }
|
||||
|
||||
public ControllerNameAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public class ControllerNameAttributeConvention : IControllerModelConvention
|
||||
public ControllerNameAttribute(string name)
|
||||
{
|
||||
public void Apply(ControllerModel controller)
|
||||
{
|
||||
var controllerNameAttribute = controller.Attributes.OfType<ControllerNameAttribute>().SingleOrDefault();
|
||||
if (controllerNameAttribute != null)
|
||||
{
|
||||
controller.ControllerName = controllerNameAttribute.Name;
|
||||
}
|
||||
}
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public class ControllerNameAttributeConvention : IControllerModelConvention
|
||||
{
|
||||
public void Apply(ControllerModel controller)
|
||||
{
|
||||
var controllerNameAttribute = controller.Attributes.OfType<ControllerNameAttribute>().SingleOrDefault();
|
||||
if (controllerNameAttribute != null)
|
||||
{
|
||||
controller.ControllerName = controllerNameAttribute.Name;
|
||||
}
|
||||
}
|
||||
}
|
@ -25,239 +25,245 @@
|
||||
|
||||
using SecurityContext = ASC.Core.SecurityContext;
|
||||
|
||||
namespace ASC.Api.Core
|
||||
namespace ASC.Api.Core;
|
||||
|
||||
[Scope]
|
||||
public class ApiContext : ICloneable
|
||||
{
|
||||
[Scope]
|
||||
public class ApiContext : ICloneable
|
||||
public IHttpContextAccessor HttpContextAccessor { get; set; }
|
||||
public string[] Fields { get; set; }
|
||||
public string[] FilterValues { get; set; }
|
||||
public bool FromCache { get; set; }
|
||||
public Tenant Tenant => _tenant ??= _tenantManager.GetCurrentTenant(HttpContextAccessor?.HttpContext);
|
||||
public long? TotalCount
|
||||
{
|
||||
private static int MaxCount = 1000;
|
||||
public IHttpContextAccessor HttpContextAccessor { get; set; }
|
||||
public Tenant tenant;
|
||||
public Tenant Tenant { get { return tenant ??= TenantManager.GetCurrentTenant(HttpContextAccessor?.HttpContext); } }
|
||||
|
||||
public ApiContext(IHttpContextAccessor httpContextAccessor, SecurityContext securityContext, TenantManager tenantManager)
|
||||
set
|
||||
{
|
||||
SecurityContext = securityContext;
|
||||
TenantManager = tenantManager;
|
||||
HttpContextAccessor = httpContextAccessor;
|
||||
if (httpContextAccessor.HttpContext == null) return;
|
||||
|
||||
Count = MaxCount;
|
||||
var query = HttpContextAccessor.HttpContext.Request.Query;
|
||||
//Try parse values
|
||||
var count = query.GetRequestValue("count");
|
||||
if (!string.IsNullOrEmpty(count) && ulong.TryParse(count, out var countParsed))
|
||||
if (HttpContextAccessor.HttpContext.Items.ContainsKey(nameof(TotalCount)))
|
||||
{
|
||||
//Count specified and valid
|
||||
Count = Math.Min((long)countParsed, MaxCount);
|
||||
HttpContextAccessor.HttpContext.Items[nameof(TotalCount)] = value;
|
||||
}
|
||||
|
||||
var startIndex = query.GetRequestValue("startIndex");
|
||||
if (startIndex != null && long.TryParse(startIndex, out var startIndexParsed))
|
||||
else
|
||||
{
|
||||
StartIndex = Math.Max(0, startIndexParsed);
|
||||
SpecifiedStartIndex = StartIndex;
|
||||
}
|
||||
|
||||
var sortOrder = query.GetRequestValue("sortOrder");
|
||||
if ("descending".Equals(sortOrder))
|
||||
{
|
||||
SortDescending = true;
|
||||
}
|
||||
|
||||
FilterToType = query.GetRequestValue("type");
|
||||
SortBy = query.GetRequestValue("sortBy");
|
||||
FilterBy = query.GetRequestValue("filterBy");
|
||||
FilterOp = query.GetRequestValue("filterOp");
|
||||
FilterValue = query.GetRequestValue("filterValue");
|
||||
FilterValues = query.GetRequestArray("filterValue");
|
||||
Fields = query.GetRequestArray("fields");
|
||||
|
||||
var updatedSince = query.GetRequestValue("updatedSince");
|
||||
if (updatedSince != null)
|
||||
{
|
||||
UpdatedSince = Convert.ToDateTime(updatedSince);
|
||||
}
|
||||
}
|
||||
|
||||
public string[] Fields { get; set; }
|
||||
|
||||
public string[] FilterValues { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Filters responce to specific type from request parameter "type"
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The type name is retrieved from [DataContractAttribute] name
|
||||
/// </remarks>
|
||||
public string FilterToType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets count to get item from collection. Request parameter "count"
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Don't forget to call _context.SetDataPaginated() to prevent SmartList from filtering response if you fetch data from DB with TOP & COUNT
|
||||
/// </remarks>
|
||||
public long Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets start index to get item from collection. Request parameter "startIndex"
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Don't forget to call _context.SetDataPaginated() to prevent SmartList from filtering response if you fetch data from DB with TOP & COUNT
|
||||
/// </remarks>
|
||||
public long StartIndex { get; set; }
|
||||
|
||||
internal long SpecifiedStartIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets field to sort by from request parameter "sortBy"
|
||||
/// </summary>
|
||||
public string SortBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets field to filter from request parameter "filterBy"
|
||||
/// </summary>
|
||||
public string FilterBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets filter operation from request parameter "filterOp"
|
||||
/// can be one of the following:"contains","equals","startsWith","present"
|
||||
/// </summary>
|
||||
public string FilterOp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value to filter from request parameter "filterValue"
|
||||
/// </summary>
|
||||
public string FilterValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sort direction. From request parameter "sortOrder" can be "descending" or "ascending"
|
||||
/// Like ...&sortOrder=descending&...
|
||||
/// </summary>
|
||||
public bool SortDescending { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value to filter from request parameter "updatedSince"
|
||||
/// </summary>
|
||||
public DateTime UpdatedSince { get; set; }
|
||||
|
||||
public bool FromCache { get; set; }
|
||||
|
||||
internal long SpecifiedCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set mark that data is already paginated and additional filtering is not needed
|
||||
/// </summary>
|
||||
public ApiContext SetDataPaginated()
|
||||
{
|
||||
//Count = 0;//We always ask for +1 count so smart list should cut it
|
||||
StartIndex = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApiContext SetDataSorted()
|
||||
{
|
||||
SortBy = string.Empty;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApiContext SetDataFiltered()
|
||||
{
|
||||
FilterBy = string.Empty;
|
||||
FilterOp = string.Empty;
|
||||
FilterValue = string.Empty;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApiContext SetTotalCount(long totalCollectionCount)
|
||||
{
|
||||
TotalCount = totalCollectionCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long? TotalCount
|
||||
{
|
||||
set
|
||||
{
|
||||
if (HttpContextAccessor.HttpContext.Items.ContainsKey(nameof(TotalCount)))
|
||||
{
|
||||
HttpContextAccessor.HttpContext.Items[nameof(TotalCount)] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContextAccessor.HttpContext.Items.Add(nameof(TotalCount), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SecurityContext SecurityContext { get; }
|
||||
private TenantManager TenantManager { get; }
|
||||
|
||||
public ApiContext SetCount(int count)
|
||||
{
|
||||
HttpContextAccessor.HttpContext.Items[nameof(Count)] = count;
|
||||
return this;
|
||||
}
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return MemberwiseClone();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("C:{0},S:{1},So:{2},Sd:{3},Fb;{4},Fo:{5},Fv:{6},Us:{7},Ftt:{8}", Count, StartIndex,
|
||||
SortBy, SortDescending, FilterBy, FilterOp, FilterValue, UpdatedSince.Ticks, FilterToType);
|
||||
}
|
||||
|
||||
public void AuthByClaim()
|
||||
{
|
||||
var id = HttpContextAccessor.HttpContext.User.Claims.FirstOrDefault(r => r.Type == ClaimTypes.Sid);
|
||||
if (Guid.TryParse(id?.Value, out var userId))
|
||||
{
|
||||
SecurityContext.AuthenticateMeWithoutCookie(userId);
|
||||
HttpContextAccessor.HttpContext.Items.Add(nameof(TotalCount), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class QueryExtension
|
||||
{
|
||||
internal static string[] GetRequestArray(this IQueryCollection query, string key)
|
||||
{
|
||||
if (query != null)
|
||||
{
|
||||
var values = query[key + "[]"];
|
||||
if (values.Count > 0)
|
||||
return values;
|
||||
/// <summary>
|
||||
/// Filters responce to specific type from request parameter "type"
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The type name is retrieved from [DataContractAttribute] name
|
||||
/// </remarks>
|
||||
public string FilterToType { get; set; }
|
||||
|
||||
values = query[key];
|
||||
if (values.Count > 0)
|
||||
{
|
||||
if (values.Count == 1) //If it's only one element
|
||||
{
|
||||
//Try split
|
||||
if (!string.IsNullOrEmpty(values[0]))
|
||||
return values[0].Split(',');
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
/// <summary>
|
||||
/// Gets count to get item from collection. Request parameter "count"
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Don't forget to call _context.SetDataPaginated() to prevent SmartList from filtering response if you fetch data from DB with TOP & COUNT
|
||||
/// </remarks>
|
||||
public long Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets start index to get item from collection. Request parameter "startIndex"
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Don't forget to call _context.SetDataPaginated() to prevent SmartList from filtering response if you fetch data from DB with TOP & COUNT
|
||||
/// </remarks>
|
||||
public long StartIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets field to sort by from request parameter "sortBy"
|
||||
/// </summary>
|
||||
public string SortBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets field to filter from request parameter "filterBy"
|
||||
/// </summary>
|
||||
public string FilterBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets filter operation from request parameter "filterOp"
|
||||
/// can be one of the following:"contains","equals","startsWith","present"
|
||||
/// </summary>
|
||||
public string FilterOp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value to filter from request parameter "filterValue"
|
||||
/// </summary>
|
||||
public string FilterValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sort direction. From request parameter "sortOrder" can be "descending" or "ascending"
|
||||
/// Like ...&sortOrder=descending&...
|
||||
/// </summary>
|
||||
public bool SortDescending { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value to filter from request parameter "updatedSince"
|
||||
/// </summary>
|
||||
public DateTime UpdatedSince { get; set; }
|
||||
|
||||
internal long SpecifiedCount { get; private set; }
|
||||
internal long SpecifiedStartIndex { get; set; }
|
||||
|
||||
private Tenant _tenant;
|
||||
private static int _maxCount = 1000;
|
||||
private readonly SecurityContext _securityContext;
|
||||
private readonly TenantManager _tenantManager;
|
||||
|
||||
public ApiContext(IHttpContextAccessor httpContextAccessor, SecurityContext securityContext, TenantManager tenantManager)
|
||||
{
|
||||
_securityContext = securityContext;
|
||||
_tenantManager = tenantManager;
|
||||
HttpContextAccessor = httpContextAccessor;
|
||||
if (httpContextAccessor.HttpContext == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public static string GetRequestValue(this IQueryCollection query, string key)
|
||||
Count = _maxCount;
|
||||
var query = HttpContextAccessor.HttpContext.Request.Query;
|
||||
//Try parse values
|
||||
var count = query.GetRequestValue("count");
|
||||
if (!string.IsNullOrEmpty(count) && ulong.TryParse(count, out var countParsed))
|
||||
{
|
||||
var reqArray = query.GetRequestArray(key);
|
||||
return reqArray?.FirstOrDefault();
|
||||
//Count specified and valid
|
||||
Count = Math.Min((long)countParsed, _maxCount);
|
||||
}
|
||||
|
||||
var startIndex = query.GetRequestValue("startIndex");
|
||||
if (startIndex != null && long.TryParse(startIndex, out var startIndexParsed))
|
||||
{
|
||||
StartIndex = Math.Max(0, startIndexParsed);
|
||||
SpecifiedStartIndex = StartIndex;
|
||||
}
|
||||
|
||||
var sortOrder = query.GetRequestValue("sortOrder");
|
||||
if ("descending".Equals(sortOrder))
|
||||
{
|
||||
SortDescending = true;
|
||||
}
|
||||
|
||||
FilterToType = query.GetRequestValue("type");
|
||||
SortBy = query.GetRequestValue("sortBy");
|
||||
FilterBy = query.GetRequestValue("filterBy");
|
||||
FilterOp = query.GetRequestValue("filterOp");
|
||||
FilterValue = query.GetRequestValue("filterValue");
|
||||
FilterValues = query.GetRequestArray("filterValue");
|
||||
Fields = query.GetRequestArray("fields");
|
||||
|
||||
var updatedSince = query.GetRequestValue("updatedSince");
|
||||
if (updatedSince != null)
|
||||
{
|
||||
UpdatedSince = Convert.ToDateTime(updatedSince);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ApiContextExtension
|
||||
/// <summary>
|
||||
/// Set mark that data is already paginated and additional filtering is not needed
|
||||
/// </summary>
|
||||
public ApiContext SetDataPaginated()
|
||||
{
|
||||
public static bool Check(this ApiContext context, string field)
|
||||
//Count = 0;//We always ask for +1 count so smart list should cut it
|
||||
StartIndex = 0;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApiContext SetDataSorted()
|
||||
{
|
||||
SortBy = string.Empty;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApiContext SetDataFiltered()
|
||||
{
|
||||
FilterBy = string.Empty;
|
||||
FilterOp = string.Empty;
|
||||
FilterValue = string.Empty;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApiContext SetTotalCount(long totalCollectionCount)
|
||||
{
|
||||
TotalCount = totalCollectionCount;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApiContext SetCount(int count)
|
||||
{
|
||||
HttpContextAccessor.HttpContext.Items[nameof(Count)] = count;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return MemberwiseClone();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("C:{0},S:{1},So:{2},Sd:{3},Fb;{4},Fo:{5},Fv:{6},Us:{7},Ftt:{8}", Count, StartIndex,
|
||||
SortBy, SortDescending, FilterBy, FilterOp, FilterValue, UpdatedSince.Ticks, FilterToType);
|
||||
}
|
||||
|
||||
public void AuthByClaim()
|
||||
{
|
||||
var id = HttpContextAccessor.HttpContext.User.Claims.FirstOrDefault(r => r.Type == ClaimTypes.Sid);
|
||||
if (Guid.TryParse(id?.Value, out var userId))
|
||||
{
|
||||
return context?.Fields == null || (context.Fields != null && context.Fields.Contains(field, StringComparer.InvariantCultureIgnoreCase));
|
||||
_securityContext.AuthenticateMeWithoutCookie(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class QueryExtension
|
||||
{
|
||||
internal static string[] GetRequestArray(this IQueryCollection query, string key)
|
||||
{
|
||||
if (query != null)
|
||||
{
|
||||
var values = query[key + "[]"];
|
||||
if (values.Count > 0) return values;
|
||||
|
||||
values = query[key];
|
||||
if (values.Count > 0)
|
||||
{
|
||||
if (values.Count == 1) //If it's only one element
|
||||
{
|
||||
//Try split
|
||||
if (!string.IsNullOrEmpty(values[0]))
|
||||
return values[0].Split(',');
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetRequestValue(this IQueryCollection query, string key)
|
||||
{
|
||||
var reqArray = query.GetRequestArray(key);
|
||||
|
||||
return reqArray?.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ApiContextExtension
|
||||
{
|
||||
public static bool Check(this ApiContext context, string field)
|
||||
{
|
||||
return context?.Fields == null
|
||||
|| (context.Fields != null
|
||||
&& context.Fields.Contains(field, StringComparer.InvariantCultureIgnoreCase));
|
||||
}
|
||||
}
|
@ -23,335 +23,384 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Api.Core
|
||||
namespace ASC.Api.Core;
|
||||
|
||||
[TypeConverter(typeof(ApiDateTimeTypeConverter))]
|
||||
public sealed class ApiDateTime : IComparable<ApiDateTime>, IComparable
|
||||
{
|
||||
[TypeConverter(typeof(ApiDateTimeTypeConverter))]
|
||||
public class ApiDateTime : IComparable<ApiDateTime>, IComparable
|
||||
public DateTime UtcTime { get; private set; }
|
||||
public TimeSpan TimeZoneOffset { get; private set; }
|
||||
|
||||
internal static readonly string[] Formats = new[]
|
||||
{
|
||||
internal static readonly string[] Formats = new[]
|
||||
{
|
||||
"o",
|
||||
"yyyy'-'MM'-'dd'T'HH'-'mm'-'ss'.'fffffffK",
|
||||
"yyyy'-'MM'-'dd'T'HH'-'mm'-'ss'.'fffK",
|
||||
"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK",
|
||||
"yyyy'-'MM'-'dd'T'HH'-'mm'-'ssK",
|
||||
"yyyy'-'MM'-'dd'T'HH':'mm':'ssK",
|
||||
"yyyy'-'MM'-'dd"
|
||||
};
|
||||
"o",
|
||||
"yyyy'-'MM'-'dd'T'HH'-'mm'-'ss'.'fffffffK",
|
||||
"yyyy'-'MM'-'dd'T'HH'-'mm'-'ss'.'fffK",
|
||||
"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK",
|
||||
"yyyy'-'MM'-'dd'T'HH'-'mm'-'ssK",
|
||||
"yyyy'-'MM'-'dd'T'HH':'mm':'ssK",
|
||||
"yyyy'-'MM'-'dd"
|
||||
};
|
||||
|
||||
public ApiDateTime()
|
||||
: this(null, null, null)
|
||||
private readonly TenantManager _tenantManager;
|
||||
private readonly TimeZoneConverter _timeZoneConverter;
|
||||
|
||||
public ApiDateTime() : this(null, null, null) { }
|
||||
|
||||
public ApiDateTime(
|
||||
TenantManager tenantManager,
|
||||
TimeZoneConverter timeZoneConverter,
|
||||
DateTime? dateTime)
|
||||
: this(tenantManager, dateTime, null, timeZoneConverter) { }
|
||||
|
||||
public ApiDateTime(
|
||||
TenantManager tenantManager,
|
||||
DateTime? dateTime,
|
||||
TimeZoneInfo timeZone,
|
||||
TimeZoneConverter timeZoneConverter)
|
||||
{
|
||||
if (dateTime.HasValue && dateTime.Value > DateTime.MinValue && dateTime.Value < DateTime.MaxValue)
|
||||
{
|
||||
_tenantManager = tenantManager;
|
||||
_timeZoneConverter = timeZoneConverter;
|
||||
SetDate(dateTime.Value, timeZone);
|
||||
}
|
||||
|
||||
public ApiDateTime(TenantManager tenantManager, TimeZoneConverter timeZoneConverter, DateTime? dateTime)
|
||||
: this(tenantManager, dateTime, null, timeZoneConverter)
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
public ApiDateTime(TenantManager tenantManager, DateTime? dateTime, TimeZoneInfo timeZone, TimeZoneConverter timeZoneConverter)
|
||||
{
|
||||
if (dateTime.HasValue && dateTime.Value > DateTime.MinValue && dateTime.Value < DateTime.MaxValue)
|
||||
{
|
||||
TenantManager = tenantManager;
|
||||
TimeZoneConverter = timeZoneConverter;
|
||||
SetDate(dateTime.Value, timeZone);
|
||||
}
|
||||
else
|
||||
{
|
||||
UtcTime = DateTime.MinValue;
|
||||
TimeZoneOffset = TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
public ApiDateTime(DateTime utcTime, TimeSpan offset)
|
||||
{
|
||||
UtcTime = new DateTime(utcTime.Ticks, DateTimeKind.Utc);
|
||||
TimeZoneOffset = offset;
|
||||
}
|
||||
|
||||
public static ApiDateTime Parse(string data, TenantManager tenantManager, TimeZoneConverter timeZoneConverter)
|
||||
{
|
||||
return Parse(data, null, tenantManager, timeZoneConverter);
|
||||
}
|
||||
|
||||
public static ApiDateTime Parse(string data, TimeZoneInfo tz, TenantManager tenantManager, TimeZoneConverter timeZoneConverter)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data)) throw new ArgumentNullException("data");
|
||||
|
||||
if (data.Length < 7) throw new ArgumentException("invalid date time format");
|
||||
|
||||
var offsetPart = data.Substring(data.Length - 6, 6);
|
||||
if (DateTime.TryParseExact(data, Formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var dateTime))
|
||||
{
|
||||
//Parse time
|
||||
var tzOffset = TimeSpan.Zero;
|
||||
if (offsetPart.Contains(":") && TimeSpan.TryParse(offsetPart.TrimStart('+'), out tzOffset))
|
||||
{
|
||||
return new ApiDateTime(dateTime, tzOffset);
|
||||
}
|
||||
if (!data.EndsWith("Z", true, CultureInfo.InvariantCulture))
|
||||
{
|
||||
if (tz == null)
|
||||
{
|
||||
tz = GetTimeZoneInfo(tenantManager, timeZoneConverter);
|
||||
}
|
||||
tzOffset = tz.GetUtcOffset(dateTime);
|
||||
dateTime = dateTime.Subtract(tzOffset);
|
||||
}
|
||||
return new ApiDateTime(dateTime, tzOffset);
|
||||
|
||||
}
|
||||
throw new ArgumentException("invalid date time format: " + data);
|
||||
}
|
||||
|
||||
|
||||
private void SetDate(DateTime value, TimeZoneInfo timeZone)
|
||||
{
|
||||
TimeZoneOffset = TimeSpan.Zero;
|
||||
UtcTime = DateTime.MinValue;
|
||||
|
||||
if (timeZone == null)
|
||||
{
|
||||
timeZone = GetTimeZoneInfo(TenantManager, TimeZoneConverter);
|
||||
}
|
||||
|
||||
//Hack
|
||||
if (timeZone.IsInvalidTime(new DateTime(value.Ticks, DateTimeKind.Unspecified)))
|
||||
{
|
||||
value = value.AddHours(1);
|
||||
}
|
||||
|
||||
if (value.Kind == DateTimeKind.Local)
|
||||
{
|
||||
value = TimeZoneInfo.ConvertTimeToUtc(new DateTime(value.Ticks, DateTimeKind.Unspecified), timeZone);
|
||||
}
|
||||
if (value.Kind == DateTimeKind.Unspecified)
|
||||
{
|
||||
//Assume it's utc
|
||||
value = new DateTime(value.Ticks, DateTimeKind.Utc);
|
||||
}
|
||||
|
||||
if (value.Kind == DateTimeKind.Utc)
|
||||
{
|
||||
UtcTime = value; //Set UTC time
|
||||
TimeZoneOffset = timeZone.GetUtcOffset(value);
|
||||
}
|
||||
|
||||
TimeZoneOffset = TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
private static TimeZoneInfo GetTimeZoneInfo(TenantManager tenantManager, TimeZoneConverter timeZoneConverter)
|
||||
public ApiDateTime(DateTime utcTime, TimeSpan offset)
|
||||
{
|
||||
UtcTime = new DateTime(utcTime.Ticks, DateTimeKind.Utc);
|
||||
TimeZoneOffset = offset;
|
||||
}
|
||||
|
||||
public static ApiDateTime Parse(string data, TenantManager tenantManager, TimeZoneConverter timeZoneConverter)
|
||||
{
|
||||
return Parse(data, null, tenantManager, timeZoneConverter);
|
||||
}
|
||||
|
||||
public static ApiDateTime Parse(string data, TimeZoneInfo tz, TenantManager tenantManager, TimeZoneConverter timeZoneConverter)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data)) throw new ArgumentNullException(nameof(data));
|
||||
|
||||
var offsetPart = data.Substring(data.Length - 6, 6);
|
||||
if (DateTime.TryParseExact(data, Formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var dateTime))
|
||||
{
|
||||
var timeZone = TimeZoneInfo.Local;
|
||||
try
|
||||
//Parse time
|
||||
var tzOffset = TimeSpan.Zero;
|
||||
if (offsetPart.Contains(':') && TimeSpan.TryParse(offsetPart.TrimStart('+'), out tzOffset))
|
||||
{
|
||||
timeZone = timeZoneConverter.GetTimeZone(tenantManager.GetCurrentTenant().TimeZone);
|
||||
return new ApiDateTime(dateTime, tzOffset);
|
||||
}
|
||||
catch (Exception)
|
||||
|
||||
if (!data.EndsWith("Z", true, CultureInfo.InvariantCulture))
|
||||
{
|
||||
//Tenant failed
|
||||
if (tz == null)
|
||||
{
|
||||
tz = GetTimeZoneInfo(tenantManager, timeZoneConverter);
|
||||
}
|
||||
|
||||
tzOffset = tz.GetUtcOffset(dateTime);
|
||||
dateTime = dateTime.Subtract(tzOffset);
|
||||
}
|
||||
return timeZone;
|
||||
|
||||
return new ApiDateTime(dateTime, tzOffset);
|
||||
}
|
||||
|
||||
private string ToRoundTripString(DateTime date, TimeSpan offset)
|
||||
throw new ArgumentException("invalid date time format: " + data);
|
||||
}
|
||||
|
||||
|
||||
private void SetDate(DateTime value, TimeZoneInfo timeZone)
|
||||
{
|
||||
TimeZoneOffset = TimeSpan.Zero;
|
||||
UtcTime = DateTime.MinValue;
|
||||
|
||||
if (timeZone == null)
|
||||
{
|
||||
var dateString = date.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffff", CultureInfo.InvariantCulture);
|
||||
var offsetString = offset.Ticks == 0 ? "Z" : ((offset < TimeSpan.Zero) ? "-" : "+") + offset.ToString("hh\\:mm", CultureInfo.InvariantCulture);
|
||||
return dateString + offsetString;
|
||||
timeZone = GetTimeZoneInfo(_tenantManager, _timeZoneConverter);
|
||||
}
|
||||
|
||||
public static ApiDateTime FromDate(TenantManager tenantManager, TimeZoneConverter timeZoneConverter, DateTime d)
|
||||
//Hack
|
||||
if (timeZone.IsInvalidTime(new DateTime(value.Ticks, DateTimeKind.Unspecified)))
|
||||
{
|
||||
value = value.AddHours(1);
|
||||
}
|
||||
|
||||
if (value.Kind == DateTimeKind.Local)
|
||||
{
|
||||
value = TimeZoneInfo.ConvertTimeToUtc(new DateTime(value.Ticks, DateTimeKind.Unspecified), timeZone);
|
||||
}
|
||||
|
||||
if (value.Kind == DateTimeKind.Unspecified)
|
||||
{
|
||||
value = new DateTime(value.Ticks, DateTimeKind.Utc); //Assume it's utc
|
||||
}
|
||||
|
||||
if (value.Kind == DateTimeKind.Utc)
|
||||
{
|
||||
UtcTime = value; //Set UTC time
|
||||
TimeZoneOffset = timeZone.GetUtcOffset(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static TimeZoneInfo GetTimeZoneInfo(TenantManager tenantManager, TimeZoneConverter timeZoneConverter)
|
||||
{
|
||||
var timeZone = TimeZoneInfo.Local;
|
||||
try
|
||||
{
|
||||
timeZone = timeZoneConverter.GetTimeZone(tenantManager.GetCurrentTenant().TimeZone);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//Tenant failed
|
||||
}
|
||||
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
private string ToRoundTripString(DateTime date, TimeSpan offset)
|
||||
{
|
||||
var dateString = date.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffff", CultureInfo.InvariantCulture);
|
||||
var offsetString = offset.Ticks == 0
|
||||
? "Z" : ((offset < TimeSpan.Zero)
|
||||
? "-" : "+") + offset.ToString("hh\\:mm", CultureInfo.InvariantCulture);
|
||||
|
||||
return dateString + offsetString;
|
||||
}
|
||||
|
||||
public static ApiDateTime FromDate(TenantManager tenantManager, TimeZoneConverter timeZoneConverter, DateTime d)
|
||||
{
|
||||
var date = new ApiDateTime(tenantManager, timeZoneConverter, d);
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
public static ApiDateTime FromDate(TenantManager tenantManager, TimeZoneConverter timeZoneConverter, DateTime? d)
|
||||
{
|
||||
if (d.HasValue)
|
||||
{
|
||||
var date = new ApiDateTime(tenantManager, timeZoneConverter, d);
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
public static ApiDateTime FromDate(TenantManager tenantManager, TimeZoneConverter timeZoneConverter, DateTime? d)
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool operator >(ApiDateTime left, ApiDateTime right)
|
||||
{
|
||||
if (ReferenceEquals(left, right))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (left == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(ApiDateTime left, ApiDateTime right)
|
||||
{
|
||||
if (ReferenceEquals(left, right))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (left == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(ApiDateTime left, ApiDateTime right)
|
||||
{
|
||||
return !(left >= right);
|
||||
}
|
||||
|
||||
public static bool operator <(ApiDateTime left, ApiDateTime right)
|
||||
{
|
||||
return !(left > right);
|
||||
}
|
||||
|
||||
public static bool operator ==(ApiDateTime left, ApiDateTime right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(ApiDateTime left, ApiDateTime right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public static implicit operator DateTime(ApiDateTime d)
|
||||
{
|
||||
if (d == null)
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
|
||||
return d.UtcTime;
|
||||
}
|
||||
|
||||
public static implicit operator DateTime?(ApiDateTime d)
|
||||
{
|
||||
if (d == null)
|
||||
{
|
||||
if (d.HasValue)
|
||||
{
|
||||
var date = new ApiDateTime(tenantManager, timeZoneConverter, d);
|
||||
return date;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return d.UtcTime;
|
||||
}
|
||||
|
||||
public static bool operator >(ApiDateTime left, ApiDateTime right)
|
||||
public int CompareTo(DateTime other)
|
||||
{
|
||||
return CompareTo(new ApiDateTime(_tenantManager, _timeZoneConverter, other));
|
||||
}
|
||||
|
||||
public int CompareTo(ApiDateTime other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
if (ReferenceEquals(left, right)) return false;
|
||||
if (left == null) return false;
|
||||
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
public static bool operator >=(ApiDateTime left, ApiDateTime right)
|
||||
{
|
||||
if (ReferenceEquals(left, right)) return false;
|
||||
if (left == null) return false;
|
||||
|
||||
return left.CompareTo(right) >= 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static bool operator <=(ApiDateTime left, ApiDateTime right)
|
||||
{
|
||||
return !(left >= right);
|
||||
}
|
||||
return UtcTime.CompareTo(other.UtcTime);
|
||||
}
|
||||
|
||||
public static bool operator <(ApiDateTime left, ApiDateTime right)
|
||||
{
|
||||
return !(left > right);
|
||||
}
|
||||
|
||||
public static bool operator ==(ApiDateTime left, ApiDateTime right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(ApiDateTime left, ApiDateTime right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public static implicit operator DateTime(ApiDateTime d)
|
||||
{
|
||||
if (d == null) return DateTime.MinValue;
|
||||
return d.UtcTime;
|
||||
}
|
||||
|
||||
public static implicit operator DateTime?(ApiDateTime d)
|
||||
{
|
||||
if (d == null) return null;
|
||||
return d.UtcTime;
|
||||
}
|
||||
|
||||
public int CompareTo(DateTime other)
|
||||
{
|
||||
return CompareTo(new ApiDateTime(TenantManager, TimeZoneConverter, other));
|
||||
}
|
||||
|
||||
public int CompareTo(ApiDateTime other)
|
||||
{
|
||||
if (other == null) return 1;
|
||||
return UtcTime.CompareTo(other.UtcTime);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is null) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != typeof(ApiDateTime)) return false;
|
||||
return Equals((ApiDateTime)obj);
|
||||
if (!(obj is ApiDateTime)) return false;
|
||||
return Equals((ApiDateTime)obj);
|
||||
}
|
||||
|
||||
public bool Equals(ApiDateTime other)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Equals(ApiDateTime other)
|
||||
return UtcTime.Equals(other.UtcTime) && TimeZoneOffset.Equals(other.TimeZoneOffset);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
if (other is null) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return UtcTime.Equals(other.UtcTime) && TimeZoneOffset.Equals(other.TimeZoneOffset);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return UtcTime.GetHashCode() * 397 + TimeZoneOffset.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj is DateTime dateTime)
|
||||
return CompareTo(dateTime);
|
||||
return obj is ApiDateTime apiDateTime ? CompareTo(apiDateTime) : 0;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var localUtcTime = UtcTime;
|
||||
|
||||
if (!UtcTime.Equals(DateTime.MinValue))
|
||||
localUtcTime = UtcTime.Add(TimeZoneOffset);
|
||||
|
||||
return ToRoundTripString(localUtcTime, TimeZoneOffset);
|
||||
}
|
||||
|
||||
public DateTime UtcTime { get; private set; }
|
||||
public TimeSpan TimeZoneOffset { get; private set; }
|
||||
private TenantManager TenantManager { get; }
|
||||
private TimeZoneConverter TimeZoneConverter { get; }
|
||||
|
||||
public static ApiDateTime GetSample()
|
||||
{
|
||||
return new ApiDateTime(DateTime.UtcNow, TimeSpan.Zero);
|
||||
return UtcTime.GetHashCode() * 397 + TimeZoneOffset.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public class ApiDateTimeTypeConverter : DateTimeConverter
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
if (obj is DateTime dateTime)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
return value.ToString();
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
return CompareTo(dateTime);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string @string)
|
||||
{
|
||||
return ApiDateTime.Parse(@string, null, null);
|
||||
}
|
||||
if (value is DateTime time)
|
||||
{
|
||||
return new ApiDateTime(null, null, time);
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
return obj is ApiDateTime apiDateTime ? CompareTo(apiDateTime) : 0;
|
||||
}
|
||||
|
||||
public class ApiDateTimeConverter : System.Text.Json.Serialization.JsonConverter<ApiDateTime>
|
||||
public override string ToString()
|
||||
{
|
||||
public override ApiDateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
var localUtcTime = UtcTime;
|
||||
if (!UtcTime.Equals(DateTime.MinValue))
|
||||
{
|
||||
if (reader.TryGetDateTime(out var result))
|
||||
localUtcTime = UtcTime.Add(TimeZoneOffset);
|
||||
}
|
||||
|
||||
return ToRoundTripString(localUtcTime, TimeZoneOffset);
|
||||
}
|
||||
|
||||
public static ApiDateTime GetSample()
|
||||
{
|
||||
return new ApiDateTime(DateTime.UtcNow, TimeSpan.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
public class ApiDateTimeTypeConverter : DateTimeConverter
|
||||
{
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string @string)
|
||||
{
|
||||
return ApiDateTime.Parse(@string, null, null);
|
||||
}
|
||||
if (value is DateTime time)
|
||||
{
|
||||
return new ApiDateTime(null, null, time);
|
||||
}
|
||||
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
}
|
||||
|
||||
public class ApiDateTimeConverter : System.Text.Json.Serialization.JsonConverter<ApiDateTime>
|
||||
{
|
||||
public override ApiDateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TryGetDateTime(out var result))
|
||||
{
|
||||
return new ApiDateTime(result, TimeSpan.Zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DateTime.TryParseExact(reader.GetString(), ApiDateTime.Formats,
|
||||
CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTime))
|
||||
{
|
||||
return new ApiDateTime(result, TimeSpan.Zero);
|
||||
return new ApiDateTime(dateTime, TimeSpan.Zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DateTime.TryParseExact(reader.GetString(), ApiDateTime.Formats, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTime))
|
||||
{
|
||||
return new ApiDateTime(dateTime, TimeSpan.Zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ApiDateTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, ApiDateTime value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
return new ApiDateTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Scope]
|
||||
public class ApiDateTimeHelper
|
||||
public override void Write(Utf8JsonWriter writer, ApiDateTime value, JsonSerializerOptions options)
|
||||
{
|
||||
private TenantManager TenantManager { get; }
|
||||
private TimeZoneConverter TimeZoneConverter { get; }
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public ApiDateTimeHelper(TenantManager tenantManager, TimeZoneConverter timeZoneConverter)
|
||||
{
|
||||
TenantManager = tenantManager;
|
||||
TimeZoneConverter = timeZoneConverter;
|
||||
}
|
||||
[Scope]
|
||||
public class ApiDateTimeHelper
|
||||
{
|
||||
private readonly TenantManager _tenantManager;
|
||||
private readonly TimeZoneConverter _timeZoneConverter;
|
||||
|
||||
public ApiDateTime Get(DateTime? from)
|
||||
{
|
||||
return ApiDateTime.FromDate(TenantManager, TimeZoneConverter, from);
|
||||
}
|
||||
public ApiDateTimeHelper(TenantManager tenantManager, TimeZoneConverter timeZoneConverter)
|
||||
{
|
||||
_tenantManager = tenantManager;
|
||||
_timeZoneConverter = timeZoneConverter;
|
||||
}
|
||||
|
||||
public ApiDateTime Get(DateTime? from)
|
||||
{
|
||||
return ApiDateTime.FromDate(_tenantManager, _timeZoneConverter, from);
|
||||
}
|
||||
}
|
@ -1,24 +1,25 @@
|
||||
using JsonConverter = System.Text.Json.Serialization.JsonConverter;
|
||||
|
||||
namespace ASC.Api.Core
|
||||
namespace ASC.Api.Core;
|
||||
|
||||
public abstract class BaseStartup
|
||||
{
|
||||
public abstract class BaseStartup
|
||||
{
|
||||
public IConfiguration Configuration { get; }
|
||||
public IHostEnvironment HostEnvironment { get; }
|
||||
public virtual JsonConverter[] Converters { get; }
|
||||
public virtual bool AddControllersAsServices { get; } = false;
|
||||
public IConfiguration Configuration { get; }
|
||||
public IHostEnvironment HostEnvironment { get; }
|
||||
public virtual JsonConverter[] Converters { get; }
|
||||
public virtual bool AddControllersAsServices { get; } = false;
|
||||
public virtual bool ConfirmAddScheme { get; } = false;
|
||||
public virtual bool AddAndUseSession { get; } = false;
|
||||
protected DIHelper DIHelper { get; }
|
||||
protected DIHelper DIHelper { get; }
|
||||
protected bool LoadProducts { get; set; } = true;
|
||||
protected bool LoadConsumers { get; } = true;
|
||||
|
||||
public BaseStartup(IConfiguration configuration, IHostEnvironment hostEnvironment)
|
||||
{
|
||||
Configuration = configuration;
|
||||
protected bool LoadConsumers { get; } = true;
|
||||
|
||||
public BaseStartup(IConfiguration configuration, IHostEnvironment hostEnvironment)
|
||||
{
|
||||
Configuration = configuration;
|
||||
HostEnvironment = hostEnvironment;
|
||||
DIHelper = new DIHelper();
|
||||
DIHelper = new DIHelper();
|
||||
|
||||
if (bool.TryParse(Configuration["core:products"], out var loadProducts))
|
||||
{
|
||||
LoadProducts = loadProducts;
|
||||
@ -26,16 +27,19 @@ namespace ASC.Api.Core
|
||||
}
|
||||
|
||||
public virtual void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
{
|
||||
services.AddCustomHealthCheck(Configuration);
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddMemoryCache();
|
||||
services.AddHttpClient();
|
||||
|
||||
if (AddAndUseSession)
|
||||
{
|
||||
services.AddSession();
|
||||
}
|
||||
|
||||
DIHelper.Configure(services);
|
||||
|
||||
|
||||
Action<JsonOptions> jsonOptions = options =>
|
||||
{
|
||||
options.JsonSerializerOptions.WriteIndented = false;
|
||||
@ -43,11 +47,11 @@ namespace ASC.Api.Core
|
||||
options.JsonSerializerOptions.Converters.Add(new ApiDateTimeConverter());
|
||||
|
||||
if (Converters != null)
|
||||
{
|
||||
{
|
||||
foreach (var c in Converters)
|
||||
{
|
||||
options.JsonSerializerOptions.Converters.Add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -72,11 +76,11 @@ namespace ASC.Api.Core
|
||||
|
||||
if (kafkaConfiguration != null)
|
||||
{
|
||||
DIHelper.TryAdd(typeof(ICacheNotify<>), typeof(KafkaCache<>));
|
||||
DIHelper.TryAdd(typeof(ICacheNotify<>), typeof(KafkaCacheNotify<>));
|
||||
}
|
||||
else if (redisConfiguration != null)
|
||||
{
|
||||
DIHelper.TryAdd(typeof(ICacheNotify<>), typeof(RedisCache<>));
|
||||
DIHelper.TryAdd(typeof(ICacheNotify<>), typeof(RedisCacheNotify<>));
|
||||
|
||||
services.AddStackExchangeRedisExtensions<NewtonsoftSerializer>(redisConfiguration);
|
||||
}
|
||||
@ -85,70 +89,71 @@ namespace ASC.Api.Core
|
||||
DIHelper.TryAdd(typeof(ICacheNotify<>), typeof(MemoryCacheNotify<>));
|
||||
}
|
||||
|
||||
|
||||
DIHelper.TryAdd(typeof(IWebhookPublisher), typeof(WebhookPublisher));
|
||||
|
||||
if (LoadProducts)
|
||||
{
|
||||
DIHelper.RegisterProducts(Configuration, HostEnvironment.ContentRootPath);
|
||||
}
|
||||
|
||||
services.AddMvcCore(config =>
|
||||
{
|
||||
config.Conventions.Add(new ControllerNameAttributeConvention());
|
||||
|
||||
var builder = services.AddMvcCore(config =>
|
||||
{
|
||||
config.Conventions.Add(new ControllerNameAttributeConvention());
|
||||
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
|
||||
|
||||
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
|
||||
|
||||
config.Filters.Add(new AuthorizeFilter(policy));
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(TenantStatusFilter)));
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(PaymentFilter)));
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(IpSecurityFilter)));
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(ProductSecurityFilter)));
|
||||
config.Filters.Add(new CustomResponseFilterAttribute());
|
||||
config.Filters.Add(new CustomExceptionFilterAttribute());
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(FormatFilter)));
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(WebhooksGlobalFilterAttribute)));
|
||||
|
||||
config.OutputFormatters.RemoveType<XmlSerializerOutputFormatter>();
|
||||
config.OutputFormatters.Add(new XmlOutputFormatter());
|
||||
config.Filters.Add(new AuthorizeFilter(policy));
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(TenantStatusFilter)));
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(PaymentFilter)));
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(IpSecurityFilter)));
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(ProductSecurityFilter)));
|
||||
config.Filters.Add(new CustomResponseFilterAttribute());
|
||||
config.Filters.Add(new CustomExceptionFilterAttribute());
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(FormatFilter)));
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(WebhooksGlobalFilterAttribute)));
|
||||
|
||||
config.OutputFormatters.RemoveType<XmlSerializerOutputFormatter>();
|
||||
config.OutputFormatters.Add(new XmlOutputFormatter());
|
||||
});
|
||||
|
||||
|
||||
var authBuilder = services.AddAuthentication("cookie")
|
||||
.AddScheme<AuthenticationSchemeOptions, CookieAuthHandler>("cookie", a => { });
|
||||
|
||||
if (ConfirmAddScheme)
|
||||
|
||||
if (ConfirmAddScheme)
|
||||
{
|
||||
authBuilder.AddScheme<AuthenticationSchemeOptions, ConfirmAuthHandler>("confirm", a => { });
|
||||
}
|
||||
|
||||
services.AddAutoMapper(Assembly.GetAssembly(typeof(MappingProfile)));
|
||||
authBuilder.AddScheme<AuthenticationSchemeOptions, ConfirmAuthHandler>("confirm", a => { });
|
||||
}
|
||||
|
||||
services.AddAutoMapper(Assembly.GetAssembly(typeof(MappingProfile)));
|
||||
}
|
||||
|
||||
public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
||||
|
||||
public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
||||
});
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
if (AddAndUseSession)
|
||||
{
|
||||
app.UseSession();
|
||||
|
||||
app.UseAuthentication();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseCultureMiddleware();
|
||||
|
||||
app.UseDisposeMiddleware();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
endpoints.MapCustom();
|
||||
}
|
||||
|
||||
app.UseAuthentication();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseCultureMiddleware();
|
||||
|
||||
app.UseDisposeMiddleware();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
endpoints.MapCustom();
|
||||
|
||||
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
|
||||
{
|
||||
@ -158,25 +163,25 @@ namespace ASC.Api.Core
|
||||
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
|
||||
{
|
||||
Predicate = r => r.Name.Contains("self")
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void ConfigureContainer(ContainerBuilder builder)
|
||||
{
|
||||
builder.Register(Configuration, LoadProducts, LoadConsumers);
|
||||
}
|
||||
}
|
||||
builder.Register(Configuration, LoadProducts, LoadConsumers);
|
||||
}
|
||||
}
|
||||
|
||||
public static class LogNLogConfigureExtenstion
|
||||
{
|
||||
public static class LogNLogConfigureExtenstion
|
||||
{
|
||||
public static IHostBuilder ConfigureNLogLogging(this IHostBuilder hostBuilder)
|
||||
{
|
||||
return hostBuilder.ConfigureLogging((hostBuildexContext, r) =>
|
||||
{
|
||||
_ = new ConfigureLogNLog(hostBuildexContext.Configuration, new ConfigurationExtension(hostBuildexContext.Configuration), hostBuildexContext.HostingEnvironment);
|
||||
_ = new ConfigureLogNLog(hostBuildexContext.Configuration,
|
||||
new ConfigurationExtension(hostBuildexContext.Configuration), hostBuildexContext.HostingEnvironment);
|
||||
r.AddNLog(LogManager.Configuration);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,36 +1,35 @@
|
||||
namespace ASC.Api.Core
|
||||
{
|
||||
public class BaseWorkerStartup
|
||||
{
|
||||
public IConfiguration Configuration { get; }
|
||||
namespace ASC.Api.Core;
|
||||
|
||||
public BaseWorkerStartup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public virtual void ConfigureServices(IServiceCollection services)
|
||||
public class BaseWorkerStartup
|
||||
{
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
public BaseWorkerStartup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public virtual void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddCustomHealthCheck(Configuration);
|
||||
}
|
||||
|
||||
public virtual void Configure(IApplicationBuilder app)
|
||||
{
|
||||
app.UseRouting();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddCustomHealthCheck(Configuration);
|
||||
}
|
||||
|
||||
public virtual void Configure(IApplicationBuilder app)
|
||||
{
|
||||
app.UseRouting();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
|
||||
{
|
||||
Predicate = _ => true,
|
||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
|
||||
});
|
||||
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
|
||||
{
|
||||
Predicate = r => r.Name.Contains("self")
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
|
||||
{
|
||||
Predicate = _ => true,
|
||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
|
||||
});
|
||||
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
|
||||
{
|
||||
Predicate = r => r.Name.Contains("self")
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -1,49 +1,50 @@
|
||||
namespace ASC.Api.Core.Core
|
||||
namespace ASC.Api.Core.Core;
|
||||
|
||||
public class CustomEndpointDataSource : EndpointDataSource
|
||||
{
|
||||
public class CustomEndpointDataSource : EndpointDataSource
|
||||
public EndpointDataSource Source { get; }
|
||||
public override IReadOnlyList<Endpoint> Endpoints { get; }
|
||||
|
||||
public CustomEndpointDataSource(EndpointDataSource source)
|
||||
{
|
||||
public EndpointDataSource Source { get; }
|
||||
Source = source;
|
||||
var endpoints = Source.Endpoints.Cast<RouteEndpoint>();
|
||||
Endpoints = endpoints
|
||||
.SelectMany(r =>
|
||||
{
|
||||
var endpoints = new List<RouteEndpoint>();
|
||||
|
||||
public override IReadOnlyList<Endpoint> Endpoints { get; }
|
||||
public CustomEndpointDataSource(EndpointDataSource source)
|
||||
{
|
||||
Source = source;
|
||||
var endpoints = Source.Endpoints.Cast<RouteEndpoint>();
|
||||
Endpoints = endpoints
|
||||
.SelectMany(r =>
|
||||
var attr = r.Metadata.OfType<CustomHttpMethodAttribute>().FirstOrDefault();
|
||||
var enableFormat = attr == null || !attr.DisableFormat;
|
||||
|
||||
if (enableFormat)
|
||||
{
|
||||
var endpoints = new List<RouteEndpoint>();
|
||||
endpoints.Add(new RouteEndpoint(r.RequestDelegate, RoutePatternFactory.Parse(r.RoutePattern.RawText
|
||||
+ ".{format}"), r.Order, r.Metadata, r.DisplayName));
|
||||
}
|
||||
else
|
||||
{
|
||||
endpoints.Add(new RouteEndpoint(r.RequestDelegate, RoutePatternFactory.Parse(r.RoutePattern.RawText + ".json"), r.Order - 1, r.Metadata, r.DisplayName));
|
||||
endpoints.Add(new RouteEndpoint(r.RequestDelegate, RoutePatternFactory.Parse(r.RoutePattern.RawText + ".xml"), r.Order - 1, r.Metadata, r.DisplayName));
|
||||
}
|
||||
|
||||
return endpoints;
|
||||
|
||||
var attr = r.Metadata.OfType<CustomHttpMethodAttribute>().FirstOrDefault();
|
||||
var enableFormat = attr == null || !attr.DisableFormat;
|
||||
|
||||
if (enableFormat)
|
||||
{
|
||||
endpoints.Add(new RouteEndpoint(r.RequestDelegate, RoutePatternFactory.Parse(r.RoutePattern.RawText + ".{format}"), r.Order, r.Metadata, r.DisplayName));
|
||||
}
|
||||
else
|
||||
{
|
||||
endpoints.Add(new RouteEndpoint(r.RequestDelegate, RoutePatternFactory.Parse(r.RoutePattern.RawText + ".json"), r.Order - 1, r.Metadata, r.DisplayName));
|
||||
endpoints.Add(new RouteEndpoint(r.RequestDelegate, RoutePatternFactory.Parse(r.RoutePattern.RawText + ".xml"), r.Order - 1, r.Metadata, r.DisplayName));
|
||||
}
|
||||
|
||||
return endpoints;
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public override IChangeToken GetChangeToken()
|
||||
{
|
||||
return Source.GetChangeToken();
|
||||
}
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public static class EndpointExtension
|
||||
public override IChangeToken GetChangeToken()
|
||||
{
|
||||
public static IEndpointRouteBuilder MapCustom(this IEndpointRouteBuilder endpoints)
|
||||
{
|
||||
endpoints.DataSources.Add(new CustomEndpointDataSource(endpoints.DataSources.First()));
|
||||
return endpoints;
|
||||
}
|
||||
return Source.GetChangeToken();
|
||||
}
|
||||
}
|
||||
|
||||
public static class EndpointExtension
|
||||
{
|
||||
public static IEndpointRouteBuilder MapCustom(this IEndpointRouteBuilder endpoints)
|
||||
{
|
||||
endpoints.DataSources.Add(new CustomEndpointDataSource(endpoints.DataSources.First()));
|
||||
|
||||
return endpoints;
|
||||
}
|
||||
}
|
@ -1,64 +1,58 @@
|
||||
namespace ASC.Api.Core.Core
|
||||
namespace ASC.Api.Core.Core;
|
||||
|
||||
public static class CustomHealthCheck
|
||||
{
|
||||
public static class CustomHealthCheck
|
||||
public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration)
|
||||
var hcBuilder = services.AddHealthChecks();
|
||||
|
||||
hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy());
|
||||
|
||||
var configurationExtension = new ConfigurationExtension(configuration);
|
||||
|
||||
var connectionString = configurationExtension.GetConnectionStrings("default");
|
||||
|
||||
if (string.Equals(connectionString.ProviderName, "MySql.Data.MySqlClient"))
|
||||
{
|
||||
hcBuilder.AddMySql(connectionString.ConnectionString,
|
||||
name: "mysqldb",
|
||||
tags: new string[] { "mysqldb" });
|
||||
}
|
||||
|
||||
if (string.Equals(connectionString.ProviderName, "Npgsql"))
|
||||
{
|
||||
hcBuilder.AddNpgSql(connectionString.ConnectionString,
|
||||
name: "postgredb",
|
||||
tags: new string[] { "postgredb" });
|
||||
}
|
||||
|
||||
var kafkaSettings = configurationExtension.GetSetting<KafkaSettings>("kafka");
|
||||
if (kafkaSettings != null && !string.IsNullOrEmpty(kafkaSettings.BootstrapServers))
|
||||
{
|
||||
var hcBuilder = services.AddHealthChecks();
|
||||
var clientConfig = new ClientConfig { BootstrapServers = kafkaSettings.BootstrapServers };
|
||||
|
||||
hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy());
|
||||
hcBuilder.AddKafka(new ProducerConfig(clientConfig),
|
||||
name: "kafka",
|
||||
tags: new string[] { "kafka" });
|
||||
|
||||
var configurationExtension = new ConfigurationExtension(configuration);
|
||||
|
||||
var connectionString = configurationExtension.GetConnectionStrings("default");
|
||||
|
||||
if (String.Compare(connectionString.ProviderName, "MySql.Data.MySqlClient") == 0)
|
||||
{
|
||||
hcBuilder.AddMySql(connectionString.ConnectionString,
|
||||
name: "mysqldb",
|
||||
tags: new string[] { "mysqldb" }
|
||||
);
|
||||
}
|
||||
|
||||
if (String.Compare(connectionString.ProviderName, "Npgsql") == 0)
|
||||
{
|
||||
hcBuilder.AddNpgSql(connectionString.ConnectionString,
|
||||
name: "postgredb",
|
||||
tags: new string[] { "postgredb" }
|
||||
);
|
||||
}
|
||||
|
||||
var kafkaSettings = configurationExtension.GetSetting<KafkaSettings>("kafka");
|
||||
|
||||
if (kafkaSettings != null && !string.IsNullOrEmpty(kafkaSettings.BootstrapServers))
|
||||
{
|
||||
var clientConfig = new ClientConfig { BootstrapServers = kafkaSettings.BootstrapServers };
|
||||
|
||||
hcBuilder.AddKafka(new ProducerConfig(clientConfig),
|
||||
name: "kafka",
|
||||
tags: new string[] { "kafka" });
|
||||
|
||||
}
|
||||
|
||||
|
||||
var elasticSettings = configuration.GetSection("elastic");
|
||||
|
||||
if (elasticSettings != null && elasticSettings.GetChildren().Any())
|
||||
{
|
||||
var host = elasticSettings.GetSection("Host").Value ?? "localhost";
|
||||
var scheme = elasticSettings.GetSection("Scheme").Value ?? "http";
|
||||
var port = elasticSettings.GetSection("Port").Value ?? "9200";
|
||||
var elasticSearchUri = $"{scheme}://{host}:{port}";
|
||||
|
||||
if (Uri.IsWellFormedUriString(elasticSearchUri, UriKind.Absolute))
|
||||
{
|
||||
hcBuilder.AddElasticsearch(elasticSearchUri,
|
||||
name: "elasticsearch",
|
||||
tags: new string[] { "elasticsearch" });
|
||||
}
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
var elasticSettings = configuration.GetSection("elastic");
|
||||
if (elasticSettings != null && elasticSettings.GetChildren().Any())
|
||||
{
|
||||
var host = elasticSettings.GetSection("Host").Value ?? "localhost";
|
||||
var scheme = elasticSettings.GetSection("Scheme").Value ?? "http";
|
||||
var port = elasticSettings.GetSection("Port").Value ?? "9200";
|
||||
var elasticSearchUri = $"{scheme}://{host}:{port}";
|
||||
|
||||
if (Uri.IsWellFormedUriString(elasticSearchUri, UriKind.Absolute))
|
||||
{
|
||||
hcBuilder.AddElasticsearch(elasticSearchUri,
|
||||
name: "elasticsearch",
|
||||
tags: new string[] { "elasticsearch" });
|
||||
}
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
@ -23,18 +23,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Api.Collections
|
||||
{
|
||||
[CollectionDataContract(Name = "hash", Namespace = "", ItemName = "entry", KeyName = "key", ValueName = "value")]
|
||||
public class ItemDictionary<TKey, TValue> : Dictionary<TKey, TValue>
|
||||
{
|
||||
public ItemDictionary()
|
||||
{
|
||||
}
|
||||
namespace ASC.Api.Collections;
|
||||
|
||||
public ItemDictionary(IDictionary<TKey, TValue> items)
|
||||
: base(items)
|
||||
{
|
||||
}
|
||||
}
|
||||
[CollectionDataContract(Name = "hash", Namespace = "", ItemName = "entry", KeyName = "key", ValueName = "value")]
|
||||
public class ItemDictionary<TKey, TValue> : Dictionary<TKey, TValue>
|
||||
{
|
||||
public ItemDictionary() { }
|
||||
|
||||
public ItemDictionary(IDictionary<TKey, TValue> items)
|
||||
: base(items) { }
|
||||
}
|
@ -23,15 +23,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Api.Collections
|
||||
namespace ASC.Api.Collections;
|
||||
|
||||
public class ItemKeyValuePair<TKey, TValue>
|
||||
{
|
||||
|
||||
public class ItemKeyValuePair<TKey, TValue>
|
||||
{
|
||||
public TKey Key { get; set; }
|
||||
|
||||
|
||||
public TValue Value { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
public TKey Key { get; set; }
|
||||
public TValue Value { get; set; }
|
||||
}
|
@ -1,31 +1,27 @@
|
||||
namespace ASC.Api.Core
|
||||
{
|
||||
public class Module
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
namespace ASC.Api.Core;
|
||||
|
||||
public string AppName { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Link { get; set; }
|
||||
public class Module
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string AppName { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Link { get; set; }
|
||||
public string IconUrl { get; set; }
|
||||
public string ImageUrl { get; set; }
|
||||
public string HelpUrl { get; set; }
|
||||
public string Description { get; set; }
|
||||
public bool IsPrimary { get; set; }
|
||||
|
||||
public string IconUrl { get; set; }
|
||||
public string ImageUrl { get; set; }
|
||||
|
||||
public string HelpUrl { get; set; }
|
||||
public string Description { get; set; }
|
||||
public bool IsPrimary { get; set; }
|
||||
|
||||
public Module(Product product)
|
||||
{
|
||||
Id = product.ProductID;
|
||||
AppName = product.ProductClassName;
|
||||
Title = product.Name;
|
||||
Description = product.Description;
|
||||
IconUrl = product.Context.IconFileName;
|
||||
ImageUrl = product.Context.LargeIconFileName;
|
||||
Link = product.StartURL;
|
||||
IsPrimary = product.IsPrimary;
|
||||
HelpUrl = product.HelpURL;
|
||||
}
|
||||
}
|
||||
}
|
||||
public Module(Product product)
|
||||
{
|
||||
Id = product.ProductID;
|
||||
AppName = product.ProductClassName;
|
||||
Title = product.Name;
|
||||
Description = product.Description;
|
||||
IconUrl = product.Context.IconFileName;
|
||||
ImageUrl = product.Context.LargeIconFileName;
|
||||
Link = product.StartURL;
|
||||
IsPrimary = product.IsPrimary;
|
||||
HelpUrl = product.HelpURL;
|
||||
}
|
||||
}
|
@ -24,28 +24,23 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Api.Utils
|
||||
namespace ASC.Api.Utils;
|
||||
|
||||
public static class Update
|
||||
{
|
||||
public static class Update
|
||||
public static T IfNotEquals<T>(T current, T @new)
|
||||
{
|
||||
public static T IfNotEquals<T>(T current, T @new)
|
||||
{
|
||||
if (!Equals(current, @new))
|
||||
{
|
||||
return @new;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
if (!Equals(current, @new)) return @new;
|
||||
|
||||
public static T IfNotEmptyAndNotEquals<T>(T current, T @new)
|
||||
{
|
||||
if (Equals(@new, default(T))) return current;
|
||||
return current;
|
||||
}
|
||||
|
||||
if (!Equals(current, @new))
|
||||
{
|
||||
return @new;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
public static T IfNotEmptyAndNotEquals<T>(T current, T @new)
|
||||
{
|
||||
if (Equals(@new, default(T))) return current;
|
||||
|
||||
if (!Equals(current, @new)) return @new;
|
||||
|
||||
return current;
|
||||
}
|
||||
}
|
@ -23,38 +23,37 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Api.Utils
|
||||
namespace ASC.Api.Utils;
|
||||
|
||||
public static class Validate
|
||||
{
|
||||
public static class Validate
|
||||
public static T If<T>(this T item, Func<T, bool> @if, Func<T> then) where T : class
|
||||
{
|
||||
public static T If<T>(this T item, Func<T, bool> @if, Func<T> then) where T : class
|
||||
{
|
||||
return @if(item) ? then() : item;
|
||||
}
|
||||
return @if(item) ? then() : item;
|
||||
}
|
||||
|
||||
public static T IfNull<T>(this T item, Func<T> func) where T : class
|
||||
{
|
||||
return item.If((x) => x == default(T), func);
|
||||
}
|
||||
public static T IfNull<T>(this T item, Func<T> func) where T : class
|
||||
{
|
||||
return item.If((x) => x == default(T), func);
|
||||
}
|
||||
|
||||
public static T ThrowIfNull<T>(this T item, Exception e) where T : class
|
||||
{
|
||||
return item.IfNull(() => { throw e; });
|
||||
}
|
||||
public static T ThrowIfNull<T>(this T item, Exception e) where T : class
|
||||
{
|
||||
return item.IfNull(() => { throw e; });
|
||||
}
|
||||
|
||||
public static T NotFoundIfNull<T>(this T item) where T : class
|
||||
{
|
||||
return NotFoundIfNull(item, "Item not found");
|
||||
}
|
||||
public static T NotFoundIfNull<T>(this T item) where T : class
|
||||
{
|
||||
return NotFoundIfNull(item, "Item not found");
|
||||
}
|
||||
|
||||
public static T NotFoundIfNull<T>(this T item, string message) where T : class
|
||||
{
|
||||
return item.IfNull(() => { throw new ItemNotFoundException(message); });
|
||||
}
|
||||
public static T NotFoundIfNull<T>(this T item, string message) where T : class
|
||||
{
|
||||
return item.IfNull(() => { throw new ItemNotFoundException(message); });
|
||||
}
|
||||
|
||||
public static T? NullIfDefault<T>(this T item) where T : struct
|
||||
{
|
||||
return EqualityComparer<T>.Default.Equals(item, default(T)) ? default(T?) : item;
|
||||
}
|
||||
public static T? NullIfDefault<T>(this T item) where T : struct
|
||||
{
|
||||
return EqualityComparer<T>.Default.Equals(item, default(T)) ? default(T?) : item;
|
||||
}
|
||||
}
|
@ -1,23 +1,23 @@
|
||||
namespace ASC.Api.Core.Core
|
||||
namespace ASC.Api.Core.Core;
|
||||
|
||||
public class XmlOutputFormatter : IOutputFormatter
|
||||
{
|
||||
public class XmlOutputFormatter : IOutputFormatter
|
||||
public bool CanWriteResult(OutputFormatterCanWriteContext context)
|
||||
{
|
||||
public bool CanWriteResult(OutputFormatterCanWriteContext context)
|
||||
{
|
||||
return context.ContentType == MimeMapping.GetMimeMapping(".xml");
|
||||
}
|
||||
|
||||
public Task WriteAsync(OutputFormatterWriteContext context)
|
||||
{
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
DateParseHandling = DateParseHandling.None
|
||||
};
|
||||
|
||||
var responseJson = JsonConvert.SerializeObject(context.Object, Formatting.Indented, settings);
|
||||
responseJson = JsonConvert.DeserializeObject<XDocument>("{\"result\":" + responseJson + "}", settings).ToString(SaveOptions.None);
|
||||
return context.HttpContext.Response.WriteAsync(responseJson);
|
||||
}
|
||||
return context.ContentType == MimeMapping.GetMimeMapping(".xml");
|
||||
}
|
||||
}
|
||||
|
||||
public Task WriteAsync(OutputFormatterWriteContext context)
|
||||
{
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
DateParseHandling = DateParseHandling.None
|
||||
};
|
||||
|
||||
var responseJson = JsonConvert.SerializeObject(context.Object, Formatting.Indented, settings);
|
||||
responseJson = JsonConvert.DeserializeObject<XDocument>("{\"result\":" + responseJson + "}", settings).ToString(SaveOptions.None);
|
||||
|
||||
return context.HttpContext.Response.WriteAsync(responseJson);
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using System.Web;
|
||||
global using System.Xml.Linq;
|
||||
global using StackExchange.Redis.Extensions.Newtonsoft;
|
||||
|
||||
global using ASC.Api.Core;
|
||||
global using ASC.Api.Core.Auth;
|
||||
global using ASC.Api.Core.Convention;
|
||||
@ -36,7 +36,6 @@ global using ASC.Core;
|
||||
global using ASC.Core.Common.EF;
|
||||
global using ASC.Core.Tenants;
|
||||
global using ASC.Core.Users;
|
||||
global using ASC.IPSecurity;
|
||||
global using ASC.Security.Cryptography;
|
||||
global using ASC.Web.Api.Routing;
|
||||
global using ASC.Web.Core;
|
||||
@ -82,3 +81,4 @@ global using NLog;
|
||||
global using NLog.Extensions.Logging;
|
||||
|
||||
global using StackExchange.Redis.Extensions.Core.Configuration;
|
||||
global using StackExchange.Redis.Extensions.Newtonsoft;
|
@ -1,97 +1,91 @@
|
||||
namespace ASC.Api.Core.Middleware
|
||||
namespace ASC.Api.Core.Middleware;
|
||||
|
||||
public abstract class CommonApiResponse
|
||||
{
|
||||
public abstract class CommonApiResponse
|
||||
public int Status { get; set; }
|
||||
public HttpStatusCode StatusCode { get; set; }
|
||||
|
||||
protected CommonApiResponse(HttpStatusCode statusCode)
|
||||
{
|
||||
public int Status { get; set; }
|
||||
StatusCode = statusCode;
|
||||
|
||||
public HttpStatusCode StatusCode { get; set; }
|
||||
|
||||
protected CommonApiResponse(HttpStatusCode statusCode)
|
||||
{
|
||||
StatusCode = statusCode;
|
||||
}
|
||||
|
||||
public static SuccessApiResponse Create(HttpStatusCode statusCode, object response)
|
||||
{
|
||||
return new SuccessApiResponse(statusCode, response);
|
||||
}
|
||||
|
||||
public static ErrorApiResponse CreateError(HttpStatusCode statusCode, Exception error)
|
||||
{
|
||||
return new ErrorApiResponse(statusCode, error);
|
||||
}
|
||||
}
|
||||
|
||||
public class ErrorApiResponse : CommonApiResponse
|
||||
public static SuccessApiResponse Create(HttpStatusCode statusCode, object response)
|
||||
{
|
||||
public CommonApiError Error { get; set; }
|
||||
|
||||
protected internal ErrorApiResponse(HttpStatusCode statusCode, Exception error, string message = null) : base(statusCode)
|
||||
{
|
||||
Status = 1;
|
||||
Error = CommonApiError.FromException(error, message);
|
||||
}
|
||||
return new SuccessApiResponse(statusCode, response);
|
||||
}
|
||||
|
||||
public class SuccessApiResponse : CommonApiResponse
|
||||
public static ErrorApiResponse CreateError(HttpStatusCode statusCode, Exception error)
|
||||
{
|
||||
public int? Count { get; set; }
|
||||
return new ErrorApiResponse(statusCode, error);
|
||||
}
|
||||
}
|
||||
|
||||
public long? Total { get; set; }
|
||||
public class ErrorApiResponse : CommonApiResponse
|
||||
{
|
||||
public CommonApiError Error { get; set; }
|
||||
|
||||
public object Response { get; set; }
|
||||
protected internal ErrorApiResponse(HttpStatusCode statusCode, Exception error, string message = null) : base(statusCode)
|
||||
{
|
||||
Status = 1;
|
||||
Error = CommonApiError.FromException(error, message);
|
||||
}
|
||||
}
|
||||
|
||||
protected internal SuccessApiResponse(HttpStatusCode statusCode, object response, long? total = null, int? count = null) : base(statusCode)
|
||||
public class SuccessApiResponse : CommonApiResponse
|
||||
{
|
||||
public int? Count { get; set; }
|
||||
public long? Total { get; set; }
|
||||
public object Response { get; set; }
|
||||
|
||||
protected internal SuccessApiResponse(HttpStatusCode statusCode, object response, long? total = null, int? count = null) : base(statusCode)
|
||||
{
|
||||
Status = 0;
|
||||
Response = response;
|
||||
Total = total;
|
||||
|
||||
if (count.HasValue)
|
||||
{
|
||||
Status = 0;
|
||||
Response = response;
|
||||
Total = total;
|
||||
|
||||
if (count.HasValue)
|
||||
Count = count;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (response is List<object> list)
|
||||
{
|
||||
Count = count;
|
||||
Count = list.Count;
|
||||
}
|
||||
else if (response is IEnumerable<object> collection)
|
||||
{
|
||||
Count = collection.Count();
|
||||
}
|
||||
else if (response == null)
|
||||
{
|
||||
Count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (response is List<object> list)
|
||||
{
|
||||
Count = list.Count;
|
||||
}
|
||||
else if (response is IEnumerable<object> collection)
|
||||
{
|
||||
Count = collection.Count();
|
||||
}
|
||||
else if (response == null)
|
||||
{
|
||||
Count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Count = 1;
|
||||
}
|
||||
Count = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CommonApiError
|
||||
{
|
||||
public string Message { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
|
||||
public string Stack { get; set; }
|
||||
|
||||
public int Hresult { get; set; }
|
||||
|
||||
public static CommonApiError FromException(Exception exception, string message = null)
|
||||
{
|
||||
return new CommonApiError()
|
||||
{
|
||||
Message = message ?? exception.Message,
|
||||
Type = exception.GetType().ToString(),
|
||||
Stack = exception.StackTrace,
|
||||
Hresult = exception.HResult
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CommonApiError
|
||||
{
|
||||
public string Message { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Stack { get; set; }
|
||||
public int Hresult { get; set; }
|
||||
|
||||
public static CommonApiError FromException(Exception exception, string message = null)
|
||||
{
|
||||
return new CommonApiError()
|
||||
{
|
||||
Message = message ?? exception.Message,
|
||||
Type = exception.GetType().ToString(),
|
||||
Stack = exception.StackTrace,
|
||||
Hresult = exception.HResult
|
||||
};
|
||||
}
|
||||
}
|
@ -1,45 +1,44 @@
|
||||
namespace ASC.Api.Core.Middleware
|
||||
namespace ASC.Api.Core.Middleware;
|
||||
|
||||
public class CultureMiddleware
|
||||
{
|
||||
public class CultureMiddleware
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public CultureMiddleware(RequestDelegate next)
|
||||
{
|
||||
private readonly RequestDelegate next;
|
||||
|
||||
public CultureMiddleware(RequestDelegate next)
|
||||
{
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context, UserManager userManager, TenantManager tenantManager, AuthContext authContext)
|
||||
{
|
||||
CultureInfo culture = null;
|
||||
|
||||
if (authContext.IsAuthenticated)
|
||||
{
|
||||
var user = userManager.GetUsers(authContext.CurrentAccount.ID);
|
||||
|
||||
if (!string.IsNullOrEmpty(user.CultureName))
|
||||
{
|
||||
culture = user.GetCulture();
|
||||
}
|
||||
}
|
||||
|
||||
if (culture == null)
|
||||
{
|
||||
culture = tenantManager.GetCurrentTenant().GetCulture();
|
||||
}
|
||||
|
||||
Thread.CurrentThread.CurrentCulture = culture;
|
||||
Thread.CurrentThread.CurrentUICulture = culture;
|
||||
|
||||
await next.Invoke(context);
|
||||
}
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public static class CultureMiddlewareExtensions
|
||||
public async Task Invoke(HttpContext context, UserManager userManager, TenantManager tenantManager, AuthContext authContext)
|
||||
{
|
||||
public static IApplicationBuilder UseCultureMiddleware(this IApplicationBuilder builder)
|
||||
CultureInfo culture = null;
|
||||
|
||||
if (authContext.IsAuthenticated)
|
||||
{
|
||||
return builder.UseMiddleware<CultureMiddleware>();
|
||||
var user = userManager.GetUsers(authContext.CurrentAccount.ID);
|
||||
|
||||
if (!string.IsNullOrEmpty(user.CultureName))
|
||||
{
|
||||
culture = user.GetCulture();
|
||||
}
|
||||
}
|
||||
|
||||
if (culture == null)
|
||||
{
|
||||
culture = tenantManager.GetCurrentTenant().GetCulture();
|
||||
}
|
||||
|
||||
Thread.CurrentThread.CurrentCulture = culture;
|
||||
Thread.CurrentThread.CurrentUICulture = culture;
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CultureMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseCultureMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<CultureMiddleware>();
|
||||
}
|
||||
}
|
@ -1,27 +1,26 @@
|
||||
namespace ASC.Api.Core.Middleware
|
||||
namespace ASC.Api.Core.Middleware;
|
||||
|
||||
public class DisposeMiddleware
|
||||
{
|
||||
public class DisposeMiddleware
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public DisposeMiddleware(RequestDelegate next)
|
||||
{
|
||||
private readonly RequestDelegate next;
|
||||
|
||||
public DisposeMiddleware(RequestDelegate next)
|
||||
{
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
context.Response.RegisterForDispose(new DisposableHttpContext(context));
|
||||
|
||||
await next.Invoke(context);
|
||||
}
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public static class DisposeMiddlewareExtensions
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
public static IApplicationBuilder UseDisposeMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<DisposeMiddleware>();
|
||||
}
|
||||
context.Response.RegisterForDispose(new DisposableHttpContext(context));
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DisposeMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseDisposeMiddleware(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<DisposeMiddleware>();
|
||||
}
|
||||
}
|
@ -1,36 +1,32 @@
|
||||
namespace ASC.Api.Core.Middleware
|
||||
namespace ASC.Api.Core.Middleware;
|
||||
|
||||
[Scope]
|
||||
public class IpSecurityFilter : IResourceFilter
|
||||
{
|
||||
[Scope]
|
||||
public class IpSecurityFilter : IResourceFilter
|
||||
private readonly AuthContext _authContext;
|
||||
private readonly IPSecurity.IPSecurity _iPSecurity;
|
||||
private readonly ILog _logger;
|
||||
|
||||
public IpSecurityFilter(
|
||||
IOptionsMonitor<ILog> options,
|
||||
AuthContext authContext,
|
||||
IPSecurity.IPSecurity IPSecurity)
|
||||
{
|
||||
private readonly ILog log;
|
||||
_logger = options.CurrentValue;
|
||||
_authContext = authContext;
|
||||
_iPSecurity = IPSecurity;
|
||||
}
|
||||
|
||||
public IpSecurityFilter(
|
||||
IOptionsMonitor<ILog> options,
|
||||
AuthContext authContext,
|
||||
IPSecurity.IPSecurity IPSecurity)
|
||||
public void OnResourceExecuted(ResourceExecutedContext context) { }
|
||||
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
if (_authContext.IsAuthenticated && !_iPSecurity.Verify())
|
||||
{
|
||||
log = options.CurrentValue;
|
||||
AuthContext = authContext;
|
||||
this.IPSecurity = IPSecurity;
|
||||
}
|
||||
context.Result = new StatusCodeResult((int)HttpStatusCode.Forbidden);
|
||||
_logger.WarnFormat("IPSecurity: user {0}", _authContext.CurrentAccount.ID);
|
||||
|
||||
private AuthContext AuthContext { get; }
|
||||
public IPRestrictionsSettings IPRestrictionsSettings { get; }
|
||||
private IPSecurity.IPSecurity IPSecurity { get; }
|
||||
|
||||
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
if (AuthContext.IsAuthenticated && !IPSecurity.Verify())
|
||||
{
|
||||
context.Result = new StatusCodeResult((int)HttpStatusCode.Forbidden);
|
||||
log.WarnFormat("IPSecurity: user {0}", AuthContext.CurrentAccount.ID);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,39 +1,37 @@
|
||||
namespace ASC.Api.Core.Middleware
|
||||
namespace ASC.Api.Core.Middleware;
|
||||
|
||||
[Scope]
|
||||
public class PaymentFilter : IResourceFilter
|
||||
{
|
||||
[Scope]
|
||||
public class PaymentFilter : IResourceFilter
|
||||
private readonly TenantExtra _tenantExtra;
|
||||
private readonly ILog _logger;
|
||||
|
||||
public PaymentFilter(IOptionsMonitor<ILog> options, TenantExtra tenantExtra)
|
||||
{
|
||||
private readonly ILog log;
|
||||
_logger = options.CurrentValue;
|
||||
_tenantExtra = tenantExtra;
|
||||
}
|
||||
|
||||
public PaymentFilter(IOptionsMonitor<ILog> options, TenantExtra tenantExtra)
|
||||
public void OnResourceExecuted(ResourceExecutedContext context) { }
|
||||
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
if (context.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor
|
||||
&& !controllerActionDescriptor.EndpointMetadata.OfType<CustomHttpMethodAttribute>().FirstOrDefault().Check)
|
||||
{
|
||||
log = options.CurrentValue;
|
||||
TenantExtra = tenantExtra;
|
||||
_logger.Debug("Payment is not required");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private TenantExtra TenantExtra { get; }
|
||||
|
||||
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||
var header = context.HttpContext.Request.Headers["Payment-Info"];
|
||||
if (string.IsNullOrEmpty(header) || (bool.TryParse(header, out var flag) && flag))
|
||||
{
|
||||
}
|
||||
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
if (context.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor && !controllerActionDescriptor.EndpointMetadata.OfType<CustomHttpMethodAttribute>().FirstOrDefault().Check)
|
||||
if (_tenantExtra.IsNotPaid())
|
||||
{
|
||||
log.Debug("Payment is not required");
|
||||
return;
|
||||
}
|
||||
|
||||
var header = context.HttpContext.Request.Headers["Payment-Info"];
|
||||
if (string.IsNullOrEmpty(header) || (bool.TryParse(header, out var flag) && flag))
|
||||
{
|
||||
if (TenantExtra.IsNotPaid())
|
||||
{
|
||||
context.Result = new StatusCodeResult((int)HttpStatusCode.PaymentRequired);
|
||||
log.WarnFormat("Payment Required {0}.", context.HttpContext.Request.Url());
|
||||
}
|
||||
context.Result = new StatusCodeResult((int)HttpStatusCode.PaymentRequired);
|
||||
_logger.WarnFormat("Payment Required {0}.", context.HttpContext.Request.Url());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +1,23 @@
|
||||
namespace ASC.Api.Core.Middleware
|
||||
namespace ASC.Api.Core.Middleware;
|
||||
|
||||
[Scope]
|
||||
public class ProductSecurityFilter : IResourceFilter
|
||||
{
|
||||
[Scope]
|
||||
public class ProductSecurityFilter : IResourceFilter
|
||||
private static readonly IDictionary<string, Guid> _products;
|
||||
private readonly ILog _logger;
|
||||
private readonly WebItemSecurity _webItemSecurity;
|
||||
private readonly AuthContext _authContext;
|
||||
|
||||
static ProductSecurityFilter()
|
||||
{
|
||||
private static readonly IDictionary<string, Guid> products;
|
||||
private readonly ILog log;
|
||||
var blog = new Guid("6a598c74-91ae-437d-a5f4-ad339bd11bb2");
|
||||
var bookmark = new Guid("28b10049-dd20-4f54-b986-873bc14ccfc7");
|
||||
var forum = new Guid("853b6eb9-73ee-438d-9b09-8ffeedf36234");
|
||||
var news = new Guid("3cfd481b-46f2-4a4a-b55c-b8c0c9def02c");
|
||||
var wiki = new Guid("742cf945-cbbc-4a57-82d6-1600a12cf8ca");
|
||||
var photo = new Guid("9d51954f-db9b-4aed-94e3-ed70b914e101");
|
||||
|
||||
private WebItemSecurity WebItemSecurity { get; }
|
||||
private AuthContext AuthContext { get; }
|
||||
|
||||
static ProductSecurityFilter()
|
||||
{
|
||||
var blog = new Guid("6a598c74-91ae-437d-a5f4-ad339bd11bb2");
|
||||
var bookmark = new Guid("28b10049-dd20-4f54-b986-873bc14ccfc7");
|
||||
var forum = new Guid("853b6eb9-73ee-438d-9b09-8ffeedf36234");
|
||||
var news = new Guid("3cfd481b-46f2-4a4a-b55c-b8c0c9def02c");
|
||||
var wiki = new Guid("742cf945-cbbc-4a57-82d6-1600a12cf8ca");
|
||||
var photo = new Guid("9d51954f-db9b-4aed-94e3-ed70b914e101");
|
||||
|
||||
products = new Dictionary<string, Guid>
|
||||
_products = new Dictionary<string, Guid>
|
||||
{
|
||||
{ "blog", blog },
|
||||
{ "bookmark", bookmark },
|
||||
@ -34,70 +33,73 @@
|
||||
{ "calendar", WebItemManager.CalendarProductID },
|
||||
{ "mail", WebItemManager.MailProductID },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public ProductSecurityFilter(
|
||||
IOptionsMonitor<ILog> options,
|
||||
WebItemSecurity webItemSecurity,
|
||||
AuthContext authContext)
|
||||
{
|
||||
_logger = options.CurrentValue;
|
||||
_webItemSecurity = webItemSecurity;
|
||||
_authContext = authContext;
|
||||
}
|
||||
|
||||
public void OnResourceExecuted(ResourceExecutedContext context) { }
|
||||
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
if (!_authContext.IsAuthenticated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
public ProductSecurityFilter(
|
||||
IOptionsMonitor<ILog> options,
|
||||
WebItemSecurity webItemSecurity,
|
||||
AuthContext authContext)
|
||||
if (context.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
|
||||
{
|
||||
log = options.CurrentValue;
|
||||
WebItemSecurity = webItemSecurity;
|
||||
AuthContext = authContext;
|
||||
}
|
||||
|
||||
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
if (!AuthContext.IsAuthenticated) return;
|
||||
|
||||
if (context.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
|
||||
var pid = FindProduct(controllerActionDescriptor);
|
||||
if (pid != Guid.Empty)
|
||||
{
|
||||
var pid = FindProduct(controllerActionDescriptor);
|
||||
if (pid != Guid.Empty)
|
||||
if (CallContext.GetData("asc.web.product_id") == null)
|
||||
{
|
||||
if (CallContext.GetData("asc.web.product_id") == null)
|
||||
{
|
||||
CallContext.SetData("asc.web.product_id", pid);
|
||||
}
|
||||
if (!WebItemSecurity.IsAvailableForMe(pid))
|
||||
{
|
||||
context.Result = new StatusCodeResult((int)HttpStatusCode.Forbidden);
|
||||
log.WarnFormat("Product {0} denied for user {1}", controllerActionDescriptor.ControllerName, AuthContext.CurrentAccount);
|
||||
}
|
||||
CallContext.SetData("asc.web.product_id", pid);
|
||||
}
|
||||
|
||||
if (!_webItemSecurity.IsAvailableForMe(pid))
|
||||
{
|
||||
context.Result = new StatusCodeResult((int)HttpStatusCode.Forbidden);
|
||||
_logger.WarnFormat("Product {0} denied for user {1}", controllerActionDescriptor.ControllerName, _authContext.CurrentAccount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Guid FindProduct(ControllerActionDescriptor method)
|
||||
{
|
||||
if (method == null || string.IsNullOrEmpty(method.ControllerName))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
var name = method.ControllerName.ToLower();
|
||||
if (name == "community")
|
||||
{
|
||||
var url = method.MethodInfo.GetCustomAttribute<HttpMethodAttribute>().Template;
|
||||
if (!string.IsNullOrEmpty(url))
|
||||
{
|
||||
var module = url.Split('/')[0];
|
||||
if (products.ContainsKey(module))
|
||||
{
|
||||
return products[module];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (products.ContainsKey(name))
|
||||
{
|
||||
return products[name];
|
||||
}
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Guid FindProduct(ControllerActionDescriptor method)
|
||||
{
|
||||
if (method == null || string.IsNullOrEmpty(method.ControllerName))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var name = method.ControllerName.ToLower();
|
||||
if (name == "community")
|
||||
{
|
||||
var url = method.MethodInfo.GetCustomAttribute<HttpMethodAttribute>().Template;
|
||||
if (!string.IsNullOrEmpty(url))
|
||||
{
|
||||
var module = url.Split('/')[0];
|
||||
if (_products.TryGetValue(module, out var communityProduct))
|
||||
{
|
||||
return communityProduct;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_products.TryGetValue(name, out var product))
|
||||
{
|
||||
return product;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
@ -1,57 +1,56 @@
|
||||
namespace ASC.Api.Core.Middleware
|
||||
namespace ASC.Api.Core.Middleware;
|
||||
|
||||
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
|
||||
{
|
||||
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
|
||||
public override void OnException(ExceptionContext context)
|
||||
{
|
||||
public override void OnException(ExceptionContext context)
|
||||
var status = (HttpStatusCode)context.HttpContext.Response.StatusCode;
|
||||
string message = null;
|
||||
|
||||
if (status == HttpStatusCode.OK)
|
||||
{
|
||||
var status = (HttpStatusCode)context.HttpContext.Response.StatusCode;
|
||||
string message = null;
|
||||
|
||||
if (status == HttpStatusCode.OK)
|
||||
{
|
||||
status = HttpStatusCode.InternalServerError;
|
||||
}
|
||||
|
||||
switch (context.Exception)
|
||||
{
|
||||
case ItemNotFoundException:
|
||||
status = HttpStatusCode.NotFound;
|
||||
message = "The record could not be found";
|
||||
break;
|
||||
case ArgumentException:
|
||||
status = HttpStatusCode.BadRequest;
|
||||
message = "Invalid arguments";
|
||||
break;
|
||||
case SecurityException:
|
||||
status = HttpStatusCode.Forbidden;
|
||||
message = "Access denied";
|
||||
break;
|
||||
case InvalidOperationException:
|
||||
status = HttpStatusCode.Forbidden;
|
||||
break;
|
||||
}
|
||||
|
||||
var result = new ObjectResult(new ErrorApiResponse(status, context.Exception, message))
|
||||
{
|
||||
StatusCode = (int)status
|
||||
};
|
||||
|
||||
context.Result = result;
|
||||
status = HttpStatusCode.InternalServerError;
|
||||
}
|
||||
|
||||
switch (context.Exception)
|
||||
{
|
||||
case ItemNotFoundException:
|
||||
status = HttpStatusCode.NotFound;
|
||||
message = "The record could not be found";
|
||||
break;
|
||||
case ArgumentException:
|
||||
status = HttpStatusCode.BadRequest;
|
||||
message = "Invalid arguments";
|
||||
break;
|
||||
case SecurityException:
|
||||
status = HttpStatusCode.Forbidden;
|
||||
message = "Access denied";
|
||||
break;
|
||||
case InvalidOperationException:
|
||||
status = HttpStatusCode.Forbidden;
|
||||
break;
|
||||
}
|
||||
|
||||
var result = new ObjectResult(new ErrorApiResponse(status, context.Exception, message))
|
||||
{
|
||||
StatusCode = (int)status
|
||||
};
|
||||
|
||||
context.Result = result;
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomResponseFilterAttribute : ResultFilterAttribute
|
||||
public class CustomResponseFilterAttribute : ResultFilterAttribute
|
||||
{
|
||||
public override void OnResultExecuting(ResultExecutingContext context)
|
||||
{
|
||||
public override void OnResultExecuting(ResultExecutingContext context)
|
||||
if (context.Result is ObjectResult result)
|
||||
{
|
||||
if (context.Result is ObjectResult result)
|
||||
{
|
||||
context.HttpContext.Items.TryGetValue("TotalCount", out var total);
|
||||
context.HttpContext.Items.TryGetValue("Count", out var count);
|
||||
result.Value = new SuccessApiResponse((HttpStatusCode)context.HttpContext.Response.StatusCode, result.Value, (long?)total, (int?)count);
|
||||
}
|
||||
|
||||
base.OnResultExecuting(context);
|
||||
context.HttpContext.Items.TryGetValue("TotalCount", out var total);
|
||||
context.HttpContext.Items.TryGetValue("Count", out var count);
|
||||
result.Value = new SuccessApiResponse((HttpStatusCode)context.HttpContext.Response.StatusCode, result.Value, (long?)total, (int?)count);
|
||||
}
|
||||
|
||||
base.OnResultExecuting(context);
|
||||
}
|
||||
}
|
@ -1,38 +1,36 @@
|
||||
namespace ASC.Api.Core.Middleware
|
||||
namespace ASC.Api.Core.Middleware;
|
||||
|
||||
[Scope]
|
||||
public class TenantStatusFilter : IResourceFilter
|
||||
{
|
||||
[Scope]
|
||||
public class TenantStatusFilter : IResourceFilter
|
||||
private readonly TenantManager _tenantManager;
|
||||
private readonly ILog _logger;
|
||||
|
||||
public TenantStatusFilter(IOptionsMonitor<ILog> options, TenantManager tenantManager)
|
||||
{
|
||||
private readonly ILog log;
|
||||
_logger = options.CurrentValue;
|
||||
_tenantManager = tenantManager;
|
||||
}
|
||||
|
||||
public TenantStatusFilter(IOptionsMonitor<ILog> options, TenantManager tenantManager)
|
||||
public void OnResourceExecuted(ResourceExecutedContext context) { }
|
||||
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
var tenant = _tenantManager.GetCurrentTenant(false);
|
||||
if (tenant == null)
|
||||
{
|
||||
log = options.CurrentValue;
|
||||
TenantManager = tenantManager;
|
||||
context.Result = new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
_logger.Warn("Current tenant not found");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private TenantManager TenantManager { get; }
|
||||
|
||||
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||
if (tenant.Status == TenantStatus.RemovePending || tenant.Status == TenantStatus.Suspended)
|
||||
{
|
||||
}
|
||||
context.Result = new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
_logger.WarnFormat("Tenant {0} is not removed or suspended", tenant.TenantId);
|
||||
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
var tenant = TenantManager.GetCurrentTenant(false);
|
||||
if (tenant == null)
|
||||
{
|
||||
context.Result = new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
log.WarnFormat("Tenant {0} not found", tenant.TenantId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tenant.Status == TenantStatus.RemovePending || tenant.Status == TenantStatus.Suspended)
|
||||
{
|
||||
context.Result = new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||
log.WarnFormat("Tenant {0} is not removed or suspended", tenant.TenantId);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,50 +1,53 @@
|
||||
namespace ASC.Api.Core.Middleware
|
||||
{
|
||||
[Scope]
|
||||
public class WebhooksGlobalFilterAttribute : ResultFilterAttribute
|
||||
{
|
||||
private IWebhookPublisher WebhookPublisher { get; }
|
||||
private static List<string> methodList = new List<string> { "POST", "UPDATE", "DELETE" };
|
||||
private JsonSerializerOptions jsonSerializerOptions;
|
||||
|
||||
public WebhooksGlobalFilterAttribute(IWebhookPublisher webhookPublisher, Action<JsonOptions> projectJsonOptions)
|
||||
{
|
||||
WebhookPublisher = webhookPublisher;
|
||||
|
||||
var jsonOptions = new JsonOptions();
|
||||
projectJsonOptions.Invoke(jsonOptions);
|
||||
jsonSerializerOptions = jsonOptions.JsonSerializerOptions;
|
||||
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
||||
namespace ASC.Api.Core.Middleware;
|
||||
|
||||
[Scope]
|
||||
public class WebhooksGlobalFilterAttribute : ResultFilterAttribute
|
||||
{
|
||||
private readonly IWebhookPublisher _webhookPublisher;
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||
private static List<string> _methodList = new List<string> { "POST", "UPDATE", "DELETE" };
|
||||
|
||||
public WebhooksGlobalFilterAttribute(IWebhookPublisher webhookPublisher, Action<JsonOptions> projectJsonOptions)
|
||||
{
|
||||
_webhookPublisher = webhookPublisher;
|
||||
|
||||
var jsonOptions = new JsonOptions();
|
||||
projectJsonOptions.Invoke(jsonOptions);
|
||||
_jsonSerializerOptions = jsonOptions.JsonSerializerOptions;
|
||||
}
|
||||
|
||||
public override void OnResultExecuted(ResultExecutedContext context)
|
||||
{
|
||||
var method = context.HttpContext.Request.Method;
|
||||
|
||||
if (!_methodList.Contains(method) || context.Canceled)
|
||||
{
|
||||
base.OnResultExecuted(context);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public override void OnResultExecuted(ResultExecutedContext context)
|
||||
{
|
||||
var method = context.HttpContext.Request.Method;
|
||||
|
||||
if (!methodList.Contains(method) || context.Canceled)
|
||||
{
|
||||
base.OnResultExecuted(context);
|
||||
return;
|
||||
}
|
||||
|
||||
var endpoint = (RouteEndpoint)context.HttpContext.GetEndpoint();
|
||||
var routePattern = endpoint?.RoutePattern.RawText;
|
||||
|
||||
if (routePattern == null)
|
||||
{
|
||||
base.OnResultExecuted(context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.Result is ObjectResult objectResult)
|
||||
{
|
||||
var resultContent = System.Text.Json.JsonSerializer.Serialize(objectResult.Value, jsonSerializerOptions);
|
||||
var endpoint = (RouteEndpoint)context.HttpContext.GetEndpoint();
|
||||
var routePattern = endpoint?.RoutePattern.RawText;
|
||||
|
||||
var eventName = $"method: {method}, route: {routePattern}";
|
||||
if (routePattern == null)
|
||||
{
|
||||
base.OnResultExecuted(context);
|
||||
|
||||
WebhookPublisher.Publish(eventName, resultContent);
|
||||
}
|
||||
|
||||
base.OnResultExecuted(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.Result is ObjectResult objectResult)
|
||||
{
|
||||
var resultContent = JsonSerializer.Serialize(objectResult.Value, _jsonSerializerOptions);
|
||||
|
||||
var eventName = $"method: {method}, route: {routePattern}";
|
||||
|
||||
_webhookPublisher.Publish(eventName, resultContent);
|
||||
}
|
||||
|
||||
base.OnResultExecuted(context);
|
||||
}
|
||||
}
|
@ -23,28 +23,24 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Web.Api.Models
|
||||
namespace ASC.Web.Api.Models;
|
||||
|
||||
public class Contact
|
||||
{
|
||||
public class Contact
|
||||
public string Type { get; set; }
|
||||
public string Value { get; set; }
|
||||
|
||||
//For binder
|
||||
public Contact() { }
|
||||
|
||||
public Contact(string type, string value)
|
||||
{
|
||||
public string Type { get; set; }
|
||||
Type = type;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public string Value { get; set; }
|
||||
|
||||
public Contact()
|
||||
{
|
||||
//For binder
|
||||
}
|
||||
|
||||
public Contact(string type, string value)
|
||||
{
|
||||
Type = type;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static Contact GetSample()
|
||||
{
|
||||
return new Contact("GTalk", "my@gmail.com");
|
||||
}
|
||||
public static Contact GetSample()
|
||||
{
|
||||
return new Contact("GTalk", "my@gmail.com");
|
||||
}
|
||||
}
|
@ -23,95 +23,94 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Web.Api.Models
|
||||
namespace ASC.Web.Api.Models;
|
||||
|
||||
public class EmployeeWraper
|
||||
{
|
||||
public class EmployeeWraper
|
||||
public Guid Id { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string AvatarSmall { get; set; }
|
||||
public string ProfileUrl { get; set; }
|
||||
|
||||
public static EmployeeWraper GetSample()
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public string AvatarSmall { get; set; }
|
||||
|
||||
public string ProfileUrl { get; set; }
|
||||
|
||||
public static EmployeeWraper GetSample()
|
||||
return new EmployeeWraper
|
||||
{
|
||||
return new EmployeeWraper
|
||||
{
|
||||
Id = Guid.Empty,
|
||||
DisplayName = "Mike Zanyatski",
|
||||
Title = "Manager",
|
||||
AvatarSmall = "url to small avatar",
|
||||
};
|
||||
Id = Guid.Empty,
|
||||
DisplayName = "Mike Zanyatski",
|
||||
Title = "Manager",
|
||||
AvatarSmall = "url to small avatar",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Scope]
|
||||
public class EmployeeWraperHelper
|
||||
{
|
||||
protected UserPhotoManager UserPhotoManager { get; }
|
||||
protected UserManager UserManager { get; }
|
||||
|
||||
private readonly ApiContext _httpContext;
|
||||
private readonly DisplayUserSettingsHelper _displayUserSettingsHelper;
|
||||
private readonly CommonLinkUtility _commonLinkUtility;
|
||||
|
||||
public EmployeeWraperHelper(
|
||||
ApiContext httpContext,
|
||||
DisplayUserSettingsHelper displayUserSettingsHelper,
|
||||
UserPhotoManager userPhotoManager,
|
||||
CommonLinkUtility commonLinkUtility,
|
||||
UserManager userManager)
|
||||
{
|
||||
UserPhotoManager = userPhotoManager;
|
||||
UserManager = userManager;
|
||||
_httpContext = httpContext;
|
||||
_displayUserSettingsHelper = displayUserSettingsHelper;
|
||||
_commonLinkUtility = commonLinkUtility;
|
||||
|
||||
}
|
||||
|
||||
public EmployeeWraper Get(UserInfo userInfo)
|
||||
{
|
||||
return Init(new EmployeeWraper(), userInfo);
|
||||
}
|
||||
|
||||
public EmployeeWraper Get(Guid userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Get(UserManager.GetUsers(userId));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return Get(Constants.LostUser);
|
||||
}
|
||||
}
|
||||
|
||||
[Scope]
|
||||
public class EmployeeWraperHelper
|
||||
protected EmployeeWraper Init(EmployeeWraper result, UserInfo userInfo)
|
||||
{
|
||||
private ApiContext HttpContext { get; }
|
||||
private DisplayUserSettingsHelper DisplayUserSettingsHelper { get; }
|
||||
protected UserPhotoManager UserPhotoManager { get; }
|
||||
private CommonLinkUtility CommonLinkUtility { get; }
|
||||
protected UserManager UserManager { get; }
|
||||
result.Id = userInfo.ID;
|
||||
result.DisplayName = _displayUserSettingsHelper.GetFullUserName(userInfo);
|
||||
|
||||
public EmployeeWraperHelper(
|
||||
ApiContext httpContext,
|
||||
DisplayUserSettingsHelper displayUserSettingsHelper,
|
||||
UserPhotoManager userPhotoManager,
|
||||
CommonLinkUtility commonLinkUtility,
|
||||
UserManager userManager)
|
||||
if (!string.IsNullOrEmpty(userInfo.Title))
|
||||
{
|
||||
HttpContext = httpContext;
|
||||
DisplayUserSettingsHelper = displayUserSettingsHelper;
|
||||
UserPhotoManager = userPhotoManager;
|
||||
CommonLinkUtility = commonLinkUtility;
|
||||
UserManager = userManager;
|
||||
result.Title = userInfo.Title;
|
||||
}
|
||||
|
||||
public EmployeeWraper Get(UserInfo userInfo)
|
||||
var userInfoLM = userInfo.LastModified.GetHashCode();
|
||||
|
||||
if (_httpContext.Check("avatarSmall"))
|
||||
{
|
||||
return Init(new EmployeeWraper(), userInfo);
|
||||
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);
|
||||
}
|
||||
|
||||
public EmployeeWraper Get(Guid userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Get(UserManager.GetUsers(userId));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return Get(Constants.LostUser);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -23,254 +23,235 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Web.Api.Models
|
||||
namespace ASC.Web.Api.Models;
|
||||
|
||||
public class EmployeeWraperFull : EmployeeWraper
|
||||
{
|
||||
public class EmployeeWraperFull : EmployeeWraper
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string Email { get; set; }
|
||||
public List<Contact> Contacts { get; set; }
|
||||
public ApiDateTime Birthday { get; set; }
|
||||
public string Sex { get; set; }
|
||||
public EmployeeStatus Status { get; set; }
|
||||
public EmployeeActivationStatus ActivationStatus { get; set; }
|
||||
public ApiDateTime Terminated { get; set; }
|
||||
public string Department { get; set; }
|
||||
public ApiDateTime WorkFrom { get; set; }
|
||||
public List<GroupWrapperSummary> Groups { get; set; }
|
||||
public string Location { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public string AvatarMax { get; set; }
|
||||
public string AvatarMedium { get; set; }
|
||||
public string Avatar { get; set; }
|
||||
public bool IsAdmin { get; set; }
|
||||
public bool IsLDAP { get; set; }
|
||||
public List<string> ListAdminModules { get; set; }
|
||||
public bool IsOwner { get; set; }
|
||||
public bool IsVisitor { get; set; }
|
||||
public string CultureName { get; set; }
|
||||
public string MobilePhone { get; set; }
|
||||
public MobilePhoneActivationStatus MobilePhoneActivationStatus { get; set; }
|
||||
public bool IsSSO { get; set; }
|
||||
|
||||
public new static EmployeeWraperFull GetSample()
|
||||
{
|
||||
public string FirstName { get; set; }
|
||||
|
||||
public string LastName { get; set; }
|
||||
|
||||
public string UserName { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
|
||||
public List<Contact> Contacts { get; set; }
|
||||
|
||||
public ApiDateTime Birthday { get; set; }
|
||||
|
||||
public string Sex { get; set; }
|
||||
|
||||
public EmployeeStatus Status { get; set; }
|
||||
|
||||
public EmployeeActivationStatus ActivationStatus { get; set; }
|
||||
|
||||
public ApiDateTime Terminated { get; set; }
|
||||
|
||||
public string Department { get; set; }
|
||||
|
||||
public ApiDateTime WorkFrom { get; set; }
|
||||
|
||||
public List<GroupWrapperSummary> Groups { get; set; }
|
||||
|
||||
public string Location { get; set; }
|
||||
|
||||
public string Notes { get; set; }
|
||||
|
||||
public string AvatarMax { get; set; }
|
||||
|
||||
public string AvatarMedium { get; set; }
|
||||
|
||||
public string Avatar { get; set; }
|
||||
|
||||
public bool IsAdmin { get; set; }
|
||||
|
||||
public bool IsLDAP { get; set; }
|
||||
|
||||
public List<string> ListAdminModules { get; set; }
|
||||
|
||||
public bool IsOwner { get; set; }
|
||||
|
||||
public bool IsVisitor { get; set; }
|
||||
|
||||
public string CultureName { get; set; }
|
||||
|
||||
|
||||
public string MobilePhone { get; set; }
|
||||
|
||||
public MobilePhoneActivationStatus MobilePhoneActivationStatus { get; set; }
|
||||
|
||||
public bool IsSSO { get; set; }
|
||||
|
||||
public new static EmployeeWraperFull GetSample()
|
||||
return new EmployeeWraperFull
|
||||
{
|
||||
return new EmployeeWraperFull
|
||||
{
|
||||
Avatar = "url to big avatar",
|
||||
AvatarSmall = "url to small avatar",
|
||||
AvatarMax = "url to max avatar",
|
||||
Contacts = new List<Contact> { Contact.GetSample() },
|
||||
Email = "my@gmail.com",
|
||||
FirstName = "Mike",
|
||||
Id = Guid.Empty,
|
||||
IsAdmin = false,
|
||||
ListAdminModules = new List<string> { "projects", "crm" },
|
||||
UserName = "Mike.Zanyatski",
|
||||
LastName = "Zanyatski",
|
||||
Title = "Manager",
|
||||
Groups = new List<GroupWrapperSummary> { GroupWrapperSummary.GetSample() },
|
||||
AvatarMedium = "url to medium avatar",
|
||||
Birthday = ApiDateTime.GetSample(),
|
||||
Department = "Marketing",
|
||||
Location = "Palo Alto",
|
||||
Notes = "Notes to worker",
|
||||
Sex = "male",
|
||||
Status = EmployeeStatus.Active,
|
||||
WorkFrom = ApiDateTime.GetSample(),
|
||||
Terminated = ApiDateTime.GetSample(),
|
||||
CultureName = "en-EN",
|
||||
IsLDAP = false,
|
||||
IsSSO = false
|
||||
};
|
||||
}
|
||||
Avatar = "url to big avatar",
|
||||
AvatarSmall = "url to small avatar",
|
||||
AvatarMax = "url to max avatar",
|
||||
Contacts = new List<Contact> { Contact.GetSample() },
|
||||
Email = "my@gmail.com",
|
||||
FirstName = "Mike",
|
||||
Id = Guid.Empty,
|
||||
IsAdmin = false,
|
||||
ListAdminModules = new List<string> { "projects", "crm" },
|
||||
UserName = "Mike.Zanyatski",
|
||||
LastName = "Zanyatski",
|
||||
Title = "Manager",
|
||||
Groups = new List<GroupWrapperSummary> { GroupWrapperSummary.GetSample() },
|
||||
AvatarMedium = "url to medium avatar",
|
||||
Birthday = ApiDateTime.GetSample(),
|
||||
Department = "Marketing",
|
||||
Location = "Palo Alto",
|
||||
Notes = "Notes to worker",
|
||||
Sex = "male",
|
||||
Status = EmployeeStatus.Active,
|
||||
WorkFrom = ApiDateTime.GetSample(),
|
||||
Terminated = ApiDateTime.GetSample(),
|
||||
CultureName = "en-EN",
|
||||
IsLDAP = false,
|
||||
IsSSO = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Scope]
|
||||
public class EmployeeWraperFullHelper : EmployeeWraperHelper
|
||||
{
|
||||
private readonly ApiContext _context;
|
||||
private readonly WebItemSecurity _webItemSecurity;
|
||||
private readonly ApiDateTimeHelper _apiDateTimeHelper;
|
||||
|
||||
public EmployeeWraperFullHelper(
|
||||
ApiContext context,
|
||||
UserManager userManager,
|
||||
UserPhotoManager userPhotoManager,
|
||||
WebItemSecurity webItemSecurity,
|
||||
CommonLinkUtility commonLinkUtility,
|
||||
DisplayUserSettingsHelper displayUserSettingsHelper,
|
||||
ApiDateTimeHelper apiDateTimeHelper)
|
||||
: base(context, displayUserSettingsHelper, userPhotoManager, commonLinkUtility, userManager)
|
||||
{
|
||||
_context = context;
|
||||
_webItemSecurity = webItemSecurity;
|
||||
_apiDateTimeHelper = apiDateTimeHelper;
|
||||
}
|
||||
|
||||
[Scope]
|
||||
public class EmployeeWraperFullHelper : EmployeeWraperHelper
|
||||
public static Expression<Func<User, UserInfo>> GetExpression(ApiContext apiContext)
|
||||
{
|
||||
private ApiContext Context { get; }
|
||||
private WebItemSecurity WebItemSecurity { get; }
|
||||
private ApiDateTimeHelper ApiDateTimeHelper { get; }
|
||||
|
||||
public EmployeeWraperFullHelper(
|
||||
ApiContext context,
|
||||
UserManager userManager,
|
||||
UserPhotoManager userPhotoManager,
|
||||
WebItemSecurity webItemSecurity,
|
||||
CommonLinkUtility commonLinkUtility,
|
||||
DisplayUserSettingsHelper displayUserSettingsHelper,
|
||||
ApiDateTimeHelper apiDateTimeHelper)
|
||||
: base(context, displayUserSettingsHelper, userPhotoManager, commonLinkUtility, userManager)
|
||||
if (apiContext?.Fields == null)
|
||||
{
|
||||
Context = context;
|
||||
WebItemSecurity = webItemSecurity;
|
||||
ApiDateTimeHelper = apiDateTimeHelper;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Expression<Func<User, UserInfo>> GetExpression(ApiContext apiContext)
|
||||
var newExpr = Expression.New(typeof(UserInfo));
|
||||
|
||||
//i => new UserInfo { ID = i.id }
|
||||
var parameter = Expression.Parameter(typeof(User), "i");
|
||||
var bindExprs = new List<MemberAssignment>();
|
||||
|
||||
if (apiContext.Check("Id"))
|
||||
{
|
||||
if (apiContext?.Fields == null) return null;
|
||||
var newExpr = Expression.New(typeof(UserInfo));
|
||||
|
||||
//i => new UserInfo { ID = i.id }
|
||||
var parameter = Expression.Parameter(typeof(User), "i");
|
||||
var bindExprs = new List<MemberAssignment>();
|
||||
|
||||
if (apiContext.Check("Id"))
|
||||
{
|
||||
bindExprs.Add(Expression.Bind(typeof(UserInfo).GetProperty("ID"), Expression.Property(parameter, typeof(User).GetProperty("Id"))));
|
||||
}
|
||||
|
||||
var body = Expression.MemberInit(newExpr, bindExprs);
|
||||
var lambda = Expression.Lambda<Func<User, UserInfo>>(body, parameter);
|
||||
|
||||
return lambda;
|
||||
bindExprs.Add(Expression.Bind(typeof(UserInfo).GetProperty("ID"),
|
||||
Expression.Property(parameter, typeof(User).GetProperty("Id"))));
|
||||
}
|
||||
|
||||
public EmployeeWraperFull GetFull(UserInfo userInfo)
|
||||
var body = Expression.MemberInit(newExpr, bindExprs);
|
||||
var lambda = Expression.Lambda<Func<User, UserInfo>>(body, parameter);
|
||||
|
||||
return lambda;
|
||||
}
|
||||
|
||||
public EmployeeWraperFull GetFull(UserInfo userInfo)
|
||||
{
|
||||
var result = new EmployeeWraperFull
|
||||
{
|
||||
var result = new EmployeeWraperFull
|
||||
{
|
||||
UserName = userInfo.UserName,
|
||||
FirstName = userInfo.FirstName,
|
||||
LastName = userInfo.LastName,
|
||||
Birthday = ApiDateTimeHelper.Get(userInfo.BirthDate),
|
||||
Status = userInfo.Status,
|
||||
ActivationStatus = userInfo.ActivationStatus & ~EmployeeActivationStatus.AutoGenerated,
|
||||
Terminated = ApiDateTimeHelper.Get(userInfo.TerminatedDate),
|
||||
WorkFrom = ApiDateTimeHelper.Get(userInfo.WorkFromDate),
|
||||
Email = userInfo.Email,
|
||||
IsVisitor = userInfo.IsVisitor(UserManager),
|
||||
IsAdmin = userInfo.IsAdmin(UserManager),
|
||||
IsOwner = userInfo.IsOwner(Context.Tenant),
|
||||
IsLDAP = userInfo.IsLDAP(),
|
||||
IsSSO = userInfo.IsSSO()
|
||||
};
|
||||
UserName = userInfo.UserName,
|
||||
FirstName = userInfo.FirstName,
|
||||
LastName = userInfo.LastName,
|
||||
Birthday = _apiDateTimeHelper.Get(userInfo.BirthDate),
|
||||
Status = userInfo.Status,
|
||||
ActivationStatus = userInfo.ActivationStatus & ~EmployeeActivationStatus.AutoGenerated,
|
||||
Terminated = _apiDateTimeHelper.Get(userInfo.TerminatedDate),
|
||||
WorkFrom = _apiDateTimeHelper.Get(userInfo.WorkFromDate),
|
||||
Email = userInfo.Email,
|
||||
IsVisitor = userInfo.IsVisitor(UserManager),
|
||||
IsAdmin = userInfo.IsAdmin(UserManager),
|
||||
IsOwner = userInfo.IsOwner(_context.Tenant),
|
||||
IsLDAP = userInfo.IsLDAP(),
|
||||
IsSSO = userInfo.IsSSO()
|
||||
};
|
||||
|
||||
Init(result, userInfo);
|
||||
Init(result, userInfo);
|
||||
|
||||
if (userInfo.Sex.HasValue)
|
||||
{
|
||||
result.Sex = userInfo.Sex.Value ? "male" : "female";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(userInfo.Location))
|
||||
{
|
||||
result.Location = userInfo.Location;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(userInfo.Notes))
|
||||
{
|
||||
result.Notes = userInfo.Notes;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(userInfo.MobilePhone))
|
||||
{
|
||||
result.MobilePhone = userInfo.MobilePhone;
|
||||
}
|
||||
|
||||
result.MobilePhoneActivationStatus = userInfo.MobilePhoneActivationStatus;
|
||||
|
||||
if (!string.IsNullOrEmpty(userInfo.CultureName))
|
||||
{
|
||||
result.CultureName = userInfo.CultureName;
|
||||
}
|
||||
|
||||
FillConacts(result, userInfo);
|
||||
|
||||
if (Context.Check("groups") || Context.Check("department"))
|
||||
{
|
||||
var groups = UserManager.GetUserGroups(userInfo.ID)
|
||||
.Select(x => new GroupWrapperSummary(x, UserManager))
|
||||
.ToList();
|
||||
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
result.Groups = groups;
|
||||
result.Department = string.Join(", ", result.Groups.Select(d => d.Name.HtmlEncode()));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Department = "";
|
||||
}
|
||||
}
|
||||
|
||||
var userInfoLM = userInfo.LastModified.GetHashCode();
|
||||
|
||||
if (Context.Check("avatarMax"))
|
||||
{
|
||||
result.AvatarMax = UserPhotoManager.GetMaxPhotoURL(userInfo.ID, out var isdef) + (isdef ? "" : $"?_={userInfoLM}");
|
||||
}
|
||||
|
||||
if (Context.Check("avatarMedium"))
|
||||
{
|
||||
result.AvatarMedium = UserPhotoManager.GetMediumPhotoURL(userInfo.ID, out var isdef) + (isdef ? "" : $"?_={userInfoLM}");
|
||||
}
|
||||
|
||||
if (Context.Check("avatar"))
|
||||
{
|
||||
result.Avatar = UserPhotoManager.GetBigPhotoURL(userInfo.ID, out var isdef) + (isdef ? "" : $"?_={userInfoLM}");
|
||||
}
|
||||
|
||||
if (Context.Check("listAdminModules"))
|
||||
{
|
||||
var listAdminModules = userInfo.GetListAdminModules(WebItemSecurity);
|
||||
|
||||
if (listAdminModules.Any())
|
||||
result.ListAdminModules = listAdminModules;
|
||||
}
|
||||
|
||||
return result;
|
||||
if (userInfo.Sex.HasValue)
|
||||
{
|
||||
result.Sex = userInfo.Sex.Value ? "male" : "female";
|
||||
}
|
||||
|
||||
private void FillConacts(EmployeeWraperFull employeeWraperFull, UserInfo userInfo)
|
||||
if (!string.IsNullOrEmpty(userInfo.Location))
|
||||
{
|
||||
if (userInfo.ContactsList == null) return;
|
||||
result.Location = userInfo.Location;
|
||||
}
|
||||
|
||||
var contacts = new List<Contact>();
|
||||
if (!string.IsNullOrEmpty(userInfo.Notes))
|
||||
{
|
||||
result.Notes = userInfo.Notes;
|
||||
}
|
||||
|
||||
for (var i = 0; i < userInfo.ContactsList.Count; i += 2)
|
||||
if (!string.IsNullOrEmpty(userInfo.MobilePhone))
|
||||
{
|
||||
result.MobilePhone = userInfo.MobilePhone;
|
||||
}
|
||||
|
||||
result.MobilePhoneActivationStatus = userInfo.MobilePhoneActivationStatus;
|
||||
|
||||
if (!string.IsNullOrEmpty(userInfo.CultureName))
|
||||
{
|
||||
result.CultureName = userInfo.CultureName;
|
||||
}
|
||||
|
||||
FillConacts(result, userInfo);
|
||||
|
||||
if (_context.Check("groups") || _context.Check("department"))
|
||||
{
|
||||
var groups = UserManager.GetUserGroups(userInfo.ID)
|
||||
.Select(x => new GroupWrapperSummary(x, UserManager))
|
||||
.ToList();
|
||||
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
if (i + 1 < userInfo.ContactsList.Count)
|
||||
{
|
||||
contacts.Add(new Contact(userInfo.ContactsList[i], userInfo.ContactsList[i + 1]));
|
||||
}
|
||||
result.Groups = groups;
|
||||
result.Department = string.Join(", ", result.Groups.Select(d => d.Name.HtmlEncode()));
|
||||
}
|
||||
|
||||
if (contacts.Any())
|
||||
else
|
||||
{
|
||||
employeeWraperFull.Contacts = contacts;
|
||||
result.Department = "";
|
||||
}
|
||||
}
|
||||
|
||||
var userInfoLM = userInfo.LastModified.GetHashCode();
|
||||
|
||||
if (_context.Check("avatarMax"))
|
||||
{
|
||||
result.AvatarMax = UserPhotoManager.GetMaxPhotoURL(userInfo.ID, out var isdef) + (isdef ? "" : $"?_={userInfoLM}");
|
||||
}
|
||||
|
||||
if (_context.Check("avatarMedium"))
|
||||
{
|
||||
result.AvatarMedium = UserPhotoManager.GetMediumPhotoURL(userInfo.ID, out var isdef) + (isdef ? "" : $"?_={userInfoLM}");
|
||||
}
|
||||
|
||||
if (_context.Check("avatar"))
|
||||
{
|
||||
result.Avatar = UserPhotoManager.GetBigPhotoURL(userInfo.ID, out var isdef) + (isdef ? "" : $"?_={userInfoLM}");
|
||||
}
|
||||
|
||||
if (_context.Check("listAdminModules"))
|
||||
{
|
||||
var listAdminModules = userInfo.GetListAdminModules(_webItemSecurity);
|
||||
if (listAdminModules.Count > 0)
|
||||
{
|
||||
result.ListAdminModules = listAdminModules;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void FillConacts(EmployeeWraperFull employeeWraperFull, UserInfo userInfo)
|
||||
{
|
||||
if (userInfo.ContactsList == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var contacts = new List<Contact>();
|
||||
|
||||
for (var i = 0; i < userInfo.ContactsList.Count; i += 2)
|
||||
{
|
||||
if (i + 1 < userInfo.ContactsList.Count)
|
||||
{
|
||||
contacts.Add(new Contact(userInfo.ContactsList[i], userInfo.ContactsList[i + 1]));
|
||||
}
|
||||
}
|
||||
|
||||
if (contacts.Count > 0)
|
||||
{
|
||||
employeeWraperFull.Contacts = contacts;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,31 +23,32 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Web.Api.Models
|
||||
using GroupInfo = ASC.Core.Users.GroupInfo;
|
||||
|
||||
namespace ASC.Web.Api.Models;
|
||||
|
||||
public class GroupWrapperSummary
|
||||
{
|
||||
public class GroupWrapperSummary
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Manager { get; set; }
|
||||
|
||||
protected GroupWrapperSummary() { }
|
||||
|
||||
public GroupWrapperSummary(GroupInfo group, UserManager userManager)
|
||||
{
|
||||
public GroupWrapperSummary(ASC.Core.Users.GroupInfo group, UserManager userManager)
|
||||
Id = group.ID;
|
||||
Name = group.Name;
|
||||
Manager = userManager.GetUsers(userManager.GetDepartmentManager(group.ID)).UserName;
|
||||
}
|
||||
|
||||
public static GroupWrapperSummary GetSample()
|
||||
{
|
||||
return new GroupWrapperSummary
|
||||
{
|
||||
Id = group.ID;
|
||||
Name = group.Name;
|
||||
Manager = userManager.GetUsers(userManager.GetDepartmentManager(group.ID)).UserName;
|
||||
}
|
||||
|
||||
protected GroupWrapperSummary()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public string Manager { get; set; }
|
||||
|
||||
public static GroupWrapperSummary GetSample()
|
||||
{
|
||||
return new GroupWrapperSummary { Id = Guid.Empty, Manager = "Jake.Zazhitski", Name = "Group Name" };
|
||||
}
|
||||
Id = Guid.Empty,
|
||||
Manager = "Jake.Zazhitski",
|
||||
Name = "Group Name"
|
||||
};
|
||||
}
|
||||
}
|
@ -1,16 +1,13 @@
|
||||
namespace ASC.Web.Api.Routing
|
||||
namespace ASC.Web.Api.Routing;
|
||||
|
||||
public class DefaultRouteAttribute : RouteAttribute
|
||||
{
|
||||
public class DefaultRouteAttribute : RouteAttribute
|
||||
public static string BaseUrl { get; set; }
|
||||
|
||||
static DefaultRouteAttribute()
|
||||
{
|
||||
public static string BaseUrl { get; set; }
|
||||
|
||||
static DefaultRouteAttribute()
|
||||
{
|
||||
BaseUrl = "api/2.0";
|
||||
}
|
||||
|
||||
public DefaultRouteAttribute() : base(BaseUrl)
|
||||
{
|
||||
}
|
||||
BaseUrl = "api/2.0";
|
||||
}
|
||||
}
|
||||
|
||||
public DefaultRouteAttribute() : base(BaseUrl) { }
|
||||
}
|
@ -1,53 +1,55 @@
|
||||
namespace ASC.Web.Api.Routing
|
||||
namespace ASC.Web.Api.Routing;
|
||||
|
||||
public abstract class CustomHttpMethodAttribute : HttpMethodAttribute
|
||||
{
|
||||
public abstract class CustomHttpMethodAttribute : HttpMethodAttribute
|
||||
{
|
||||
public bool Check { get; set; }
|
||||
public bool DisableFormat { get; set; }
|
||||
public bool Check { get; set; }
|
||||
public bool DisableFormat { get; set; }
|
||||
|
||||
public CustomHttpMethodAttribute(string method, string template = null, bool check = true, int order = 1)
|
||||
: base(new List<string>() { method }, $"[controller]{(template != null ? $"/{template}" : "")}")
|
||||
{
|
||||
Check = check;
|
||||
Order = order;
|
||||
}
|
||||
}
|
||||
|
||||
public class ReadAttribute : CustomHttpMethodAttribute
|
||||
protected CustomHttpMethodAttribute(string method, string template = null, bool check = true, int order = 1)
|
||||
: base(new List<string>() { method }, $"[controller]{(template != null ? $"/{template}" : "")}")
|
||||
{
|
||||
public ReadAttribute(bool check = true, int order = 1) :
|
||||
this(null, check, order)
|
||||
{ }
|
||||
|
||||
public ReadAttribute(string template, bool check = true, int order = 1) :
|
||||
base(HttpMethod.Get.Method, template, check, order)
|
||||
{ }
|
||||
}
|
||||
public class CreateAttribute : CustomHttpMethodAttribute
|
||||
{
|
||||
public CreateAttribute(bool check = true, int order = 1) :
|
||||
this(null, check, order)
|
||||
{ }
|
||||
public CreateAttribute(string template, bool check = true, int order = 1) :
|
||||
base(HttpMethod.Post.Method, template, check, order)
|
||||
{ }
|
||||
}
|
||||
public class UpdateAttribute : CustomHttpMethodAttribute
|
||||
{
|
||||
public UpdateAttribute(bool check = true, int order = 1) :
|
||||
this(null, check, order)
|
||||
{ }
|
||||
public UpdateAttribute(string template, bool check = true, int order = 1) :
|
||||
base(HttpMethod.Put.Method, template, check, order)
|
||||
{ }
|
||||
}
|
||||
public class DeleteAttribute : CustomHttpMethodAttribute
|
||||
{
|
||||
public DeleteAttribute(bool check = true, int order = 1) :
|
||||
this(null, check, order)
|
||||
{ }
|
||||
public DeleteAttribute(string template, bool check = true, int order = 1) :
|
||||
base(HttpMethod.Delete.Method, template, check, order)
|
||||
{ }
|
||||
Check = check;
|
||||
Order = order;
|
||||
}
|
||||
}
|
||||
|
||||
public class ReadAttribute : CustomHttpMethodAttribute
|
||||
{
|
||||
public ReadAttribute(bool check = true, int order = 1) :
|
||||
this(null, check, order)
|
||||
{ }
|
||||
|
||||
public ReadAttribute(string template, bool check = true, int order = 1) :
|
||||
base(HttpMethod.Get.Method, template, check, order)
|
||||
{ }
|
||||
}
|
||||
public class CreateAttribute : CustomHttpMethodAttribute
|
||||
{
|
||||
public CreateAttribute(bool check = true, int order = 1) :
|
||||
this(null, check, order)
|
||||
{ }
|
||||
|
||||
public CreateAttribute(string template, bool check = true, int order = 1) :
|
||||
base(HttpMethod.Post.Method, template, check, order)
|
||||
{ }
|
||||
}
|
||||
public class UpdateAttribute : CustomHttpMethodAttribute
|
||||
{
|
||||
public UpdateAttribute(bool check = true, int order = 1) :
|
||||
this(null, check, order)
|
||||
{ }
|
||||
|
||||
public UpdateAttribute(string template, bool check = true, int order = 1) :
|
||||
base(HttpMethod.Put.Method, template, check, order)
|
||||
{ }
|
||||
}
|
||||
public class DeleteAttribute : CustomHttpMethodAttribute
|
||||
{
|
||||
public DeleteAttribute(bool check = true, int order = 1) :
|
||||
this(null, check, order)
|
||||
{ }
|
||||
|
||||
public DeleteAttribute(string template, bool check = true, int order = 1) :
|
||||
base(HttpMethod.Delete.Method, template, check, order)
|
||||
{ }
|
||||
}
|
@ -24,9 +24,6 @@
|
||||
<EmbeddedResource Include="Utils\TimeZoneConverter\windowsZones.xml" />
|
||||
<EmbeddedResource Include="Utils\TimeZoneConverter\timeZoneNames.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Notify\AWSEmail.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ARSoft.Tools.NetStandard.DXSdata" Version="1.0.0" />
|
||||
<PackageReference Include="Autofac.Configuration" Version="6.0.0" />
|
||||
@ -64,7 +61,4 @@
|
||||
<Protobuf Include="protos\distributed_task_cache.proto" />
|
||||
<Protobuf Include="protos\distributed_task_cancelation.proto" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Notify\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -23,129 +23,125 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Caching
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
[Singletone]
|
||||
public class AscCacheNotify
|
||||
{
|
||||
[Singletone]
|
||||
public class AscCacheNotify
|
||||
private readonly ICacheNotify<AscCacheItem> _cacheNotify;
|
||||
|
||||
public AscCacheNotify(ICacheNotify<AscCacheItem> cacheNotify)
|
||||
{
|
||||
private ICacheNotify<AscCacheItem> CacheNotify { get; }
|
||||
_cacheNotify = cacheNotify;
|
||||
|
||||
public AscCacheNotify(ICacheNotify<AscCacheItem> cacheNotify)
|
||||
_cacheNotify.Subscribe((item) => { OnClearCache(); }, CacheNotifyAction.Any);
|
||||
}
|
||||
|
||||
public void ClearCache() => _cacheNotify.Publish(new AscCacheItem { Id = Guid.NewGuid().ToString() }, CacheNotifyAction.Any);
|
||||
|
||||
public static void OnClearCache()
|
||||
{
|
||||
var keys = MemoryCache.Default.Select(r => r.Key);
|
||||
|
||||
foreach (var k in keys)
|
||||
{
|
||||
CacheNotify = cacheNotify;
|
||||
|
||||
CacheNotify.Subscribe((item) => { OnClearCache(); }, CacheNotifyAction.Any);
|
||||
MemoryCache.Default.Remove(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearCache()
|
||||
[Singletone]
|
||||
public class AscCache : ICache
|
||||
{
|
||||
private readonly IMemoryCache _memoryCache;
|
||||
private readonly ConcurrentDictionary<string, object> _memoryCacheKeys;
|
||||
|
||||
public AscCache(IMemoryCache memoryCache)
|
||||
{
|
||||
_memoryCache = memoryCache;
|
||||
_memoryCacheKeys = new ConcurrentDictionary<string, object>();
|
||||
}
|
||||
|
||||
public T Get<T>(string key) where T : class
|
||||
{
|
||||
return _memoryCache.Get<T>(key);
|
||||
}
|
||||
|
||||
public void Insert(string key, object value, TimeSpan sligingExpiration)
|
||||
{
|
||||
var options = new MemoryCacheEntryOptions()
|
||||
.SetSlidingExpiration(sligingExpiration)
|
||||
.RegisterPostEvictionCallback(EvictionCallback);
|
||||
|
||||
_memoryCache.Set(key, value, options);
|
||||
_memoryCacheKeys.TryAdd(key, null);
|
||||
}
|
||||
|
||||
public void Insert(string key, object value, DateTime absolutExpiration)
|
||||
{
|
||||
var options = new MemoryCacheEntryOptions()
|
||||
.SetAbsoluteExpiration(absolutExpiration == DateTime.MaxValue ? DateTimeOffset.MaxValue : new DateTimeOffset(absolutExpiration))
|
||||
.RegisterPostEvictionCallback(EvictionCallback);
|
||||
|
||||
_memoryCache.Set(key, value, options);
|
||||
_memoryCacheKeys.TryAdd(key, null);
|
||||
}
|
||||
|
||||
public void Remove(string key)
|
||||
{
|
||||
_memoryCache.Remove(key);
|
||||
}
|
||||
|
||||
public void Remove(Regex pattern)
|
||||
{
|
||||
var copy = _memoryCacheKeys.ToDictionary(p => p.Key, p => p.Value);
|
||||
var keys = copy.Select(p => p.Key).Where(k => pattern.IsMatch(k));
|
||||
|
||||
foreach (var key in keys)
|
||||
{
|
||||
CacheNotify.Publish(new AscCacheItem { Id = Guid.NewGuid().ToString() }, CacheNotifyAction.Any);
|
||||
}
|
||||
|
||||
public static void OnClearCache()
|
||||
{
|
||||
var keys = MemoryCache.Default.Select(r => r.Key).ToList();
|
||||
|
||||
foreach (var k in keys)
|
||||
{
|
||||
MemoryCache.Default.Remove(k);
|
||||
}
|
||||
_memoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
[Singletone]
|
||||
public class AscCache : ICache
|
||||
{
|
||||
private IMemoryCache MemoryCache { get; }
|
||||
private ConcurrentDictionary<string, object> MemoryCacheKeys { get; }
|
||||
public ConcurrentDictionary<string, T> HashGetAll<T>(string key) =>
|
||||
_memoryCache.GetOrCreate(key, r => new ConcurrentDictionary<string, T>());
|
||||
|
||||
public AscCache(IMemoryCache memoryCache)
|
||||
public T HashGet<T>(string key, string field)
|
||||
{
|
||||
if (_memoryCache.TryGetValue<ConcurrentDictionary<string, T>>(key, out var dic)
|
||||
&& dic.TryGetValue(field, out var value))
|
||||
{
|
||||
MemoryCache = memoryCache;
|
||||
MemoryCacheKeys = new ConcurrentDictionary<string, object>();
|
||||
}
|
||||
|
||||
public T Get<T>(string key) where T : class
|
||||
{
|
||||
return MemoryCache.Get<T>(key);
|
||||
}
|
||||
|
||||
public void Insert(string key, object value, TimeSpan sligingExpiration)
|
||||
{
|
||||
var options = new MemoryCacheEntryOptions()
|
||||
.SetSlidingExpiration(sligingExpiration)
|
||||
.RegisterPostEvictionCallback(EvictionCallback);
|
||||
|
||||
MemoryCache.Set(key, value, options);
|
||||
MemoryCacheKeys.TryAdd(key, null);
|
||||
}
|
||||
|
||||
public void Insert(string key, object value, DateTime absolutExpiration)
|
||||
{
|
||||
var options = new MemoryCacheEntryOptions()
|
||||
.SetAbsoluteExpiration(absolutExpiration == DateTime.MaxValue ? DateTimeOffset.MaxValue : new DateTimeOffset(absolutExpiration))
|
||||
.RegisterPostEvictionCallback(EvictionCallback);
|
||||
|
||||
MemoryCache.Set(key, value, options);
|
||||
MemoryCacheKeys.TryAdd(key, null);
|
||||
return value;
|
||||
}
|
||||
|
||||
private void EvictionCallback(object key, object value, EvictionReason reason, object state)
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public void HashSet<T>(string key, string field, T value)
|
||||
{
|
||||
var dic = HashGetAll<T>(key);
|
||||
if (value != null)
|
||||
{
|
||||
MemoryCacheKeys.TryRemove(key.ToString(), out _);
|
||||
dic.AddOrUpdate(field, value, (k, v) => value);
|
||||
_memoryCache.Set(key, dic, DateTime.MaxValue);
|
||||
}
|
||||
|
||||
public void Remove(string key)
|
||||
{
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
|
||||
public void Remove(Regex pattern)
|
||||
{
|
||||
var copy = MemoryCacheKeys.ToDictionary(p => p.Key, p => p.Value);
|
||||
var keys = copy.Select(p => p.Key).Where(k => pattern.IsMatch(k)).ToArray();
|
||||
|
||||
foreach (var key in keys)
|
||||
{
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ConcurrentDictionary<string, T> HashGetAll<T>(string key)
|
||||
{
|
||||
return MemoryCache.GetOrCreate(key, r => new ConcurrentDictionary<string, T>());
|
||||
}
|
||||
|
||||
public T HashGet<T>(string key, string field)
|
||||
{
|
||||
if (MemoryCache.TryGetValue<ConcurrentDictionary<string, T>>(key, out var dic) && dic.TryGetValue(field, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
public void HashSet<T>(string key, string field, T value)
|
||||
{
|
||||
var dic = HashGetAll<T>(key);
|
||||
if (value != null)
|
||||
else if (dic != null)
|
||||
{
|
||||
dic.TryRemove(field, out _);
|
||||
|
||||
if (dic.IsEmpty)
|
||||
{
|
||||
dic.AddOrUpdate(field, value, (k, v) => value);
|
||||
MemoryCache.Set(key, dic, DateTime.MaxValue);
|
||||
}
|
||||
else if (dic != null)
|
||||
{
|
||||
dic.TryRemove(field, out _);
|
||||
if (dic.Count == 0)
|
||||
{
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryCache.Set(key, dic, DateTime.MaxValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_memoryCache.Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
_memoryCache.Set(key, dic, DateTime.MaxValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EvictionCallback(object key, object value, EvictionReason reason, object state)
|
||||
{
|
||||
_memoryCacheKeys.TryRemove(key.ToString(), out _);
|
||||
}
|
||||
}
|
@ -23,15 +23,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Caching
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
[Flags]
|
||||
public enum CacheNotifyAction
|
||||
{
|
||||
[Flags]
|
||||
public enum CacheNotifyAction
|
||||
{
|
||||
Insert = 1,
|
||||
Update = 2,
|
||||
Remove = 4,
|
||||
InsertOrUpdate = Insert | Update,
|
||||
Any = InsertOrUpdate | Remove,
|
||||
}
|
||||
}
|
||||
Insert = 1,
|
||||
Update = 2,
|
||||
Remove = 4,
|
||||
InsertOrUpdate = Insert | Update,
|
||||
Any = InsertOrUpdate | Remove,
|
||||
}
|
@ -23,26 +23,24 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Caching
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
[Singletone(typeof(AscCache))]
|
||||
public interface ICache
|
||||
{
|
||||
[Singletone(typeof(AscCache))]
|
||||
public interface ICache
|
||||
{
|
||||
T Get<T>(string key) where T : class;
|
||||
|
||||
void Insert(string key, object value, TimeSpan sligingExpiration);
|
||||
|
||||
void Insert(string key, object value, DateTime absolutExpiration);
|
||||
|
||||
void Remove(string key);
|
||||
|
||||
void Remove(Regex pattern);
|
||||
|
||||
|
||||
ConcurrentDictionary<string, T> HashGetAll<T>(string key);
|
||||
|
||||
T HashGet<T>(string key, string field);
|
||||
|
||||
void HashSet<T>(string key, string field, T value);
|
||||
}
|
||||
}
|
||||
T Get<T>(string key) where T : class;
|
||||
|
||||
void Insert(string key, object value, TimeSpan sligingExpiration);
|
||||
|
||||
void Insert(string key, object value, DateTime absolutExpiration);
|
||||
|
||||
void Remove(string key);
|
||||
|
||||
void Remove(Regex pattern);
|
||||
|
||||
ConcurrentDictionary<string, T> HashGetAll<T>(string key);
|
||||
|
||||
T HashGet<T>(string key, string field);
|
||||
|
||||
void HashSet<T>(string key, string field, T value);
|
||||
}
|
@ -23,15 +23,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Caching
|
||||
{
|
||||
[Singletone]
|
||||
public interface ICacheNotify<T> where T : IMessage<T>, new()
|
||||
{
|
||||
void Publish(T obj, CacheNotifyAction action);
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
void Subscribe(Action<T> onchange, CacheNotifyAction action);
|
||||
|
||||
void Unsubscribe(CacheNotifyAction action);
|
||||
}
|
||||
}
|
||||
[Singletone]
|
||||
public interface ICacheNotify<T> where T : IMessage<T>, new()
|
||||
{
|
||||
void Publish(T obj, CacheNotifyAction action);
|
||||
|
||||
void Subscribe(Action<T> onchange, CacheNotifyAction action);
|
||||
|
||||
void Unsubscribe(CacheNotifyAction action);
|
||||
}
|
@ -1,199 +0,0 @@
|
||||
namespace ASC.Common.Caching
|
||||
{
|
||||
[Singletone]
|
||||
public class KafkaCache<T> : IDisposable, ICacheNotify<T> where T : IMessage<T>, new()
|
||||
{
|
||||
private ClientConfig ClientConfig { get; set; }
|
||||
private AdminClientConfig AdminClientConfig { get; set; }
|
||||
private ILog Log { get; set; }
|
||||
private ConcurrentDictionary<string, CancellationTokenSource> Cts { get; set; }
|
||||
private ConcurrentDictionary<string, Action<T>> Actions { get; set; }
|
||||
private ProtobufSerializer<T> ValueSerializer { get; } = new ProtobufSerializer<T>();
|
||||
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; set; }
|
||||
private Guid Key { get; set; }
|
||||
|
||||
public KafkaCache(ConfigurationExtension configuration, IOptionsMonitor<ILog> options)
|
||||
{
|
||||
Log = options.CurrentValue;
|
||||
Cts = new ConcurrentDictionary<string, CancellationTokenSource>();
|
||||
Actions = new ConcurrentDictionary<string, Action<T>>();
|
||||
Key = Guid.NewGuid();
|
||||
|
||||
var settings = configuration.GetSetting<KafkaSettings>("kafka");
|
||||
|
||||
ClientConfig = new ClientConfig { BootstrapServers = settings.BootstrapServers };
|
||||
AdminClientConfig = new AdminClientConfig { BootstrapServers = settings.BootstrapServers };
|
||||
}
|
||||
|
||||
public void Publish(T obj, CacheNotifyAction cacheNotifyAction)
|
||||
{
|
||||
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))
|
||||
{
|
||||
onchange(obj);
|
||||
}
|
||||
|
||||
var message = new Message<AscCacheItem, T>
|
||||
{
|
||||
Value = obj,
|
||||
Key = new AscCacheItem
|
||||
{
|
||||
Id = Key.ToString()
|
||||
}
|
||||
};
|
||||
|
||||
Producer.ProduceAsync(channelName, message);
|
||||
}
|
||||
catch (ProduceException<Null, string> e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void Subscribe(Action<T> onchange, CacheNotifyAction cacheNotifyAction)
|
||||
{
|
||||
var channelName = GetChannelName(cacheNotifyAction);
|
||||
|
||||
Cts[channelName] = new CancellationTokenSource();
|
||||
Actions[channelName] = onchange;
|
||||
|
||||
void action()
|
||||
{
|
||||
var conf = new ConsumerConfig(ClientConfig)
|
||||
{
|
||||
GroupId = Guid.NewGuid().ToString()
|
||||
};
|
||||
|
||||
|
||||
using (var adminClient = new AdminClientBuilder(AdminClientConfig)
|
||||
.SetErrorHandler((_, e) => Log.Error(e))
|
||||
.Build())
|
||||
{
|
||||
try
|
||||
{
|
||||
//TODO: must add checking exist
|
||||
adminClient.CreateTopicsAsync(
|
||||
new TopicSpecification[]
|
||||
{
|
||||
new TopicSpecification
|
||||
{
|
||||
Name = channelName,
|
||||
NumPartitions = 1,
|
||||
ReplicationFactor = 1
|
||||
}
|
||||
}).Wait();
|
||||
}
|
||||
catch (AggregateException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
using var c = new ConsumerBuilder<AscCacheItem, T>(conf)
|
||||
.SetErrorHandler((_, e) => Log.Error(e))
|
||||
.SetKeyDeserializer(KeyDeserializer)
|
||||
.SetValueDeserializer(ValueDeserializer)
|
||||
.Build();
|
||||
|
||||
c.Assign(new TopicPartition(channelName, new Partition()));
|
||||
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cr = c.Consume(Cts[channelName].Token);
|
||||
if (cr != null && cr.Message != null && cr.Message.Value != null && !(new Guid(cr.Message.Key.Id)).Equals(Key) && Actions.TryGetValue(channelName, out var act))
|
||||
{
|
||||
try
|
||||
{
|
||||
act(cr.Message.Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("Kafka onmessage", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ConsumeException e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
c.Close();
|
||||
}
|
||||
}
|
||||
|
||||
var task = new Task(action, TaskCreationOptions.LongRunning);
|
||||
task.Start();
|
||||
}
|
||||
|
||||
private string GetChannelName(CacheNotifyAction cacheNotifyAction)
|
||||
{
|
||||
return $"ascchannel{cacheNotifyAction}{typeof(T).FullName}".ToLower();
|
||||
}
|
||||
|
||||
public void Unsubscribe(CacheNotifyAction action)
|
||||
{
|
||||
Cts.TryGetValue(GetChannelName(action), out var source);
|
||||
if (source != null)
|
||||
{
|
||||
source.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing && Producer != null)
|
||||
{
|
||||
Producer.Dispose();
|
||||
}
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
~KafkaCache()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class KafkaSettings
|
||||
{
|
||||
public string BootstrapServers { get; set; }
|
||||
}
|
||||
}
|
196
common/ASC.Common/Caching/KafkaCacheNotify.cs
Normal file
196
common/ASC.Common/Caching/KafkaCacheNotify.cs
Normal file
@ -0,0 +1,196 @@
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
[Singletone]
|
||||
public class KafkaCacheNotify<T> : IDisposable, ICacheNotify<T> where T : IMessage<T>, new()
|
||||
{
|
||||
private IProducer<AscCacheItem, T> _producer;
|
||||
|
||||
private bool _disposedValue = false; // To detect redundant calls
|
||||
private readonly ClientConfig _clientConfig;
|
||||
private readonly AdminClientConfig _adminClientConfig;
|
||||
private readonly ILog _logger;
|
||||
private readonly ConcurrentDictionary<string, CancellationTokenSource> _cancelationToken;
|
||||
private readonly ConcurrentDictionary<string, Action<T>> _actions;
|
||||
private readonly ProtobufSerializer<T> _valueSerializer = new ProtobufSerializer<T>();
|
||||
private readonly ProtobufDeserializer<T> _valueDeserializer = new ProtobufDeserializer<T>();
|
||||
private readonly ProtobufSerializer<AscCacheItem> _keySerializer = new ProtobufSerializer<AscCacheItem>();
|
||||
private readonly ProtobufDeserializer<AscCacheItem> _keyDeserializer = new ProtobufDeserializer<AscCacheItem>();
|
||||
private readonly Guid _key;
|
||||
|
||||
public KafkaCacheNotify(ConfigurationExtension configuration, IOptionsMonitor<ILog> options)
|
||||
{
|
||||
_logger = options.CurrentValue;
|
||||
_cancelationToken = new ConcurrentDictionary<string, CancellationTokenSource>();
|
||||
_actions = new ConcurrentDictionary<string, Action<T>>();
|
||||
_key = Guid.NewGuid();
|
||||
|
||||
var settings = configuration.GetSetting<KafkaSettings>("kafka");
|
||||
|
||||
_clientConfig = new ClientConfig { BootstrapServers = settings.BootstrapServers };
|
||||
_adminClientConfig = new AdminClientConfig { BootstrapServers = settings.BootstrapServers };
|
||||
}
|
||||
|
||||
public void Publish(T obj, CacheNotifyAction notifyAction)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_producer == null)
|
||||
{
|
||||
_producer = new ProducerBuilder<AscCacheItem, T>(new ProducerConfig(_clientConfig))
|
||||
.SetErrorHandler((_, e) => _logger.Error(e))
|
||||
.SetKeySerializer(_keySerializer)
|
||||
.SetValueSerializer(_valueSerializer)
|
||||
.Build();
|
||||
}
|
||||
|
||||
var channelName = GetChannelName(notifyAction);
|
||||
|
||||
if (_actions.TryGetValue(channelName, out var onchange))
|
||||
{
|
||||
onchange(obj);
|
||||
}
|
||||
|
||||
var message = new Message<AscCacheItem, T>
|
||||
{
|
||||
Value = obj,
|
||||
Key = new AscCacheItem
|
||||
{
|
||||
Id = _key.ToString()
|
||||
}
|
||||
};
|
||||
|
||||
_producer.ProduceAsync(channelName, message);
|
||||
}
|
||||
catch (ProduceException<Null, string> e)
|
||||
{
|
||||
_logger.Error(e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void Subscribe(Action<T> onchange, CacheNotifyAction notifyAction)
|
||||
{
|
||||
var channelName = GetChannelName(notifyAction);
|
||||
|
||||
_cancelationToken[channelName] = new CancellationTokenSource();
|
||||
_actions[channelName] = onchange;
|
||||
|
||||
void action()
|
||||
{
|
||||
var conf = new ConsumerConfig(_clientConfig)
|
||||
{
|
||||
GroupId = Guid.NewGuid().ToString()
|
||||
};
|
||||
|
||||
|
||||
using (var adminClient = new AdminClientBuilder(_adminClientConfig)
|
||||
.SetErrorHandler((_, e) => _logger.Error(e))
|
||||
.Build())
|
||||
{
|
||||
try
|
||||
{
|
||||
//TODO: must add checking exist
|
||||
adminClient.CreateTopicsAsync(
|
||||
new TopicSpecification[]
|
||||
{
|
||||
new TopicSpecification
|
||||
{
|
||||
Name = channelName,
|
||||
NumPartitions = 1,
|
||||
ReplicationFactor = 1
|
||||
}
|
||||
}).Wait();
|
||||
}
|
||||
catch (AggregateException) { }
|
||||
}
|
||||
|
||||
using var c = new ConsumerBuilder<AscCacheItem, T>(conf)
|
||||
.SetErrorHandler((_, e) => _logger.Error(e))
|
||||
.SetKeyDeserializer(_keyDeserializer)
|
||||
.SetValueDeserializer(_valueDeserializer)
|
||||
.Build();
|
||||
|
||||
c.Assign(new TopicPartition(channelName, new Partition()));
|
||||
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cr = c.Consume(_cancelationToken[channelName].Token);
|
||||
if (cr != null && cr.Message != null && cr.Message.Value != null && !(new Guid(cr.Message.Key.Id)).Equals(_key) && _actions.TryGetValue(channelName, out var act))
|
||||
{
|
||||
try
|
||||
{
|
||||
act(cr.Message.Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error("Kafka onmessage", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ConsumeException e)
|
||||
{
|
||||
_logger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
c.Close();
|
||||
}
|
||||
}
|
||||
|
||||
var task = new Task(action, TaskCreationOptions.LongRunning);
|
||||
task.Start();
|
||||
}
|
||||
|
||||
public void Unsubscribe(CacheNotifyAction notifyAction)
|
||||
{
|
||||
_cancelationToken.TryGetValue(GetChannelName(notifyAction), out var source);
|
||||
|
||||
if (source != null)
|
||||
{
|
||||
source.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing && _producer != null)
|
||||
{
|
||||
_producer.Dispose();
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
~KafkaCacheNotify()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
private string GetChannelName(CacheNotifyAction notifyAction)
|
||||
{
|
||||
return $"ascchannel{notifyAction}{typeof(T).FullName}".ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
public class KafkaSettings
|
||||
{
|
||||
public string BootstrapServers { get; set; }
|
||||
}
|
@ -1,40 +1,39 @@
|
||||
namespace ASC.Common.Caching
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
[Singletone]
|
||||
public class MemoryCacheNotify<T> : ICacheNotify<T> where T : IMessage<T>, new()
|
||||
{
|
||||
[Singletone]
|
||||
public class MemoryCacheNotify<T> : ICacheNotify<T> where T : IMessage<T>, new()
|
||||
private readonly ConcurrentDictionary<string, List<Action<T>>> _actions;
|
||||
|
||||
public MemoryCacheNotify()
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, List<Action<T>>> _actions;
|
||||
_actions = new ConcurrentDictionary<string, List<Action<T>>>();
|
||||
}
|
||||
|
||||
public MemoryCacheNotify()
|
||||
public void Publish(T obj, CacheNotifyAction notifyAction)
|
||||
{
|
||||
if (_actions.TryGetValue(GetKey(notifyAction), out var onchange) && onchange != null)
|
||||
{
|
||||
_actions = new ConcurrentDictionary<string, List<Action<T>>>();
|
||||
}
|
||||
|
||||
public void Publish(T obj, CacheNotifyAction action)
|
||||
{
|
||||
if (_actions.TryGetValue(GetKey(action), out var onchange) && onchange != null)
|
||||
{
|
||||
Parallel.ForEach(onchange, a => a(obj));
|
||||
}
|
||||
}
|
||||
|
||||
public void Subscribe(Action<T> onchange, CacheNotifyAction notifyAction)
|
||||
{
|
||||
if (onchange != null)
|
||||
{
|
||||
_actions.GetOrAdd(GetKey(notifyAction), new List<Action<T>>())
|
||||
.Add(onchange);
|
||||
}
|
||||
}
|
||||
|
||||
public void Unsubscribe(CacheNotifyAction action)
|
||||
{
|
||||
_actions.TryRemove(GetKey(action), out _);
|
||||
}
|
||||
|
||||
private string GetKey(CacheNotifyAction cacheNotifyAction)
|
||||
{
|
||||
return $"asc:channel:{cacheNotifyAction}:{typeof(T).FullName}".ToLower();
|
||||
Parallel.ForEach(onchange, a => a(obj));
|
||||
}
|
||||
}
|
||||
|
||||
public void Subscribe(Action<T> onchange, CacheNotifyAction notifyAction)
|
||||
{
|
||||
if (onchange != null)
|
||||
{
|
||||
_actions.GetOrAdd(GetKey(notifyAction), new List<Action<T>>())
|
||||
.Add(onchange);
|
||||
}
|
||||
}
|
||||
|
||||
public void Unsubscribe(CacheNotifyAction notifyAction)
|
||||
{
|
||||
_actions.TryRemove(GetKey(notifyAction), out _);
|
||||
}
|
||||
|
||||
private string GetKey(CacheNotifyAction notifyAction)
|
||||
{
|
||||
return $"asc:channel:{notifyAction}:{typeof(T).FullName}".ToLower();
|
||||
}
|
||||
}
|
@ -1,37 +1,31 @@
|
||||
namespace ASC.Common.Caching
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
public class ProtobufSerializer<T> : ISerializer<T> where T : IMessage<T>, new()
|
||||
{
|
||||
public class ProtobufSerializer<T> : ISerializer<T> where T : IMessage<T>, new()
|
||||
public byte[] Serialize(T data, SerializationContext context)
|
||||
{
|
||||
public byte[] Serialize(T data, SerializationContext context)
|
||||
{
|
||||
return data.ToByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
public class ProtobufDeserializer<T> : IDeserializer<T> where T : IMessage<T>, new()
|
||||
{
|
||||
private readonly MessageParser<T> parser;
|
||||
|
||||
public ProtobufDeserializer()
|
||||
{
|
||||
parser = new MessageParser<T>(() => new T());
|
||||
}
|
||||
|
||||
public T Deserialize(ReadOnlySpan<byte> data, bool isNull, SerializationContext context)
|
||||
{
|
||||
return parser.ParseFrom(data.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public static class GuidExtension
|
||||
{
|
||||
public static ByteString ToByteString(this Guid id)
|
||||
{
|
||||
return ByteString.CopyFrom(id.ToByteArray());
|
||||
}
|
||||
public static Guid FromByteString(this ByteString id)
|
||||
{
|
||||
return new Guid(id.ToByteArray());
|
||||
}
|
||||
return data.ToByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
public class ProtobufDeserializer<T> : IDeserializer<T> where T : IMessage<T>, new()
|
||||
{
|
||||
private readonly MessageParser<T> _parser;
|
||||
|
||||
public ProtobufDeserializer()
|
||||
{
|
||||
_parser = new MessageParser<T>(() => new T());
|
||||
}
|
||||
|
||||
public T Deserialize(ReadOnlySpan<byte> data, bool isNull, SerializationContext context)
|
||||
{
|
||||
return _parser.ParseFrom(data.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public static class GuidExtension
|
||||
{
|
||||
public static ByteString ToByteString(this Guid id) => ByteString.CopyFrom(id.ToByteArray());
|
||||
|
||||
public static Guid FromByteString(this ByteString id) => new Guid(id.ToByteArray());
|
||||
}
|
@ -17,11 +17,11 @@
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
[Singletone]
|
||||
public class RedisCache<T> : ICacheNotify<T> where T : IMessage<T>, new()
|
||||
public class RedisCacheNotify<T> : ICacheNotify<T> where T : IMessage<T>, new()
|
||||
{
|
||||
private readonly IRedisDatabase _redis;
|
||||
|
||||
public RedisCache(IRedisCacheClient redisCacheClient)
|
||||
public RedisCacheNotify(IRedisCacheClient redisCacheClient)
|
||||
{
|
||||
_redis = redisCacheClient.GetDbFromConfiguration();
|
||||
}
|
||||
@ -53,9 +53,9 @@ public class RedisCache<T> : ICacheNotify<T> where T : IMessage<T>, new()
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
private string GetChannelName(CacheNotifyAction cacheNotifyAction)
|
||||
private string GetChannelName(CacheNotifyAction action)
|
||||
{
|
||||
return $"asc:channel:{cacheNotifyAction}:{typeof(T).FullName}".ToLower();
|
||||
return $"asc:channel:{action}:{typeof(T).FullName}".ToLower();
|
||||
}
|
||||
|
||||
class RedisCachePubSubItem<T0>
|
||||
@ -64,7 +64,4 @@ public class RedisCache<T> : ICacheNotify<T> where T : IMessage<T>, new()
|
||||
|
||||
public CacheNotifyAction Action { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -23,124 +23,120 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Collections
|
||||
namespace ASC.Collections;
|
||||
|
||||
public abstract class CachedDictionaryBase<T>
|
||||
{
|
||||
public abstract class CachedDictionaryBase<T>
|
||||
protected string BaseKey { get; set; }
|
||||
protected Func<T, bool> Condition { get; set; }
|
||||
|
||||
public T this[string key] => Get(key);
|
||||
|
||||
public T this[Func<T> @default] => Get(@default);
|
||||
|
||||
protected abstract void InsertRootKey(string rootKey);
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
protected string baseKey;
|
||||
protected Func<T, bool> condition;
|
||||
InsertRootKey(BaseKey);
|
||||
}
|
||||
|
||||
public T this[string key]
|
||||
public void Clear(string rootKey)
|
||||
{
|
||||
InsertRootKey(BuildKey(string.Empty, rootKey));
|
||||
}
|
||||
|
||||
public void Reset(string key)
|
||||
{
|
||||
Reset(string.Empty, key);
|
||||
}
|
||||
|
||||
public T Get(string key)
|
||||
{
|
||||
return Get(string.Empty, key, null);
|
||||
}
|
||||
|
||||
public T Get(string key, Func<T> defaults)
|
||||
{
|
||||
return Get(string.Empty, key, defaults);
|
||||
}
|
||||
|
||||
public void Add(string key, T newValue)
|
||||
{
|
||||
Add(string.Empty, key, newValue);
|
||||
}
|
||||
|
||||
public bool HasItem(string key)
|
||||
{
|
||||
return !Equals(Get(key), default(T));
|
||||
}
|
||||
|
||||
public T Get(Func<T> @default)
|
||||
{
|
||||
var key = string.Format("func {0} {2}.{1}({3})", @default.Method.ReturnType, @default.Method.Name,
|
||||
@default.Method.DeclaringType.FullName,
|
||||
string.Join(",",
|
||||
@default.Method.GetGenericArguments().Select(x => x.FullName).ToArray
|
||||
()));
|
||||
return Get(key, @default);
|
||||
}
|
||||
|
||||
public virtual T Get(string rootkey, string key, Func<T> defaults)
|
||||
{
|
||||
var fullKey = BuildKey(key, rootkey);
|
||||
var objectCache = GetObjectFromCache(fullKey);
|
||||
|
||||
if (FitsCondition(objectCache))
|
||||
{
|
||||
get { return Get(key); }
|
||||
OnHit(fullKey);
|
||||
|
||||
return ReturnCached(objectCache);
|
||||
}
|
||||
|
||||
public T this[Func<T> @default]
|
||||
if (defaults != null)
|
||||
{
|
||||
get { return Get(@default); }
|
||||
OnMiss(fullKey);
|
||||
var newValue = defaults();
|
||||
|
||||
if (Condition == null || Condition(newValue))
|
||||
Add(rootkey, key, newValue);
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
protected abstract void InsertRootKey(string rootKey);
|
||||
return default;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
InsertRootKey(baseKey);
|
||||
}
|
||||
public abstract void Add(string rootkey, string key, T newValue);
|
||||
|
||||
public void Clear(string rootKey)
|
||||
{
|
||||
InsertRootKey(BuildKey(string.Empty, rootKey));
|
||||
}
|
||||
public abstract void Reset(string rootKey, string key);
|
||||
|
||||
public void Reset(string key)
|
||||
{
|
||||
Reset(string.Empty, key);
|
||||
}
|
||||
protected virtual bool FitsCondition(object cached)
|
||||
{
|
||||
return cached is T;
|
||||
}
|
||||
|
||||
public abstract void Reset(string rootKey, string key);
|
||||
protected virtual T ReturnCached(object objectCache)
|
||||
{
|
||||
return (T)objectCache;
|
||||
}
|
||||
|
||||
public T Get(string key)
|
||||
{
|
||||
return Get(string.Empty, key, null);
|
||||
}
|
||||
protected string BuildKey(string key, string rootkey)
|
||||
{
|
||||
return $"{BaseKey}-{rootkey}-{key}";
|
||||
}
|
||||
|
||||
public T Get(string key, Func<T> defaults)
|
||||
{
|
||||
return Get(string.Empty, key, defaults);
|
||||
}
|
||||
protected abstract object GetObjectFromCache(string fullKey);
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
protected virtual void OnHit(string fullKey)
|
||||
{
|
||||
Debug.Print("cache hit:{0}", fullKey);
|
||||
}
|
||||
|
||||
public void Add(string key, T newValue)
|
||||
{
|
||||
Add(string.Empty, key, newValue);
|
||||
}
|
||||
|
||||
public abstract void Add(string rootkey, string key, T newValue);
|
||||
|
||||
public bool HasItem(string key)
|
||||
{
|
||||
return !Equals(Get(key), default(T));
|
||||
}
|
||||
|
||||
protected string BuildKey(string key, string rootkey)
|
||||
{
|
||||
return string.Format("{0}-{1}-{2}", baseKey, rootkey, key);
|
||||
}
|
||||
|
||||
protected abstract object GetObjectFromCache(string fullKey);
|
||||
|
||||
public T Get(Func<T> @default)
|
||||
{
|
||||
var key = string.Format("func {0} {2}.{1}({3})", @default.Method.ReturnType, @default.Method.Name,
|
||||
@default.Method.DeclaringType.FullName,
|
||||
string.Join(",",
|
||||
@default.Method.GetGenericArguments().Select(x => x.FullName).ToArray
|
||||
()));
|
||||
return Get(key, @default);
|
||||
}
|
||||
|
||||
protected virtual bool FitsCondition(object cached)
|
||||
{
|
||||
return cached != null && cached is T;
|
||||
}
|
||||
|
||||
public virtual T Get(string rootkey, string key, Func<T> defaults)
|
||||
{
|
||||
var fullKey = BuildKey(key, rootkey);
|
||||
var objectCache = GetObjectFromCache(fullKey);
|
||||
if (FitsCondition(objectCache))
|
||||
{
|
||||
OnHit(fullKey);
|
||||
return ReturnCached(objectCache);
|
||||
}
|
||||
if (defaults != null)
|
||||
{
|
||||
OnMiss(fullKey);
|
||||
var newValue = defaults();
|
||||
if (condition == null || condition(newValue))
|
||||
{
|
||||
Add(rootkey, key, newValue);
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
protected virtual T ReturnCached(object objectCache)
|
||||
{
|
||||
return (T)objectCache;
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
protected virtual void OnHit(string fullKey)
|
||||
{
|
||||
Debug.Print("cache hit:{0}", fullKey);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
protected virtual void OnMiss(string fullKey)
|
||||
{
|
||||
Debug.Print("cache miss:{0}", fullKey);
|
||||
}
|
||||
[Conditional("DEBUG")]
|
||||
protected virtual void OnMiss(string fullKey)
|
||||
{
|
||||
Debug.Print("cache miss:{0}", fullKey);
|
||||
}
|
||||
}
|
@ -23,73 +23,68 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Collections
|
||||
{
|
||||
public sealed class HttpRequestDictionary<T> : CachedDictionaryBase<T>
|
||||
{
|
||||
private class CachedItem
|
||||
{
|
||||
internal T Value { get; set; }
|
||||
|
||||
internal CachedItem(T value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
namespace ASC.Collections;
|
||||
|
||||
public sealed class HttpRequestDictionary<T> : CachedDictionaryBase<T>
|
||||
{
|
||||
private readonly HttpContext _httpContext;
|
||||
|
||||
public HttpRequestDictionary(HttpContext httpContext, string baseKey)
|
||||
{
|
||||
Condition = (T) => true;
|
||||
BaseKey = baseKey;
|
||||
_httpContext = httpContext;
|
||||
}
|
||||
|
||||
public override void Reset(string rootKey, string key)
|
||||
{
|
||||
if (_httpContext != null)
|
||||
{
|
||||
var builtkey = BuildKey(key, rootKey);
|
||||
_httpContext.Items[builtkey] = null;
|
||||
}
|
||||
|
||||
HttpContext HttpContext { get; set; }
|
||||
|
||||
public HttpRequestDictionary(HttpContext httpContext, string baseKey)
|
||||
{
|
||||
condition = (T) => true;
|
||||
this.baseKey = baseKey;
|
||||
HttpContext = httpContext;
|
||||
}
|
||||
|
||||
protected override void InsertRootKey(string rootKey)
|
||||
{
|
||||
//We can't expire in HtppContext in such way
|
||||
}
|
||||
|
||||
public override void Reset(string rootKey, string key)
|
||||
{
|
||||
if (HttpContext != null)
|
||||
{
|
||||
var builtkey = BuildKey(key, rootKey);
|
||||
HttpContext.Items[builtkey] = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Add(string rootkey, string key, T newValue)
|
||||
{
|
||||
if (HttpContext != null)
|
||||
{
|
||||
var builtkey = BuildKey(key, rootkey);
|
||||
HttpContext.Items[builtkey] = new CachedItem(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected override object GetObjectFromCache(string fullKey)
|
||||
{
|
||||
return HttpContext?.Items[fullKey];
|
||||
}
|
||||
|
||||
protected override bool FitsCondition(object cached)
|
||||
{
|
||||
return cached is CachedItem;
|
||||
}
|
||||
protected override T ReturnCached(object objectCache)
|
||||
{
|
||||
return ((CachedItem)objectCache).Value;
|
||||
}
|
||||
|
||||
protected override void OnHit(string fullKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnMiss(string fullKey)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public override void Add(string rootkey, string key, T newValue)
|
||||
{
|
||||
if (_httpContext != null)
|
||||
{
|
||||
var builtkey = BuildKey(key, rootkey);
|
||||
_httpContext.Items[builtkey] = new CachedItem(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected override object GetObjectFromCache(string fullKey)
|
||||
{
|
||||
return _httpContext?.Items[fullKey];
|
||||
}
|
||||
|
||||
protected override bool FitsCondition(object cached)
|
||||
{
|
||||
return cached is CachedItem;
|
||||
}
|
||||
|
||||
protected override T ReturnCached(object objectCache)
|
||||
{
|
||||
return ((CachedItem)objectCache).Value;
|
||||
}
|
||||
|
||||
protected override void OnHit(string fullKey) { }
|
||||
|
||||
protected override void OnMiss(string fullKey) { }
|
||||
|
||||
protected override void InsertRootKey(string rootKey)
|
||||
{
|
||||
//We can't expire in HtppContext in such way
|
||||
}
|
||||
|
||||
private sealed class CachedItem
|
||||
{
|
||||
internal T Value { get; set; }
|
||||
|
||||
internal CachedItem(T value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,412 +1,436 @@
|
||||
namespace ASC.Common
|
||||
namespace ASC.Common;
|
||||
|
||||
public enum DIAttributeEnum
|
||||
{
|
||||
public enum DIAttributeEnum
|
||||
Singletone,
|
||||
Scope,
|
||||
Transient
|
||||
}
|
||||
|
||||
public class TransientAttribute : DIAttribute
|
||||
{
|
||||
public override DIAttributeEnum DIAttributeEnum => DIAttributeEnum.Transient;
|
||||
|
||||
public TransientAttribute() { }
|
||||
|
||||
public TransientAttribute(Type service) : base(service) { }
|
||||
|
||||
public TransientAttribute(Type service, Type implementation) : base(service, implementation) { }
|
||||
|
||||
public override void TryAdd(IServiceCollection services, Type service, Type implementation = null)
|
||||
{
|
||||
Singletone,
|
||||
Scope,
|
||||
Transient
|
||||
}
|
||||
|
||||
public class TransientAttribute : DIAttribute
|
||||
{
|
||||
public override DIAttributeEnum DIAttributeEnum { get => DIAttributeEnum.Transient; }
|
||||
|
||||
public TransientAttribute() { }
|
||||
|
||||
public TransientAttribute(Type service) : base(service) { }
|
||||
|
||||
public TransientAttribute(Type service, Type implementation) : base(service, implementation) { }
|
||||
|
||||
public override void TryAdd(IServiceCollection services, Type service, Type implementation = null)
|
||||
if (implementation != null)
|
||||
{
|
||||
if (implementation != null)
|
||||
{
|
||||
services.AddTransient(service, implementation);
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddTransient(service);
|
||||
}
|
||||
services.AddTransient(service, implementation);
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddTransient(service);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ScopeAttribute : DIAttribute
|
||||
public class ScopeAttribute : DIAttribute
|
||||
{
|
||||
public override DIAttributeEnum DIAttributeEnum => DIAttributeEnum.Scope;
|
||||
|
||||
public ScopeAttribute() { }
|
||||
|
||||
public ScopeAttribute(Type service) : base(service) { }
|
||||
|
||||
public ScopeAttribute(Type service, Type implementation) : base(service, implementation) { }
|
||||
|
||||
public override void TryAdd(IServiceCollection services, Type service, Type implementation = null)
|
||||
{
|
||||
public override DIAttributeEnum DIAttributeEnum { get => DIAttributeEnum.Scope; }
|
||||
|
||||
public ScopeAttribute() { }
|
||||
|
||||
public ScopeAttribute(Type service) : base(service) { }
|
||||
|
||||
public ScopeAttribute(Type service, Type implementation) : base(service, implementation) { }
|
||||
|
||||
public override void TryAdd(IServiceCollection services, Type service, Type implementation = null)
|
||||
if (implementation != null)
|
||||
{
|
||||
if (implementation != null)
|
||||
{
|
||||
services.AddScoped(service, implementation);
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddScoped(service);
|
||||
}
|
||||
services.AddScoped(service, implementation);
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddScoped(service);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SingletoneAttribute : DIAttribute
|
||||
public class SingletoneAttribute : DIAttribute
|
||||
{
|
||||
public override DIAttributeEnum DIAttributeEnum => DIAttributeEnum.Singletone;
|
||||
|
||||
public SingletoneAttribute() { }
|
||||
|
||||
public SingletoneAttribute(Type service) : base(service) { }
|
||||
|
||||
public SingletoneAttribute(Type service, Type implementation) : base(service, implementation) { }
|
||||
|
||||
public override void TryAdd(IServiceCollection services, Type service, Type implementation = null)
|
||||
{
|
||||
public override DIAttributeEnum DIAttributeEnum { get => DIAttributeEnum.Singletone; }
|
||||
|
||||
public SingletoneAttribute() { }
|
||||
|
||||
public SingletoneAttribute(Type service) : base(service) { }
|
||||
|
||||
public SingletoneAttribute(Type service, Type implementation) : base(service, implementation) { }
|
||||
|
||||
public override void TryAdd(IServiceCollection services, Type service, Type implementation = null)
|
||||
if (implementation != null)
|
||||
{
|
||||
if (implementation != null)
|
||||
{
|
||||
services.AddSingleton(service, implementation);
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton(service);
|
||||
}
|
||||
services.AddSingleton(service, implementation);
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton(service);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class DIAttribute : Attribute
|
||||
public abstract class DIAttribute : Attribute
|
||||
{
|
||||
public abstract DIAttributeEnum DIAttributeEnum { get; }
|
||||
public Type Implementation { get; }
|
||||
public Type Service { get; }
|
||||
public Type Additional { get; set; }
|
||||
|
||||
protected DIAttribute() { }
|
||||
|
||||
protected DIAttribute(Type service)
|
||||
{
|
||||
public abstract DIAttributeEnum DIAttributeEnum { get; }
|
||||
public Type Implementation { get; }
|
||||
public Type Service { get; }
|
||||
public Type Additional { get; set; }
|
||||
|
||||
public DIAttribute() { }
|
||||
|
||||
public DIAttribute(Type service)
|
||||
{
|
||||
Service = service;
|
||||
}
|
||||
|
||||
public DIAttribute(Type service, Type implementation)
|
||||
{
|
||||
Implementation = implementation;
|
||||
Service = service;
|
||||
}
|
||||
|
||||
public abstract void TryAdd(IServiceCollection services, Type service, Type implementation = null);
|
||||
Service = service;
|
||||
}
|
||||
|
||||
public class DIHelper
|
||||
protected DIAttribute(Type service, Type implementation)
|
||||
{
|
||||
public Dictionary<DIAttributeEnum, List<string>> Services { get; set; }
|
||||
public List<string> Added { get; set; }
|
||||
public List<string> Configured { get; set; }
|
||||
public IServiceCollection ServiceCollection { get; private set; }
|
||||
Implementation = implementation;
|
||||
Service = service;
|
||||
}
|
||||
|
||||
public DIHelper()
|
||||
{
|
||||
Services = new Dictionary<DIAttributeEnum, List<string>>()
|
||||
public abstract void TryAdd(IServiceCollection services, Type service, Type implementation = null);
|
||||
}
|
||||
|
||||
public class DIHelper
|
||||
{
|
||||
public Dictionary<DIAttributeEnum, List<string>> Services { get; set; }
|
||||
public List<string> Added { get; set; }
|
||||
public List<string> Configured { get; set; }
|
||||
public IServiceCollection ServiceCollection { get; private set; }
|
||||
|
||||
public DIHelper()
|
||||
{
|
||||
Services = new Dictionary<DIAttributeEnum, List<string>>()
|
||||
{
|
||||
{ DIAttributeEnum.Singletone, new List<string>() },
|
||||
{ DIAttributeEnum.Scope, new List<string>() },
|
||||
{ DIAttributeEnum.Transient, new List<string>() }
|
||||
};
|
||||
Added = new List<string>();
|
||||
Configured = new List<string>();
|
||||
}
|
||||
|
||||
public DIHelper(IServiceCollection serviceCollection) : this()
|
||||
Added = new List<string>();
|
||||
Configured = new List<string>();
|
||||
}
|
||||
|
||||
public DIHelper(IServiceCollection serviceCollection) : this()
|
||||
{
|
||||
ServiceCollection = serviceCollection;
|
||||
}
|
||||
|
||||
public void Configure(IServiceCollection serviceCollection)
|
||||
{
|
||||
ServiceCollection = serviceCollection;
|
||||
}
|
||||
|
||||
public void RegisterProducts(IConfiguration configuration, string path)
|
||||
{
|
||||
var types = AutofacExtension.FindAndLoad(configuration, path);
|
||||
|
||||
foreach (var t in types.Select(Type.GetType).Where(r => r != null))
|
||||
{
|
||||
ServiceCollection = serviceCollection;
|
||||
TryAdd(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void Configure(IServiceCollection serviceCollection)
|
||||
public bool TryAdd<TService>() where TService : class
|
||||
{
|
||||
return TryAdd(typeof(TService));
|
||||
}
|
||||
|
||||
public bool TryAdd<TService, TImplementation>() where TService : class
|
||||
{
|
||||
return TryAdd(typeof(TService), typeof(TImplementation));
|
||||
}
|
||||
|
||||
public bool TryAdd(Type service, Type implementation = null)
|
||||
{
|
||||
if (service.IsInterface && service.IsGenericType && implementation == null &&
|
||||
(service.GetGenericTypeDefinition() == typeof(IOptionsSnapshot<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IOptionsMonitor<>)
|
||||
))
|
||||
{
|
||||
ServiceCollection = serviceCollection;
|
||||
}
|
||||
service = service.GetGenericArguments().FirstOrDefault();
|
||||
|
||||
public void RegisterProducts(IConfiguration configuration, string path)
|
||||
{
|
||||
var types = AutofacExtension.FindAndLoad(configuration, path);
|
||||
|
||||
foreach (var t in types.Select(Type.GetType).Where(r => r != null))
|
||||
if (service == null)
|
||||
{
|
||||
TryAdd(t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryAdd<TService>() where TService : class
|
||||
var serviceName = $"{service}{implementation}";
|
||||
|
||||
if (Added.Contains(serviceName))
|
||||
{
|
||||
return TryAdd(typeof(TService));
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryAdd<TService, TImplementation>() where TService : class
|
||||
{
|
||||
return TryAdd(typeof(TService), typeof(TImplementation));
|
||||
}
|
||||
Added.Add(serviceName);
|
||||
|
||||
public bool TryAdd(Type service, Type implementation = null)
|
||||
var di = service.IsGenericType && (
|
||||
service.GetGenericTypeDefinition() == typeof(IConfigureOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IPostConfigureOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IOptionsMonitor<>)
|
||||
) && implementation != null ? implementation.GetCustomAttribute<DIAttribute>() : service.GetCustomAttribute<DIAttribute>();
|
||||
|
||||
var isnew = false;
|
||||
|
||||
if (di != null)
|
||||
{
|
||||
if (service.IsInterface && service.IsGenericType && implementation == null &&
|
||||
(service.GetGenericTypeDefinition() == typeof(IOptionsSnapshot<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IOptionsMonitor<>)
|
||||
))
|
||||
if (di.Additional != null)
|
||||
{
|
||||
service = service.GetGenericArguments().FirstOrDefault();
|
||||
if (service == null)
|
||||
var m = di.Additional.GetMethod("Register", BindingFlags.Public | BindingFlags.Static);
|
||||
m.Invoke(null, new[] { this });
|
||||
}
|
||||
|
||||
if (!service.IsInterface || implementation != null)
|
||||
{
|
||||
isnew = implementation != null ? Register(service, implementation) : Register(service);
|
||||
if (!isnew)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var serviceName = $"{service}{implementation}";
|
||||
if (Added.Contains(serviceName)) return false;
|
||||
Added.Add(serviceName);
|
||||
|
||||
var di = service.IsGenericType && (
|
||||
service.GetGenericTypeDefinition() == typeof(IConfigureOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IPostConfigureOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IOptionsMonitor<>)
|
||||
) && implementation != null ? implementation.GetCustomAttribute<DIAttribute>() : service.GetCustomAttribute<DIAttribute>();
|
||||
var isnew = false;
|
||||
|
||||
if (di != null)
|
||||
if (service.IsInterface && implementation == null || !service.IsInterface)
|
||||
{
|
||||
if (di.Additional != null)
|
||||
if (di.Service != null)
|
||||
{
|
||||
var m = di.Additional.GetMethod("Register", BindingFlags.Public | BindingFlags.Static);
|
||||
m.Invoke(null, new[] { this });
|
||||
}
|
||||
var a = di.Service.GetInterfaces().FirstOrDefault(x => x.IsGenericType && (
|
||||
x.GetGenericTypeDefinition() == typeof(IConfigureOptions<>) ||
|
||||
x.GetGenericTypeDefinition() == typeof(IPostConfigureOptions<>) ||
|
||||
x.GetGenericTypeDefinition() == typeof(IOptionsMonitor<>)
|
||||
));
|
||||
|
||||
if (!service.IsInterface || implementation != null)
|
||||
{
|
||||
isnew = implementation != null ? Register(service, implementation) : Register(service);
|
||||
if (!isnew) return false;
|
||||
}
|
||||
|
||||
if (service.IsInterface && implementation == null || !service.IsInterface)
|
||||
{
|
||||
if (di.Service != null)
|
||||
if (a != null)
|
||||
{
|
||||
var a = di.Service.GetInterfaces().FirstOrDefault(x => x.IsGenericType && (
|
||||
x.GetGenericTypeDefinition() == typeof(IConfigureOptions<>) ||
|
||||
x.GetGenericTypeDefinition() == typeof(IPostConfigureOptions<>) ||
|
||||
x.GetGenericTypeDefinition() == typeof(IOptionsMonitor<>)
|
||||
));
|
||||
if (a != null)
|
||||
if (!a.ContainsGenericParameters)
|
||||
{
|
||||
if (!a.ContainsGenericParameters)
|
||||
{
|
||||
var b = a.GetGenericArguments();
|
||||
var b = a.GetGenericArguments();
|
||||
|
||||
foreach (var g in b)
|
||||
foreach (var g in b)
|
||||
{
|
||||
if (g != service)
|
||||
{
|
||||
if (g != service)
|
||||
TryAdd(g);
|
||||
|
||||
if (service.IsInterface && di.Implementation == null)
|
||||
{
|
||||
TryAdd(g);
|
||||
if (service.IsInterface && di.Implementation == null)
|
||||
{
|
||||
TryAdd(service, g);
|
||||
}
|
||||
TryAdd(service, g);
|
||||
}
|
||||
}
|
||||
|
||||
TryAdd(a, di.Service);
|
||||
}
|
||||
else
|
||||
{
|
||||
Type c = null;
|
||||
var a1 = a.GetGenericTypeDefinition();
|
||||
var b = a.GetGenericArguments().FirstOrDefault();
|
||||
|
||||
if (b != null && b.IsGenericType)
|
||||
{
|
||||
var b1 = b.GetGenericTypeDefinition().MakeGenericType(service.GetGenericArguments());
|
||||
|
||||
TryAdd(b1);
|
||||
c = a1.MakeGenericType(b1);
|
||||
}
|
||||
else
|
||||
{
|
||||
c = a1.MakeGenericType(service.GetGenericArguments());
|
||||
}
|
||||
|
||||
TryAdd(c, di.Service.MakeGenericType(service.GetGenericArguments()));
|
||||
//a, di.Service
|
||||
}
|
||||
TryAdd(a, di.Service);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (di.Implementation == null)
|
||||
Type c;
|
||||
var a1 = a.GetGenericTypeDefinition();
|
||||
var b = a.GetGenericArguments().FirstOrDefault();
|
||||
|
||||
if (b != null && b.IsGenericType)
|
||||
{
|
||||
isnew = Register(service, di.Service);
|
||||
TryAdd(di.Service);
|
||||
var b1 = b.GetGenericTypeDefinition().MakeGenericType(service.GetGenericArguments());
|
||||
|
||||
TryAdd(b1);
|
||||
c = a1.MakeGenericType(b1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Register(di.Service);
|
||||
c = a1.MakeGenericType(service.GetGenericArguments());
|
||||
}
|
||||
|
||||
TryAdd(c, di.Service.MakeGenericType(service.GetGenericArguments()));
|
||||
//a, di.Service
|
||||
}
|
||||
}
|
||||
|
||||
if (di.Implementation != null)
|
||||
else
|
||||
{
|
||||
var a = di.Implementation.GetInterfaces().FirstOrDefault(x => x.IsGenericType &&
|
||||
(x.GetGenericTypeDefinition() == typeof(IConfigureOptions<>) ||
|
||||
x.GetGenericTypeDefinition() == typeof(IPostConfigureOptions<>) ||
|
||||
x.GetGenericTypeDefinition() == typeof(IOptionsMonitor<>))
|
||||
);
|
||||
if (a != null)
|
||||
if (di.Implementation == null)
|
||||
{
|
||||
if (!a.ContainsGenericParameters)
|
||||
{
|
||||
var b = a.GetGenericArguments();
|
||||
|
||||
foreach (var g in b)
|
||||
{
|
||||
if (g != service)
|
||||
{
|
||||
//TryAdd(g);
|
||||
if (service.IsInterface && implementation == null)
|
||||
{
|
||||
TryAdd(service, g);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TryAdd(a, di.Implementation);
|
||||
}
|
||||
else
|
||||
{
|
||||
Type c = null;
|
||||
var a1 = a.GetGenericTypeDefinition();
|
||||
var b = a.GetGenericArguments().FirstOrDefault();
|
||||
|
||||
if (b != null && b.IsGenericType)
|
||||
{
|
||||
var b1 = b.GetGenericTypeDefinition().MakeGenericType(service.GetGenericArguments());
|
||||
|
||||
TryAdd(b1);
|
||||
c = a1.MakeGenericType(b1);
|
||||
}
|
||||
else
|
||||
{
|
||||
c = a1.MakeGenericType(service.GetGenericArguments());
|
||||
}
|
||||
|
||||
TryAdd(c, di.Implementation.MakeGenericType(service.GetGenericArguments()));
|
||||
//a, di.Service
|
||||
}
|
||||
isnew = Register(service, di.Service);
|
||||
TryAdd(di.Service);
|
||||
}
|
||||
else
|
||||
{
|
||||
isnew = TryAdd(service, di.Implementation);
|
||||
Register(di.Service);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isnew)
|
||||
{
|
||||
ConstructorInfo[] props = null;
|
||||
|
||||
if (!service.IsInterface)
|
||||
if (di.Implementation != null)
|
||||
{
|
||||
props = service.GetConstructors();
|
||||
}
|
||||
else if (implementation != null)
|
||||
{
|
||||
props = implementation.GetConstructors();
|
||||
}
|
||||
else if (di.Service != null)
|
||||
{
|
||||
props = di.Service.GetConstructors();
|
||||
}
|
||||
|
||||
if (props != null)
|
||||
{
|
||||
var par = props.SelectMany(r => r.GetParameters()).Distinct();
|
||||
foreach (var p1 in par)
|
||||
var a = di.Implementation.GetInterfaces().FirstOrDefault(x => x.IsGenericType &&
|
||||
(x.GetGenericTypeDefinition() == typeof(IConfigureOptions<>) ||
|
||||
x.GetGenericTypeDefinition() == typeof(IPostConfigureOptions<>) ||
|
||||
x.GetGenericTypeDefinition() == typeof(IOptionsMonitor<>))
|
||||
);
|
||||
if (a != null)
|
||||
{
|
||||
TryAdd(p1.ParameterType);
|
||||
if (!a.ContainsGenericParameters)
|
||||
{
|
||||
var b = a.GetGenericArguments();
|
||||
|
||||
foreach (var g in b)
|
||||
{
|
||||
if (g != service)
|
||||
{
|
||||
//TryAdd(g);
|
||||
if (service.IsInterface && implementation == null)
|
||||
{
|
||||
TryAdd(service, g);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TryAdd(a, di.Implementation);
|
||||
}
|
||||
else
|
||||
{
|
||||
Type c;
|
||||
var a1 = a.GetGenericTypeDefinition();
|
||||
var b = a.GetGenericArguments().FirstOrDefault();
|
||||
|
||||
if (b != null && b.IsGenericType)
|
||||
{
|
||||
var b1 = b.GetGenericTypeDefinition().MakeGenericType(service.GetGenericArguments());
|
||||
|
||||
TryAdd(b1);
|
||||
c = a1.MakeGenericType(b1);
|
||||
}
|
||||
else
|
||||
{
|
||||
c = a1.MakeGenericType(service.GetGenericArguments());
|
||||
}
|
||||
|
||||
TryAdd(c, di.Implementation.MakeGenericType(service.GetGenericArguments()));
|
||||
//a, di.Service
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
isnew = TryAdd(service, di.Implementation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isnew;
|
||||
}
|
||||
|
||||
private bool Register(Type service, Type implementation = null)
|
||||
if (isnew)
|
||||
{
|
||||
if (service.IsSubclassOf(typeof(ControllerBase)) || service.GetInterfaces().Contains(typeof(IResourceFilter)) || service.GetInterfaces().Contains(typeof(IDictionary<string, string>))) return true;
|
||||
var c = service.IsGenericType && (
|
||||
service.GetGenericTypeDefinition() == typeof(IConfigureOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IPostConfigureOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IOptionsMonitor<>)
|
||||
) && implementation != null ? implementation.GetCustomAttribute<DIAttribute>() : service.GetCustomAttribute<DIAttribute>();
|
||||
var serviceName = $"{service}{implementation}";
|
||||
ConstructorInfo[] props = null;
|
||||
|
||||
if (!Services[c.DIAttributeEnum].Contains(serviceName))
|
||||
if (!service.IsInterface)
|
||||
{
|
||||
c.TryAdd(ServiceCollection, service, implementation);
|
||||
Services[c.DIAttributeEnum].Add(serviceName);
|
||||
return true;
|
||||
props = service.GetConstructors();
|
||||
}
|
||||
else if (implementation != null)
|
||||
{
|
||||
props = implementation.GetConstructors();
|
||||
}
|
||||
else if (di.Service != null)
|
||||
{
|
||||
props = di.Service.GetConstructors();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public DIHelper TryAddSingleton<TService>(Func<IServiceProvider, TService> implementationFactory) where TService : class
|
||||
{
|
||||
var serviceName = $"{typeof(TService)}";
|
||||
if (!Services[DIAttributeEnum.Singletone].Contains(serviceName))
|
||||
if (props != null)
|
||||
{
|
||||
Services[DIAttributeEnum.Singletone].Add(serviceName);
|
||||
ServiceCollection.TryAddSingleton(implementationFactory);
|
||||
}
|
||||
var par = props.SelectMany(r => r.GetParameters()).Distinct();
|
||||
|
||||
return this;
|
||||
foreach (var p1 in par)
|
||||
{
|
||||
TryAdd(p1.ParameterType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DIHelper TryAddSingleton<TService, TImplementation>() where TService : class where TImplementation : class, TService
|
||||
{
|
||||
var serviceName = $"{typeof(TService)}{typeof(TImplementation)}";
|
||||
if (!Services[DIAttributeEnum.Singletone].Contains(serviceName))
|
||||
{
|
||||
Services[DIAttributeEnum.Singletone].Add(serviceName);
|
||||
ServiceCollection.TryAddSingleton<TService, TImplementation>();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public DIHelper Configure<TOptions>(Action<TOptions> configureOptions) where TOptions : class
|
||||
{
|
||||
var serviceName = $"{typeof(TOptions)}";
|
||||
if (!Configured.Contains(serviceName))
|
||||
{
|
||||
Configured.Add(serviceName);
|
||||
ServiceCollection.Configure(configureOptions);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public DIHelper Configure<TOptions>(string name, Action<TOptions> configureOptions) where TOptions : class
|
||||
{
|
||||
var serviceName = $"{typeof(TOptions)}{name}";
|
||||
if (!Configured.Contains(serviceName))
|
||||
{
|
||||
Configured.Add(serviceName);
|
||||
ServiceCollection.Configure(name, configureOptions);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
return isnew;
|
||||
}
|
||||
}
|
||||
|
||||
public DIHelper TryAddSingleton<TService>(Func<IServiceProvider, TService> implementationFactory) where TService : class
|
||||
{
|
||||
var serviceName = $"{typeof(TService)}";
|
||||
|
||||
if (!Services[DIAttributeEnum.Singletone].Contains(serviceName))
|
||||
{
|
||||
Services[DIAttributeEnum.Singletone].Add(serviceName);
|
||||
ServiceCollection.TryAddSingleton(implementationFactory);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public DIHelper TryAddSingleton<TService, TImplementation>() where TService : class where TImplementation : class, TService
|
||||
{
|
||||
var serviceName = $"{typeof(TService)}{typeof(TImplementation)}";
|
||||
|
||||
if (!Services[DIAttributeEnum.Singletone].Contains(serviceName))
|
||||
{
|
||||
Services[DIAttributeEnum.Singletone].Add(serviceName);
|
||||
ServiceCollection.TryAddSingleton<TService, TImplementation>();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public DIHelper Configure<TOptions>(Action<TOptions> configureOptions) where TOptions : class
|
||||
{
|
||||
var serviceName = $"{typeof(TOptions)}";
|
||||
|
||||
if (!Configured.Contains(serviceName))
|
||||
{
|
||||
Configured.Add(serviceName);
|
||||
ServiceCollection.Configure(configureOptions);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public DIHelper Configure<TOptions>(string name, Action<TOptions> configureOptions) where TOptions : class
|
||||
{
|
||||
var serviceName = $"{typeof(TOptions)}{name}";
|
||||
|
||||
if (!Configured.Contains(serviceName))
|
||||
{
|
||||
Configured.Add(serviceName);
|
||||
ServiceCollection.Configure(name, configureOptions);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private bool Register(Type service, Type implementation = null)
|
||||
{
|
||||
if (service.IsSubclassOf(typeof(ControllerBase)) || service.GetInterfaces().Contains(typeof(IResourceFilter))
|
||||
|| service.GetInterfaces().Contains(typeof(IDictionary<string, string>)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var c = service.IsGenericType && (
|
||||
service.GetGenericTypeDefinition() == typeof(IConfigureOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IPostConfigureOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IOptionsMonitor<>)
|
||||
) && implementation != null ? implementation.GetCustomAttribute<DIAttribute>() : service.GetCustomAttribute<DIAttribute>();
|
||||
|
||||
var serviceName = $"{service}{implementation}";
|
||||
|
||||
if (!Services[c.DIAttributeEnum].Contains(serviceName))
|
||||
{
|
||||
c.TryAdd(ServiceCollection, service, implementation);
|
||||
Services[c.DIAttributeEnum].Add(serviceName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -22,6 +22,8 @@
|
||||
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Data;
|
||||
|
||||
public static class StreamExtension
|
||||
{
|
||||
@ -29,12 +31,13 @@ public static class StreamExtension
|
||||
|
||||
public static void StreamCopyTo(this Stream srcStream, Stream dstStream, int length)
|
||||
{
|
||||
if (srcStream == null) throw new ArgumentNullException("srcStream");
|
||||
if (dstStream == null) throw new ArgumentNullException("dstStream");
|
||||
if (srcStream == null) throw new ArgumentNullException(nameof(srcStream));
|
||||
if (dstStream == null) throw new ArgumentNullException(nameof(dstStream));
|
||||
|
||||
var buffer = new byte[BufferSize];
|
||||
int totalRead = 0;
|
||||
int readed;
|
||||
|
||||
while ((readed = srcStream.Read(buffer, 0, length - totalRead > BufferSize ? BufferSize : length - totalRead)) > 0 && totalRead < length)
|
||||
{
|
||||
dstStream.Write(buffer, 0, readed);
|
||||
|
@ -14,70 +14,71 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace System.IO
|
||||
namespace System.IO;
|
||||
|
||||
[Singletone]
|
||||
public class TempPath
|
||||
{
|
||||
[Singletone]
|
||||
public class TempPath
|
||||
private readonly string _tempFolder;
|
||||
|
||||
public TempPath(IConfiguration configuration)
|
||||
{
|
||||
readonly string tempFolder;
|
||||
|
||||
public TempPath(IConfiguration configuration)
|
||||
string rootFolder = AppContext.BaseDirectory;
|
||||
if (string.IsNullOrEmpty(rootFolder))
|
||||
{
|
||||
string rootFolder = AppContext.BaseDirectory;
|
||||
|
||||
if (string.IsNullOrEmpty(rootFolder))
|
||||
{
|
||||
rootFolder = Assembly.GetEntryAssembly().Location;
|
||||
}
|
||||
|
||||
tempFolder = configuration["temp"] ?? Path.Combine("..", "Data", "temp");
|
||||
|
||||
if (!Path.IsPathRooted(tempFolder))
|
||||
{
|
||||
tempFolder = Path.GetFullPath(Path.Combine(rootFolder, tempFolder));
|
||||
}
|
||||
|
||||
if (!Directory.Exists(tempFolder))
|
||||
{
|
||||
Directory.CreateDirectory(tempFolder);
|
||||
}
|
||||
rootFolder = Assembly.GetEntryAssembly().Location;
|
||||
}
|
||||
|
||||
public string GetTempPath()
|
||||
_tempFolder = configuration["temp"] ?? Path.Combine("..", "Data", "temp");
|
||||
if (!Path.IsPathRooted(_tempFolder))
|
||||
{
|
||||
return tempFolder;
|
||||
_tempFolder = Path.GetFullPath(Path.Combine(rootFolder, _tempFolder));
|
||||
}
|
||||
|
||||
public string GetTempFileName()
|
||||
if (!Directory.Exists(_tempFolder))
|
||||
{
|
||||
FileStream f = null;
|
||||
string path;
|
||||
var count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
path = Path.Combine(tempFolder, Path.GetRandomFileName());
|
||||
|
||||
try
|
||||
{
|
||||
using (f = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.Read))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
if (ex.HResult != -2147024816 || count++ > 65536)
|
||||
throw;
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
if (count++ > 65536)
|
||||
throw new IOException(ex.Message, ex);
|
||||
}
|
||||
} while (f == null);
|
||||
|
||||
return path;
|
||||
Directory.CreateDirectory(_tempFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GetTempPath()
|
||||
{
|
||||
return _tempFolder;
|
||||
}
|
||||
|
||||
public string GetTempFileName()
|
||||
{
|
||||
FileStream f = null;
|
||||
string path;
|
||||
var count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
path = Path.Combine(_tempFolder, Path.GetRandomFileName());
|
||||
|
||||
try
|
||||
{
|
||||
using (f = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.Read))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
if (ex.HResult != -2147024816 || count++ > 65536)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
if (count++ > 65536)
|
||||
{
|
||||
throw new IOException(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
} while (f == null);
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
@ -14,35 +14,37 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common
|
||||
namespace ASC.Common;
|
||||
|
||||
[Singletone]
|
||||
public class TempStream
|
||||
{
|
||||
[Singletone]
|
||||
public class TempStream
|
||||
private readonly TempPath _tempPath;
|
||||
|
||||
public TempStream(TempPath tempPath)
|
||||
{
|
||||
private TempPath TempPath { get; }
|
||||
_tempPath = tempPath;
|
||||
}
|
||||
|
||||
public TempStream(TempPath tempPath)
|
||||
public Stream GetBuffered(Stream srcStream)
|
||||
{
|
||||
if (srcStream == null) throw new ArgumentNullException(nameof(srcStream));
|
||||
if (!srcStream.CanSeek || srcStream.CanTimeout)
|
||||
{
|
||||
TempPath = tempPath;
|
||||
//Buffer it
|
||||
var memStream = Create();
|
||||
srcStream.CopyTo(memStream);
|
||||
memStream.Position = 0;
|
||||
|
||||
return memStream;
|
||||
}
|
||||
|
||||
public Stream GetBuffered(Stream srcStream)
|
||||
{
|
||||
if (srcStream == null) throw new ArgumentNullException("srcStream");
|
||||
if (!srcStream.CanSeek || srcStream.CanTimeout)
|
||||
{
|
||||
//Buffer it
|
||||
var memStream = Create();
|
||||
srcStream.CopyTo(memStream);
|
||||
memStream.Position = 0;
|
||||
return memStream;
|
||||
}
|
||||
return srcStream;
|
||||
}
|
||||
return srcStream;
|
||||
}
|
||||
|
||||
public Stream Create()
|
||||
{
|
||||
return new FileStream(TempPath.GetTempFileName(), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.DeleteOnClose);
|
||||
}
|
||||
public Stream Create()
|
||||
{
|
||||
return new FileStream(_tempPath.GetTempFileName(), FileMode.OpenOrCreate,
|
||||
FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.DeleteOnClose);
|
||||
}
|
||||
}
|
@ -1,154 +1,162 @@
|
||||
namespace ASC.Common.DependencyInjection
|
||||
{
|
||||
internal class AutofacComponent
|
||||
{
|
||||
public string Type { get; set; }
|
||||
public IEnumerable<AutofacService> Services { get; set; }
|
||||
}
|
||||
internal class AutofacService
|
||||
{
|
||||
public string Type { get; set; }
|
||||
}
|
||||
namespace ASC.Common.DependencyInjection;
|
||||
|
||||
public static class AutofacExtension
|
||||
internal class AutofacComponent
|
||||
{
|
||||
public string Type { get; set; }
|
||||
public IEnumerable<AutofacService> Services { get; set; }
|
||||
}
|
||||
|
||||
internal class AutofacService
|
||||
{
|
||||
public string Type { get; set; }
|
||||
}
|
||||
|
||||
public static class AutofacExtension
|
||||
{
|
||||
public static void Register(this ContainerBuilder builder, IConfiguration configuration,
|
||||
bool loadproducts = true, bool loadconsumers = true, params string[] intern)
|
||||
{
|
||||
public static void Register(this ContainerBuilder builder, IConfiguration configuration, bool loadproducts = true, bool loadconsumers = true, params string[] intern)
|
||||
{
|
||||
var modules = new List<(bool, string)>
|
||||
var modules = new List<(bool, string)>
|
||||
{
|
||||
(true, "autofac.json")
|
||||
};
|
||||
|
||||
if (loadproducts)
|
||||
{
|
||||
modules.Add((true, "autofac.products.json"));
|
||||
}
|
||||
|
||||
if (loadconsumers)
|
||||
{
|
||||
modules.Add((true, "autofac.consumers.json"));
|
||||
}
|
||||
|
||||
if (intern != null)
|
||||
{
|
||||
modules.AddRange(intern.Select(r => (false, r)));
|
||||
}
|
||||
|
||||
foreach (var p in modules)
|
||||
{
|
||||
var config = new ConfigurationBuilder();
|
||||
if (p.Item1)
|
||||
{
|
||||
config.SetBasePath(configuration["pathToConf"]);
|
||||
}
|
||||
config.AddJsonFile(p.Item2);
|
||||
|
||||
var root = config.Build();
|
||||
var module = new ConfigurationModule(root);
|
||||
builder.RegisterModule(module);
|
||||
}
|
||||
|
||||
return;
|
||||
if (loadproducts)
|
||||
{
|
||||
modules.Add((true, "autofac.products.json"));
|
||||
}
|
||||
|
||||
public static List<string> FindAndLoad(IConfiguration configuration, string currentDir, string section = "autofac.products.json")
|
||||
if (loadconsumers)
|
||||
{
|
||||
modules.Add((true, "autofac.consumers.json"));
|
||||
}
|
||||
|
||||
if (intern != null)
|
||||
{
|
||||
modules.AddRange(intern.Select(r => (false, r)));
|
||||
}
|
||||
|
||||
foreach (var p in modules)
|
||||
{
|
||||
var config = new ConfigurationBuilder();
|
||||
config.SetBasePath(configuration["pathToConf"]);
|
||||
config.AddJsonFile(section);
|
||||
var root = config.Build();
|
||||
|
||||
var sectionSettings = root.GetSection("components");
|
||||
|
||||
if (sectionSettings == null)
|
||||
if (p.Item1)
|
||||
{
|
||||
return new List<string>();
|
||||
config.SetBasePath(configuration["pathToConf"]);
|
||||
}
|
||||
|
||||
var folder = configuration["core:products:folder"];
|
||||
var subfolder = configuration["core:products:subfolder"];
|
||||
string productsDir;
|
||||
config.AddJsonFile(p.Item2);
|
||||
|
||||
if (!Path.IsPathRooted(folder))
|
||||
var root = config.Build();
|
||||
var module = new ConfigurationModule(root);
|
||||
builder.RegisterModule(module);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public static List<string> FindAndLoad(IConfiguration configuration, string currentDir, string section = "autofac.products.json")
|
||||
{
|
||||
var config = new ConfigurationBuilder();
|
||||
config.SetBasePath(configuration["pathToConf"]);
|
||||
config.AddJsonFile(section);
|
||||
var root = config.Build();
|
||||
|
||||
var sectionSettings = root.GetSection("components");
|
||||
if (sectionSettings == null)
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
var folder = configuration["core:products:folder"];
|
||||
var subfolder = configuration["core:products:subfolder"];
|
||||
string productsDir;
|
||||
|
||||
if (!Path.IsPathRooted(folder))
|
||||
{
|
||||
if (currentDir.EndsWith(CrossPlatform.PathCombine(Path.GetFileName(folder), Assembly.GetEntryAssembly().GetName().Name, subfolder)))
|
||||
{
|
||||
if (currentDir.EndsWith(CrossPlatform.PathCombine(Path.GetFileName(folder), Assembly.GetEntryAssembly().GetName().Name, subfolder)))
|
||||
{
|
||||
productsDir = Path.GetFullPath(CrossPlatform.PathCombine("..", ".."));
|
||||
}
|
||||
else
|
||||
{
|
||||
productsDir = Path.GetFullPath(CrossPlatform.PathCombine(currentDir, folder));
|
||||
}
|
||||
productsDir = Path.GetFullPath(CrossPlatform.PathCombine("..", ".."));
|
||||
}
|
||||
else
|
||||
{
|
||||
productsDir = folder;
|
||||
productsDir = Path.GetFullPath(CrossPlatform.PathCombine(currentDir, folder));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
productsDir = folder;
|
||||
}
|
||||
|
||||
var cs = new List<AutofacComponent>();
|
||||
sectionSettings.Bind(cs);
|
||||
var cs = new List<AutofacComponent>();
|
||||
sectionSettings.Bind(cs);
|
||||
|
||||
var types = new List<string>();
|
||||
var types = new List<string>();
|
||||
|
||||
foreach (var component in cs)
|
||||
foreach (var component in cs)
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadAssembly(component.Type);
|
||||
types.Add(component.Type);
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
LoadAssembly(component.Type);
|
||||
types.Add(component.Type);
|
||||
}
|
||||
|
||||
return types;
|
||||
|
||||
void LoadAssembly(string type)
|
||||
catch (System.Exception)
|
||||
{
|
||||
var dll = type.Substring(type.IndexOf(",") + 1).Trim();
|
||||
var path = GetFullPath(dll);
|
||||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
AssemblyLoadContext.Default.Resolving += new Resolver(path).Resolving;
|
||||
}
|
||||
}
|
||||
|
||||
string GetFullPath(string n)
|
||||
{
|
||||
var productPath = CrossPlatform.PathCombine(productsDir, n, subfolder);
|
||||
return GetPath(CrossPlatform.PathCombine(productPath, "bin"), n, SearchOption.AllDirectories) ?? GetPath(productPath, n, SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
|
||||
static string GetPath(string dirPath, string dll, SearchOption searchOption)
|
||||
{
|
||||
if (!Directory.Exists(dirPath)) return null;
|
||||
|
||||
return Directory.GetFiles(dirPath, $"{dll}.dll", searchOption).FirstOrDefault();
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return types;
|
||||
|
||||
class Resolver
|
||||
{
|
||||
private string ResolvePath { get; set; }
|
||||
|
||||
public Resolver(string assemblyPath)
|
||||
void LoadAssembly(string type)
|
||||
{
|
||||
ResolvePath = assemblyPath;
|
||||
var dll = type.Substring(type.IndexOf(',') + 1).Trim();
|
||||
var path = GetFullPath(dll);
|
||||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
AssemblyLoadContext.Default.Resolving += new Resolver(path).Resolving;
|
||||
}
|
||||
}
|
||||
|
||||
public Assembly Resolving(AssemblyLoadContext context, AssemblyName assemblyName)
|
||||
string GetFullPath(string n)
|
||||
{
|
||||
var path = CrossPlatform.PathCombine(Path.GetDirectoryName(ResolvePath), $"{assemblyName.Name}.dll");
|
||||
var productPath = CrossPlatform.PathCombine(productsDir, n, subfolder);
|
||||
|
||||
if (!File.Exists(path)) return null;
|
||||
return GetPath(CrossPlatform.PathCombine(productPath, "bin"), n, SearchOption.AllDirectories)
|
||||
?? GetPath(productPath, n, SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
|
||||
return context.LoadFromAssemblyPath(path);
|
||||
static string GetPath(string dirPath, string dll, SearchOption searchOption)
|
||||
{
|
||||
if (!Directory.Exists(dirPath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Directory.GetFiles(dirPath, $"{dll}.dll", searchOption).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Resolver
|
||||
{
|
||||
private readonly string _resolvePath;
|
||||
|
||||
public Resolver(string assemblyPath)
|
||||
{
|
||||
_resolvePath = assemblyPath;
|
||||
}
|
||||
|
||||
public Assembly Resolving(AssemblyLoadContext context, AssemblyName assemblyName)
|
||||
{
|
||||
var path = CrossPlatform.PathCombine(Path.GetDirectoryName(_resolvePath), $"{assemblyName.Name}.dll");
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return context.LoadFromAssemblyPath(path);
|
||||
}
|
||||
}
|
@ -23,30 +23,23 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Geolocation
|
||||
{
|
||||
public class IPGeolocationInfo
|
||||
{
|
||||
public string Key { get; set; }
|
||||
|
||||
public string City { get; set; }
|
||||
namespace ASC.Geolocation;
|
||||
|
||||
public double TimezoneOffset { get; set; }
|
||||
public class IPGeolocationInfo
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public string City { get; set; }
|
||||
public double TimezoneOffset { get; set; }
|
||||
public string TimezoneName { get; set; }
|
||||
public string IPStart { get; set; }
|
||||
public string IPEnd { get; set; }
|
||||
|
||||
public string TimezoneName { get; set; }
|
||||
|
||||
public string IPStart { get; set; }
|
||||
|
||||
public string IPEnd { get; set; }
|
||||
|
||||
|
||||
public readonly static IPGeolocationInfo Default = new IPGeolocationInfo
|
||||
{
|
||||
Key = string.Empty,
|
||||
IPStart = string.Empty,
|
||||
IPEnd = string.Empty,
|
||||
City = string.Empty,
|
||||
TimezoneName = string.Empty,
|
||||
};
|
||||
}
|
||||
public readonly static IPGeolocationInfo Default = new IPGeolocationInfo
|
||||
{
|
||||
Key = string.Empty,
|
||||
IPStart = string.Empty,
|
||||
IPEnd = string.Empty,
|
||||
City = string.Empty,
|
||||
TimezoneName = string.Empty,
|
||||
};
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ global using System.Threading.Tasks;
|
||||
global using System.Web;
|
||||
global using System.Xml.Linq;
|
||||
global using System.Xml.XPath;
|
||||
global using System.ServiceModel;
|
||||
global using System.Runtime.Serialization;
|
||||
|
||||
global using ARSoft.Tools.Net;
|
||||
global using ARSoft.Tools.Net.Dns;
|
||||
|
@ -1,92 +1,93 @@
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||
|
||||
namespace ASC.Common.Logging
|
||||
namespace ASC.Common.Logging;
|
||||
|
||||
[Singletone]
|
||||
public class EFLoggerFactory : ILoggerFactory
|
||||
{
|
||||
[Singletone]
|
||||
public class EFLoggerFactory : ILoggerFactory
|
||||
private readonly Lazy<ILogger> _logger;
|
||||
private readonly ILoggerProvider _loggerProvider;
|
||||
|
||||
public EFLoggerFactory(EFLoggerProvider loggerProvider)
|
||||
{
|
||||
Lazy<ILogger> Logger { get; set; }
|
||||
ILoggerProvider LoggerProvider { get; set; }
|
||||
|
||||
public EFLoggerFactory(EFLoggerProvider loggerProvider)
|
||||
{
|
||||
LoggerProvider = loggerProvider;
|
||||
Logger = new Lazy<ILogger>(() => LoggerProvider.CreateLogger(""));
|
||||
}
|
||||
|
||||
public void AddProvider(ILoggerProvider provider)
|
||||
{
|
||||
//LoggerProvider = provider;
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return Logger.Value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
_loggerProvider = loggerProvider;
|
||||
_logger = new Lazy<ILogger>(() => _loggerProvider.CreateLogger(""));
|
||||
}
|
||||
|
||||
[Singletone]
|
||||
public class EFLoggerProvider : ILoggerProvider
|
||||
public void AddProvider(ILoggerProvider provider)
|
||||
{
|
||||
private IOptionsMonitor<ILog> Option { get; }
|
||||
|
||||
public EFLoggerProvider(IOptionsMonitor<ILog> option)
|
||||
{
|
||||
Option = option;
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return new EFLogger(Option.Get("ASC.SQL"));
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
//LoggerProvider = provider;
|
||||
}
|
||||
|
||||
public class EFLogger : ILogger
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
public ILog CustomLogger { get; }
|
||||
return _logger.Value;
|
||||
}
|
||||
|
||||
public EFLogger(ILog customLogger)
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
[Singletone]
|
||||
public class EFLoggerProvider : ILoggerProvider
|
||||
{
|
||||
private readonly IOptionsMonitor<ILog> _option;
|
||||
|
||||
public EFLoggerProvider(IOptionsMonitor<ILog> option)
|
||||
{
|
||||
_option = option;
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return new EFLogger(_option.Get("ASC.SQL"));
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
public class EFLogger : ILogger
|
||||
{
|
||||
public ILog CustomLogger { get; }
|
||||
|
||||
public EFLogger(ILog customLogger)
|
||||
{
|
||||
CustomLogger = customLogger;
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return logLevel switch
|
||||
{
|
||||
CustomLogger = customLogger;
|
||||
}
|
||||
LogLevel.Trace => CustomLogger.IsTraceEnabled,
|
||||
LogLevel.Information => CustomLogger.IsInfoEnabled,
|
||||
LogLevel.None => false,
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state) { return null; }
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
LogLevel.Debug => CustomLogger.IsDebugEnabled,
|
||||
LogLevel.Warning => CustomLogger.IsWarnEnabled,
|
||||
LogLevel.Error => CustomLogger.IsErrorEnabled,
|
||||
LogLevel.Critical => CustomLogger.IsErrorEnabled,
|
||||
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
switch (eventId.Id)
|
||||
{
|
||||
return logLevel switch
|
||||
{
|
||||
LogLevel.Trace => CustomLogger.IsTraceEnabled,
|
||||
LogLevel.Information => CustomLogger.IsInfoEnabled,
|
||||
LogLevel.None => false,
|
||||
|
||||
LogLevel.Debug => CustomLogger.IsDebugEnabled,
|
||||
LogLevel.Warning => CustomLogger.IsWarnEnabled,
|
||||
LogLevel.Error => CustomLogger.IsErrorEnabled,
|
||||
LogLevel.Critical => CustomLogger.IsErrorEnabled,
|
||||
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
switch (eventId.Id)
|
||||
{
|
||||
//case 20000:
|
||||
// CustomLogger.Debug(formatter(state, exception));
|
||||
// break;
|
||||
case 20101:
|
||||
var keyValuePairs = state as IEnumerable<KeyValuePair<string, object>>;
|
||||
CustomLogger.DebugWithProps("", keyValuePairs);
|
||||
break;
|
||||
}
|
||||
//case 20000:
|
||||
// CustomLogger.Debug(formatter(state, exception));
|
||||
// break;
|
||||
case 20101:
|
||||
var keyValuePairs = state as IEnumerable<KeyValuePair<string, object>>;
|
||||
CustomLogger.DebugWithProps("", keyValuePairs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -23,87 +23,89 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Logging
|
||||
{
|
||||
public class SelfCleaningAppender : RollingFileAppender
|
||||
{
|
||||
private static DateTime _lastCleanDate;
|
||||
|
||||
private static int? _cleanPeriod;
|
||||
|
||||
private static int GetCleanPeriod()
|
||||
{
|
||||
if (_cleanPeriod != null)
|
||||
return _cleanPeriod.Value;
|
||||
|
||||
const string key = "CleanPeriod";
|
||||
|
||||
var value = 30;
|
||||
|
||||
var repo = log4net.LogManager.GetRepository(Assembly.GetCallingAssembly());
|
||||
|
||||
if (repo != null && repo.Properties.GetKeys().Contains(key))
|
||||
{
|
||||
int.TryParse(repo.Properties[key].ToString(), out value);
|
||||
}
|
||||
|
||||
_cleanPeriod = value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private void Clean()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(File))
|
||||
return;
|
||||
namespace ASC.Common.Logging;
|
||||
|
||||
var fileInfo = new FileInfo(File);
|
||||
|
||||
if (!fileInfo.Exists)
|
||||
return;
|
||||
|
||||
var directory = fileInfo.Directory;
|
||||
|
||||
if (directory == null || !directory.Exists)
|
||||
return;
|
||||
|
||||
var files = directory.GetFiles();
|
||||
|
||||
var cleanPeriod = GetCleanPeriod();
|
||||
|
||||
foreach (var file in files.Where(file => (DateTime.UtcNow.Date - file.CreationTimeUtc.Date).Days > cleanPeriod))
|
||||
{
|
||||
file.Delete();
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
LogLog.Error(GetType(), err.Message, err);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Append(LoggingEvent loggingEvent)
|
||||
{
|
||||
if (DateTime.UtcNow.Date > _lastCleanDate.Date)
|
||||
{
|
||||
_lastCleanDate = DateTime.UtcNow.Date;
|
||||
Clean();
|
||||
}
|
||||
public class SelfCleaningAppender : RollingFileAppender
|
||||
{
|
||||
private static DateTime _lastCleanDate;
|
||||
private static int? _cleanPeriod;
|
||||
|
||||
base.Append(loggingEvent);
|
||||
protected override void Append(LoggingEvent loggingEvent)
|
||||
{
|
||||
if (DateTime.UtcNow.Date > _lastCleanDate.Date)
|
||||
{
|
||||
_lastCleanDate = DateTime.UtcNow.Date;
|
||||
Clean();
|
||||
}
|
||||
|
||||
protected override void Append(LoggingEvent[] loggingEvents)
|
||||
{
|
||||
if (DateTime.UtcNow.Date > _lastCleanDate.Date)
|
||||
{
|
||||
_lastCleanDate = DateTime.UtcNow.Date;
|
||||
Clean();
|
||||
base.Append(loggingEvent);
|
||||
}
|
||||
|
||||
protected override void Append(LoggingEvent[] loggingEvents)
|
||||
{
|
||||
if (DateTime.UtcNow.Date > _lastCleanDate.Date)
|
||||
{
|
||||
_lastCleanDate = DateTime.UtcNow.Date;
|
||||
Clean();
|
||||
}
|
||||
|
||||
base.Append(loggingEvents);
|
||||
}
|
||||
|
||||
private static int GetCleanPeriod()
|
||||
{
|
||||
if (_cleanPeriod != null)
|
||||
{
|
||||
return _cleanPeriod.Value;
|
||||
}
|
||||
|
||||
const string key = "CleanPeriod";
|
||||
|
||||
var value = 30;
|
||||
|
||||
var repo = log4net.LogManager.GetRepository(Assembly.GetCallingAssembly());
|
||||
if (repo != null && repo.Properties.GetKeys().Contains(key))
|
||||
{
|
||||
int.TryParse(repo.Properties[key].ToString(), out value);
|
||||
}
|
||||
|
||||
_cleanPeriod = value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private void Clean()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(File))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
base.Append(loggingEvents);
|
||||
}
|
||||
}
|
||||
var fileInfo = new FileInfo(File);
|
||||
if (!fileInfo.Exists)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var directory = fileInfo.Directory;
|
||||
if (directory == null || !directory.Exists)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var files = directory.GetFiles();
|
||||
var cleanPeriod = GetCleanPeriod();
|
||||
|
||||
foreach (var file in files.Where(file => (DateTime.UtcNow.Date - file.CreationTimeUtc.Date).Days > cleanPeriod))
|
||||
{
|
||||
file.Delete();
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
LogLog.Error(GetType(), err.Message, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,118 +25,125 @@
|
||||
|
||||
using LogLevel = NLog.LogLevel;
|
||||
|
||||
namespace ASC.Common.Logging
|
||||
{
|
||||
[Target("SelfCleaning")]
|
||||
public class SelfCleaningTarget : FileTarget
|
||||
{
|
||||
private static DateTime _lastCleanDate;
|
||||
|
||||
private static int? _cleanPeriod;
|
||||
|
||||
private static int GetCleanPeriod()
|
||||
{
|
||||
if (_cleanPeriod != null)
|
||||
return _cleanPeriod.Value;
|
||||
|
||||
var value = 30;
|
||||
|
||||
const string key = "cleanPeriod";
|
||||
|
||||
if (NLog.LogManager.Configuration.Variables.Keys.Contains(key))
|
||||
{
|
||||
var variable = NLog.LogManager.Configuration.Variables[key];
|
||||
|
||||
if (variable != null && !string.IsNullOrEmpty(variable.Text))
|
||||
{
|
||||
int.TryParse(variable.Text, out value);
|
||||
}
|
||||
}
|
||||
|
||||
_cleanPeriod = value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private void Clean()
|
||||
{
|
||||
var filePath = string.Empty;
|
||||
var dirPath = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
if (FileName == null)
|
||||
return;
|
||||
|
||||
filePath = ((NLog.Layouts.SimpleLayout)FileName).Text;
|
||||
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
return;
|
||||
|
||||
dirPath = Path.GetDirectoryName(filePath);
|
||||
|
||||
if (string.IsNullOrEmpty(dirPath))
|
||||
return;
|
||||
|
||||
if (!Path.IsPathRooted(dirPath))
|
||||
dirPath = CrossPlatform.PathCombine(AppDomain.CurrentDomain.BaseDirectory, dirPath);
|
||||
|
||||
var directory = new DirectoryInfo(dirPath);
|
||||
|
||||
if (!directory.Exists)
|
||||
return;
|
||||
|
||||
var files = directory.GetFiles();
|
||||
|
||||
var cleanPeriod = GetCleanPeriod();
|
||||
|
||||
foreach (var file in files.Where(file => (DateTime.UtcNow.Date - file.CreationTimeUtc.Date).Days > cleanPeriod))
|
||||
{
|
||||
file.Delete();
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
base.Write(new LogEventInfo
|
||||
{
|
||||
Exception = err,
|
||||
Level = LogLevel.Error,
|
||||
Message = string.Format("file: {0}, dir: {1}, mess: {2}", filePath, dirPath, err.Message),
|
||||
LoggerName = "SelfCleaningTarget"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Write(IList<AsyncLogEventInfo> logEvents)
|
||||
{
|
||||
if (DateTime.UtcNow.Date > _lastCleanDate.Date)
|
||||
{
|
||||
_lastCleanDate = DateTime.UtcNow.Date;
|
||||
Clean();
|
||||
}
|
||||
namespace ASC.Common.Logging;
|
||||
|
||||
var buffer = new List<AsyncLogEventInfo>();
|
||||
[Target("SelfCleaning")]
|
||||
public class SelfCleaningTarget : FileTarget
|
||||
{
|
||||
private static DateTime _lastCleanDate;
|
||||
private static int? _cleanPeriod;
|
||||
|
||||
foreach (var logEvent in logEvents)
|
||||
protected override void Write(IList<AsyncLogEventInfo> logEvents)
|
||||
{
|
||||
if (DateTime.UtcNow.Date > _lastCleanDate.Date)
|
||||
{
|
||||
_lastCleanDate = DateTime.UtcNow.Date;
|
||||
Clean();
|
||||
}
|
||||
|
||||
var buffer = new List<AsyncLogEventInfo>();
|
||||
|
||||
foreach (var logEvent in logEvents)
|
||||
{
|
||||
buffer.Add(logEvent);
|
||||
if (buffer.Count < 10)
|
||||
{
|
||||
buffer.Add(logEvent);
|
||||
if (buffer.Count < 10) continue;
|
||||
base.Write(buffer);
|
||||
buffer.Clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
base.Write(buffer);
|
||||
base.Write(buffer);
|
||||
buffer.Clear();
|
||||
}
|
||||
|
||||
protected override void Write(LogEventInfo logEvent)
|
||||
{
|
||||
if (DateTime.UtcNow.Date > _lastCleanDate.Date)
|
||||
{
|
||||
_lastCleanDate = DateTime.UtcNow.Date;
|
||||
Clean();
|
||||
}
|
||||
|
||||
base.Write(logEvent);
|
||||
base.Write(buffer);
|
||||
}
|
||||
|
||||
protected override void Write(LogEventInfo logEvent)
|
||||
{
|
||||
if (DateTime.UtcNow.Date > _lastCleanDate.Date)
|
||||
{
|
||||
_lastCleanDate = DateTime.UtcNow.Date;
|
||||
Clean();
|
||||
}
|
||||
}
|
||||
|
||||
base.Write(logEvent);
|
||||
}
|
||||
|
||||
private static int GetCleanPeriod()
|
||||
{
|
||||
if (_cleanPeriod != null)
|
||||
{
|
||||
return _cleanPeriod.Value;
|
||||
}
|
||||
|
||||
var value = 30;
|
||||
|
||||
const string key = "cleanPeriod";
|
||||
|
||||
if (LogManager.Configuration.Variables.TryGetValue(key, out var variable))
|
||||
{
|
||||
if (variable != null && !string.IsNullOrEmpty(variable.Text))
|
||||
{
|
||||
int.TryParse(variable.Text, out value);
|
||||
}
|
||||
}
|
||||
|
||||
_cleanPeriod = value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private void Clean()
|
||||
{
|
||||
var filePath = string.Empty;
|
||||
var dirPath = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
if (FileName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
filePath = ((NLog.Layouts.SimpleLayout)FileName).Text;
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dirPath = Path.GetDirectoryName(filePath);
|
||||
if (string.IsNullOrEmpty(dirPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!Path.IsPathRooted(dirPath))
|
||||
{
|
||||
dirPath = CrossPlatform.PathCombine(AppDomain.CurrentDomain.BaseDirectory, dirPath);
|
||||
}
|
||||
|
||||
var directory = new DirectoryInfo(dirPath);
|
||||
if (!directory.Exists)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var files = directory.GetFiles();
|
||||
var cleanPeriod = GetCleanPeriod();
|
||||
|
||||
foreach (var file in files.Where(file => (DateTime.UtcNow.Date - file.CreationTimeUtc.Date).Days > cleanPeriod))
|
||||
{
|
||||
file.Delete();
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
base.Write(new LogEventInfo
|
||||
{
|
||||
Exception = err,
|
||||
Level = LogLevel.Error,
|
||||
Message = $"file: {filePath}, dir: {dirPath}, mess: {err.Message}",
|
||||
LoggerName = "SelfCleaningTarget"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,72 +23,74 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Logging
|
||||
{
|
||||
public class SpecialFolderPathConverter : PatternConverter
|
||||
{
|
||||
protected override void Convert(TextWriter writer, object state)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Option))
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
var result = string.Empty;
|
||||
const string CMD_LINE = "CommandLine:";
|
||||
if (Option.StartsWith(CMD_LINE))
|
||||
{
|
||||
var args = Environment.CommandLine.Split(' ');
|
||||
for (var i = 0; i < args.Length - 1; i++)
|
||||
{
|
||||
if (args[i].Equals(Option.Substring(CMD_LINE.Length), StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
result = args[i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var repo = log4net.LogManager.GetRepository(Assembly.GetCallingAssembly());
|
||||
if (repo != null)
|
||||
{
|
||||
var realKey = Option;
|
||||
foreach (var key in repo.Properties.GetKeys())
|
||||
{
|
||||
if (Path.DirectorySeparatorChar == '/' && key == "UNIX:" + Option)
|
||||
{
|
||||
realKey = "UNIX:" + Option;
|
||||
}
|
||||
if (Path.DirectorySeparatorChar == '\\' && key == "WINDOWS:" + Option)
|
||||
{
|
||||
realKey = "WINDOWS:" + Option;
|
||||
}
|
||||
}
|
||||
|
||||
var val = repo.Properties[realKey];
|
||||
if (val is PatternString patternString)
|
||||
{
|
||||
patternString.ActivateOptions();
|
||||
patternString.Format(writer);
|
||||
}
|
||||
else if (val != null)
|
||||
{
|
||||
result = val.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
{
|
||||
result = result.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar);
|
||||
writer.Write(result);
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
LogLog.Error(GetType(), "Can not convert " + Option, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace ASC.Common.Logging;
|
||||
|
||||
public class SpecialFolderPathConverter : PatternConverter
|
||||
{
|
||||
protected override void Convert(TextWriter writer, object state)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Option))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = string.Empty;
|
||||
const string CMD_LINE = "CommandLine:";
|
||||
|
||||
if (Option.StartsWith(CMD_LINE))
|
||||
{
|
||||
var args = Environment.CommandLine.Split(' ');
|
||||
for (var i = 0; i < args.Length - 1; i++)
|
||||
{
|
||||
if (args[i].Contains(Option, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
result = args[i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var repo = log4net.LogManager.GetRepository(Assembly.GetCallingAssembly());
|
||||
if (repo != null)
|
||||
{
|
||||
var realKey = Option;
|
||||
foreach (var key in repo.Properties.GetKeys())
|
||||
{
|
||||
if (Path.DirectorySeparatorChar == '/' && key == "UNIX:" + Option)
|
||||
{
|
||||
realKey = "UNIX:" + Option;
|
||||
}
|
||||
|
||||
if (Path.DirectorySeparatorChar == '\\' && key == "WINDOWS:" + Option)
|
||||
{
|
||||
realKey = "WINDOWS:" + Option;
|
||||
}
|
||||
}
|
||||
|
||||
var val = repo.Properties[realKey];
|
||||
if (val is PatternString patternString)
|
||||
{
|
||||
patternString.ActivateOptions();
|
||||
patternString.Format(writer);
|
||||
}
|
||||
else if (val != null)
|
||||
{
|
||||
result = val.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
{
|
||||
result = result.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar);
|
||||
writer.Write(result);
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
LogLog.Error(GetType(), "Can not convert " + Option, err);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,10 +23,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Mapping
|
||||
namespace ASC.Common.Mapping;
|
||||
|
||||
public interface IMapFrom<T>
|
||||
{
|
||||
public interface IMapFrom<T>
|
||||
void Mapping(Profile profile)
|
||||
{
|
||||
void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());
|
||||
profile.CreateMap(typeof(T), GetType());
|
||||
}
|
||||
}
|
||||
|
@ -23,34 +23,31 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Mapping
|
||||
namespace ASC.Common.Mapping;
|
||||
|
||||
public class MappingProfile : Profile
|
||||
{
|
||||
public class MappingProfile : Profile
|
||||
public MappingProfile() => Array.ForEach(AppDomain.CurrentDomain.GetAssemblies(), a => ApplyMappingsFromAssembly(a));
|
||||
|
||||
private void ApplyMappingsFromAssembly(Assembly assembly)
|
||||
{
|
||||
public MappingProfile()
|
||||
if (!assembly.GetName().Name.StartsWith("ASC."))
|
||||
{
|
||||
Array.ForEach(AppDomain.CurrentDomain.GetAssemblies(), a => ApplyMappingsFromAssembly(a));
|
||||
return;
|
||||
}
|
||||
|
||||
private void ApplyMappingsFromAssembly(Assembly assembly)
|
||||
var types = assembly.GetExportedTypes()
|
||||
.Where(t => t.GetInterfaces().Any(i =>
|
||||
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>)));
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
if (!assembly.GetName().Name.StartsWith("ASC.")) return;
|
||||
var instance = Activator.CreateInstance(type);
|
||||
|
||||
var types = assembly.GetExportedTypes()
|
||||
.Where(t => t.GetInterfaces().Any(i =>
|
||||
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>)))
|
||||
.ToList();
|
||||
var methodInfo = type.GetMethod("Mapping")
|
||||
?? type.GetInterface("IMapFrom`1").GetMethod("Mapping");
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
var instance = Activator.CreateInstance(type);
|
||||
|
||||
var methodInfo = type.GetMethod("Mapping")
|
||||
?? type.GetInterface("IMapFrom`1").GetMethod("Mapping");
|
||||
|
||||
methodInfo?.Invoke(instance, new object[] { this });
|
||||
|
||||
}
|
||||
methodInfo?.Invoke(instance, new object[] { this });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,37 +23,31 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Module;
|
||||
|
||||
using System.ServiceModel;
|
||||
|
||||
namespace ASC.Common.Module
|
||||
public class BaseWcfClient<TService> : ClientBase<TService>, IDisposable where TService : class
|
||||
{
|
||||
public class BaseWcfClient<TService> : ClientBase<TService>, IDisposable where TService : class
|
||||
{
|
||||
public BaseWcfClient()
|
||||
{
|
||||
}
|
||||
public BaseWcfClient() { }
|
||||
|
||||
void IDisposable.Dispose()
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
// msdn recommendation to close wcf client
|
||||
try
|
||||
{
|
||||
// msdn recommendation to close wcf client
|
||||
try
|
||||
{
|
||||
//Close();
|
||||
}
|
||||
catch (CommunicationException)
|
||||
{
|
||||
Abort();
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
Abort();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Abort();
|
||||
throw;
|
||||
}
|
||||
//Close();
|
||||
}
|
||||
catch (CommunicationException)
|
||||
{
|
||||
Abort();
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
Abort();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Abort();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -24,12 +24,11 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Module
|
||||
{
|
||||
public interface IServiceController
|
||||
{
|
||||
void Start();
|
||||
namespace ASC.Common.Module;
|
||||
|
||||
void Stop();
|
||||
}
|
||||
public interface IServiceController
|
||||
{
|
||||
void Start();
|
||||
|
||||
void Stop();
|
||||
}
|
||||
|
@ -1,192 +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;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Mail;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using ASC.Common.Utils;
|
||||
using Amazon.SimpleEmail;
|
||||
using Amazon.SimpleEmail.Model;
|
||||
using log4net;
|
||||
|
||||
namespace ASC.Common.Notify
|
||||
{
|
||||
public class AWSEmail
|
||||
{
|
||||
public enum SendStatus
|
||||
{
|
||||
Ok,
|
||||
Failed,
|
||||
QuotaLimit
|
||||
}
|
||||
|
||||
private readonly AmazonSimpleEmailServiceClient _emailService;
|
||||
private readonly ILog _log = LogHolder.Log("ASC.Notify.AmazonSES");
|
||||
|
||||
//Static fields
|
||||
private static readonly TimeSpan RefreshTimeout;
|
||||
private static DateTime _lastRefresh;
|
||||
private static DateTime _lastSend;
|
||||
private static TimeSpan _sendWindow = TimeSpan.MinValue;
|
||||
private static GetSendQuotaResult _quota;
|
||||
private static readonly object SynchRoot = new object();
|
||||
|
||||
|
||||
static AWSEmail()
|
||||
{
|
||||
RefreshTimeout = TimeSpan.FromMinutes(30);
|
||||
if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["ses.refreshTimeout"]))
|
||||
{
|
||||
TimeSpan.TryParse(ConfigurationManager.AppSettings["ses.refreshTimeout"], out RefreshTimeout);
|
||||
}
|
||||
_lastRefresh = DateTime.UtcNow - RefreshTimeout;//Set to refresh on first send
|
||||
}
|
||||
|
||||
public AWSEmail()
|
||||
{
|
||||
var accessKey = ConfigurationManager.AppSettings["ses.accessKey"];
|
||||
var secretKey = ConfigurationManager.AppSettings["ses.secretKey"];
|
||||
|
||||
_emailService = new AmazonSimpleEmailServiceClient(accessKey, secretKey);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public SendStatus SendEmail(MailMessage mailMessage)
|
||||
{
|
||||
//Check if we need to query stats
|
||||
RefreshQuotaIfNeeded();
|
||||
if (_quota != null)
|
||||
{
|
||||
lock (SynchRoot)
|
||||
{
|
||||
if (_quota.Max24HourSend <= _quota.SentLast24Hours)
|
||||
{
|
||||
//Quota exceeded
|
||||
//Queu next refresh to +24 hours
|
||||
_lastRefresh = DateTime.UtcNow.AddHours(24);
|
||||
_log.WarnFormat("quota limit reached. setting next check to: {0}", _lastRefresh);
|
||||
return SendStatus.QuotaLimit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
var destination = new Destination
|
||||
{
|
||||
ToAddresses = mailMessage.To.Select(adresses => adresses.Address).ToList(),
|
||||
BccAddresses = mailMessage.Bcc.Select(adresses => adresses.Address).ToList(),
|
||||
CcAddresses = mailMessage.CC.Select(adresses => adresses.Address).ToList(),
|
||||
|
||||
};
|
||||
|
||||
var body = new Body(new Content(mailMessage.Body) { Charset = Encoding.UTF8.WebName });
|
||||
|
||||
if (mailMessage.AlternateViews.Count > 0)
|
||||
{
|
||||
//Get html body
|
||||
foreach (var alternateView in mailMessage.AlternateViews)
|
||||
{
|
||||
if ("text/html".Equals(alternateView.ContentType.MediaType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var stream = alternateView.ContentStream;
|
||||
var buf = new byte[stream.Length];
|
||||
stream.Read(buf, 0, buf.Length);
|
||||
stream.Seek(0, SeekOrigin.Begin);//NOTE:seek to begin to keep HTML body
|
||||
body.Html = new Content(Encoding.UTF8.GetString(buf)) { Charset = Encoding.UTF8.WebName };
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var message = new Message(new Content(mailMessage.Subject), body);
|
||||
|
||||
var seRequest = new SendEmailRequest(mailMessage.From.ToEncodedStringEx(), destination, message);
|
||||
if (mailMessage.ReplyTo != null)
|
||||
seRequest.ReplyToAddresses.Add(mailMessage.ReplyTo.Address);
|
||||
|
||||
ThrottleIfNeeded();
|
||||
|
||||
var response = _emailService.SendEmail(seRequest);
|
||||
_lastSend = DateTime.UtcNow;
|
||||
return response != null ? SendStatus.Ok : SendStatus.Failed;
|
||||
}
|
||||
|
||||
private void ThrottleIfNeeded()
|
||||
{
|
||||
//Check last send and throttle if needed
|
||||
if (_sendWindow != TimeSpan.MinValue)
|
||||
{
|
||||
if (DateTime.UtcNow - _lastSend <= _sendWindow)
|
||||
//Possible BUG: at high frequncies maybe bug with to little differences
|
||||
{
|
||||
//This means that time passed from last send is less then message per second
|
||||
_log.DebugFormat("send rate doesn't fit in send window. sleeping for:{0}", _sendWindow);
|
||||
Thread.Sleep(_sendWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshQuotaIfNeeded()
|
||||
{
|
||||
if (!IsRefreshNeeded()) return;
|
||||
|
||||
lock (SynchRoot)
|
||||
{
|
||||
if (IsRefreshNeeded())//Double check
|
||||
{
|
||||
_log.DebugFormat("refreshing qouta. interval: {0} Last refresh was at: {1}", RefreshTimeout,
|
||||
_lastRefresh);
|
||||
|
||||
//Do quota refresh
|
||||
_lastRefresh = DateTime.UtcNow.AddMinutes(1);
|
||||
try
|
||||
{
|
||||
var quotaRequest = new GetSendQuotaRequest();
|
||||
_quota = _emailService.GetSendQuota(quotaRequest).GetSendQuotaResult;
|
||||
_sendWindow = TimeSpan.FromSeconds(1.0 / _quota.MaxSendRate);
|
||||
_log.DebugFormat("quota: {0}/{1} at {2} mps. send window:{3}", _quota.SentLast24Hours,
|
||||
_quota.Max24HourSend, _quota.MaxSendRate, _sendWindow);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.Error("error refreshing quota", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsRefreshNeeded()
|
||||
{
|
||||
return (DateTime.UtcNow - _lastRefresh) > RefreshTimeout || _quota == null;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,99 +23,113 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security
|
||||
{
|
||||
public class AscRandom : Random
|
||||
{
|
||||
private int inext;
|
||||
private int inextp;
|
||||
private const int MBIG = int.MaxValue;
|
||||
private const int MSEED = 161803398;
|
||||
private const int MZ = 0;
|
||||
private readonly int[] seeds;
|
||||
namespace ASC.Common.Security;
|
||||
|
||||
public class AscRandom : Random
|
||||
{
|
||||
private const int Mbig = int.MaxValue;
|
||||
private const int Mseed = 161803398;
|
||||
private const int Mz = 0;
|
||||
|
||||
private int _inext;
|
||||
private int _inextp;
|
||||
private readonly int[] _seeds;
|
||||
|
||||
public AscRandom() : this(Environment.TickCount) { }
|
||||
|
||||
public AscRandom(int seed)
|
||||
{
|
||||
_seeds = new int[56];
|
||||
var num4 = (seed == int.MinValue) ? int.MaxValue : Math.Abs(seed);
|
||||
var num2 = 161803398 - num4;
|
||||
_seeds[^1] = num2;
|
||||
var num3 = 1;
|
||||
|
||||
for (var i = 1; i < _seeds.Length - 1; i++)
|
||||
{
|
||||
var index = 21 * i % (_seeds.Length - 1);
|
||||
_seeds[index] = num3;
|
||||
num3 = num2 - num3;
|
||||
|
||||
if (num3 < 0)
|
||||
{
|
||||
num3 += int.MaxValue;
|
||||
}
|
||||
|
||||
num2 = _seeds[index];
|
||||
}
|
||||
|
||||
for (var j = 1; j < 5; j++)
|
||||
{
|
||||
for (var k = 1; k < _seeds.Length; k++)
|
||||
{
|
||||
_seeds[k] -= _seeds[1 + ((k + 30) % (_seeds.Length - 1))];
|
||||
|
||||
if (_seeds[k] < 0)
|
||||
{
|
||||
_seeds[k] += int.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_inext = 0;
|
||||
_inextp = 21;
|
||||
}
|
||||
|
||||
public override int Next(int maxValue)
|
||||
{
|
||||
if (maxValue < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(maxValue));
|
||||
}
|
||||
|
||||
return (int)(InternalSample() * 4.6566128752457969E-10 * maxValue);
|
||||
}
|
||||
|
||||
public override void NextBytes(byte[] buffer)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
for (var i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
buffer[i] = (byte)(InternalSample() % (byte.MaxValue + 1));
|
||||
}
|
||||
}
|
||||
|
||||
private int InternalSample()
|
||||
{
|
||||
var inext = _inext;
|
||||
var inextp = _inextp;
|
||||
|
||||
public AscRandom() : this(Environment.TickCount)
|
||||
{
|
||||
}
|
||||
if (++inext >= _seeds.Length - 1)
|
||||
{
|
||||
inext = 1;
|
||||
}
|
||||
|
||||
if (++inextp >= _seeds.Length - 1)
|
||||
{
|
||||
inextp = 1;
|
||||
}
|
||||
|
||||
var num = _seeds[inext] - _seeds[inextp];
|
||||
|
||||
if (num == int.MaxValue)
|
||||
{
|
||||
num--;
|
||||
}
|
||||
|
||||
if (num < 0)
|
||||
{
|
||||
num += int.MaxValue;
|
||||
}
|
||||
|
||||
public AscRandom(int seed)
|
||||
{
|
||||
seeds = new int[56];
|
||||
var num4 = (seed == int.MinValue) ? int.MaxValue : Math.Abs(seed);
|
||||
var num2 = 161803398 - num4;
|
||||
seeds[^1] = num2;
|
||||
var num3 = 1;
|
||||
for (var i = 1; i < seeds.Length - 1; i++)
|
||||
{
|
||||
var index = (21 * i) % (seeds.Length - 1);
|
||||
seeds[index] = num3;
|
||||
num3 = num2 - num3;
|
||||
if (num3 < 0)
|
||||
{
|
||||
num3 += int.MaxValue;
|
||||
}
|
||||
num2 = seeds[index];
|
||||
}
|
||||
for (var j = 1; j < 5; j++)
|
||||
{
|
||||
for (var k = 1; k < seeds.Length; k++)
|
||||
{
|
||||
seeds[k] -= seeds[1 + ((k + 30) % (seeds.Length - 1))];
|
||||
if (seeds[k] < 0)
|
||||
{
|
||||
seeds[k] += int.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
inext = 0;
|
||||
inextp = 21;
|
||||
}
|
||||
_seeds[inext] = num;
|
||||
_inext = inext;
|
||||
_inextp = inextp;
|
||||
|
||||
public override int Next(int maxValue)
|
||||
{
|
||||
if (maxValue < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(maxValue));
|
||||
}
|
||||
return (int)(InternalSample() * 4.6566128752457969E-10 * maxValue);
|
||||
}
|
||||
|
||||
public override void NextBytes(byte[] buffer)
|
||||
{
|
||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
for (var i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
buffer[i] = (byte)(InternalSample() % (byte.MaxValue + 1));
|
||||
}
|
||||
}
|
||||
|
||||
private int InternalSample()
|
||||
{
|
||||
var inext = this.inext;
|
||||
var inextp = this.inextp;
|
||||
if (++inext >= seeds.Length - 1)
|
||||
{
|
||||
inext = 1;
|
||||
}
|
||||
if (++inextp >= seeds.Length - 1)
|
||||
{
|
||||
inextp = 1;
|
||||
}
|
||||
var num = seeds[inext] - seeds[inextp];
|
||||
if (num == int.MaxValue)
|
||||
{
|
||||
num--;
|
||||
}
|
||||
if (num < 0)
|
||||
{
|
||||
num += int.MaxValue;
|
||||
}
|
||||
seeds[inext] = num;
|
||||
this.inext = inext;
|
||||
this.inextp = inextp;
|
||||
return num;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
}
|
@ -23,56 +23,40 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authentication
|
||||
namespace ASC.Common.Security.Authentication;
|
||||
|
||||
[Serializable]
|
||||
public class Account : IAccount
|
||||
{
|
||||
[Serializable]
|
||||
public class Account : IAccount
|
||||
public Guid ID { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public virtual bool IsAuthenticated { get; private set; }
|
||||
public string AuthenticationType => "ASC";
|
||||
|
||||
public Account(Guid id, string name, bool authenticated)
|
||||
{
|
||||
public Account(Guid id, string name, bool authenticated)
|
||||
{
|
||||
ID = id;
|
||||
Name = name;
|
||||
IsAuthenticated = authenticated;
|
||||
}
|
||||
|
||||
#region IAccount Members
|
||||
|
||||
public Guid ID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return MemberwiseClone();
|
||||
}
|
||||
|
||||
public string AuthenticationType
|
||||
{
|
||||
get { return "ASC"; }
|
||||
}
|
||||
|
||||
public virtual bool IsAuthenticated
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is IAccount a && ID.Equals(a.ID);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
ID = id;
|
||||
Name = name;
|
||||
IsAuthenticated = authenticated;
|
||||
}
|
||||
}
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return MemberwiseClone();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is IAccount a && ID.Equals(a.ID);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
@ -23,10 +23,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authentication
|
||||
{
|
||||
public interface IAccount : ISubject, ICloneable
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
namespace ASC.Common.Security.Authentication;
|
||||
|
||||
public interface IAccount : ISubject, ICloneable { }
|
||||
|
@ -24,9 +24,6 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security.Authentication
|
||||
{
|
||||
public interface ISystemAccount : IAccount
|
||||
{
|
||||
}
|
||||
}
|
||||
namespace ASC.Common.Security.Authentication;
|
||||
|
||||
public interface ISystemAccount : IAccount { }
|
||||
|
@ -24,16 +24,13 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security.Authentication
|
||||
namespace ASC.Common.Security.Authentication;
|
||||
|
||||
public interface IUserAccount : IAccount
|
||||
{
|
||||
public interface IUserAccount : IAccount
|
||||
{
|
||||
string Email { get; }
|
||||
string FirstName { get; }
|
||||
|
||||
string LastName { get; }
|
||||
|
||||
string Title { get; }
|
||||
int Tenant { get; }
|
||||
}
|
||||
}
|
||||
string Email { get; }
|
||||
string FirstName { get; }
|
||||
string LastName { get; }
|
||||
string Title { get; }
|
||||
int Tenant { get; }
|
||||
}
|
||||
|
@ -23,14 +23,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authentication
|
||||
namespace ASC.Common.Security.Authentication;
|
||||
|
||||
[Serializable]
|
||||
public class SystemAccount : Account, ISystemAccount
|
||||
{
|
||||
[Serializable]
|
||||
public class SystemAccount : Account, ISystemAccount
|
||||
{
|
||||
public SystemAccount(Guid id, string name, bool authenticated)
|
||||
: base(id, name, authenticated)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
public SystemAccount(Guid id, string name, bool authenticated)
|
||||
: base(id, name, authenticated) { }
|
||||
}
|
||||
|
@ -24,101 +24,111 @@
|
||||
*/
|
||||
|
||||
|
||||
#region usings
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
[Serializable]
|
||||
public class AuthorizingException : Exception
|
||||
{
|
||||
[Serializable]
|
||||
public class AuthorizingException : Exception
|
||||
public ISubject Subject { get; internal set; }
|
||||
public IAction[] Actions { get; internal set; }
|
||||
public override string Message => _message;
|
||||
|
||||
private readonly string _message;
|
||||
|
||||
public AuthorizingException(string message)
|
||||
: base(message) { }
|
||||
|
||||
public AuthorizingException(ISubject subject, IAction[] actions)
|
||||
{
|
||||
private readonly string _Message;
|
||||
|
||||
public AuthorizingException(string message)
|
||||
: base(message)
|
||||
if (actions == null || actions.Length == 0)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(actions));
|
||||
}
|
||||
|
||||
public AuthorizingException(ISubject subject, IAction[] actions)
|
||||
{
|
||||
if (actions == null || actions.Length == 0) throw new ArgumentNullException(nameof(actions));
|
||||
Subject = subject ?? throw new ArgumentNullException(nameof(subject));
|
||||
Actions = actions;
|
||||
var sactions = "";
|
||||
Array.ForEach(actions, action => { sactions += action.ToString() + ", "; });
|
||||
_Message = string.Format(
|
||||
"\"{0}\" access denied \"{1}\"",
|
||||
subject,
|
||||
sactions
|
||||
);
|
||||
}
|
||||
Subject = subject ?? throw new ArgumentNullException(nameof(subject));
|
||||
Actions = actions;
|
||||
var sactions = "";
|
||||
|
||||
public AuthorizingException(ISubject subject, IAction[] actions, ISubject[] denySubjects, IAction[] denyActions)
|
||||
{
|
||||
_Message = FormatErrorMessage(subject, actions, denySubjects, denyActions);
|
||||
}
|
||||
Array.ForEach(actions, action => { sactions += action.ToString() + ", "; });
|
||||
|
||||
protected AuthorizingException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
_Message = info.GetValue("_Message", typeof(string)) as string;
|
||||
Subject = info.GetValue("Subject", typeof(ISubject)) as ISubject;
|
||||
Actions = info.GetValue("Actions", typeof(IAction[])) as IAction[];
|
||||
}
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get { return _Message; }
|
||||
}
|
||||
|
||||
public ISubject Subject { get; internal set; }
|
||||
public IAction[] Actions { get; internal set; }
|
||||
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
info.AddValue("Subject", Subject, typeof(ISubject));
|
||||
info.AddValue("_Message", _Message, typeof(string));
|
||||
info.AddValue("Actions", Actions, typeof(IAction[]));
|
||||
base.GetObjectData(info, context);
|
||||
}
|
||||
|
||||
internal static string FormatErrorMessage(ISubject subject, IAction[] actions, ISubject[] denySubjects,
|
||||
IAction[] denyActions)
|
||||
{
|
||||
if (subject == null) throw new ArgumentNullException(nameof(subject));
|
||||
if (actions == null || actions.Length == 0) throw new ArgumentNullException(nameof(actions));
|
||||
if (denySubjects == null || denySubjects.Length == 0) throw new ArgumentNullException(nameof(denySubjects));
|
||||
if (denyActions == null || denyActions.Length == 0) throw new ArgumentNullException(nameof(denyActions));
|
||||
if (actions.Length != denySubjects.Length || actions.Length != denyActions.Length)
|
||||
throw new ArgumentException();
|
||||
var reasons = "";
|
||||
for (var i = 0; i < actions.Length; i++)
|
||||
{
|
||||
var reason = "";
|
||||
if (denySubjects[i] != null && denyActions[i] != null)
|
||||
reason = string.Format("{0}:{1} access denied {2}.",
|
||||
actions[i].Name,
|
||||
(denySubjects[i] is IRole ? "role:" : "") + denySubjects[i].Name,
|
||||
denyActions[i].Name
|
||||
);
|
||||
else
|
||||
reason = string.Format("{0}: access denied.", actions[i].Name);
|
||||
if (i != actions.Length - 1)
|
||||
reason += ", ";
|
||||
reasons += reason;
|
||||
}
|
||||
var sactions = "";
|
||||
Array.ForEach(actions, action => { sactions += action.ToString() + ", "; });
|
||||
var message = string.Format(
|
||||
"\"{0}\" access denied \"{1}\". Cause: {2}.",
|
||||
(subject is IRole ? "role:" : "") + subject.Name,
|
||||
sactions,
|
||||
reasons
|
||||
);
|
||||
return message;
|
||||
}
|
||||
_message = string.Format(
|
||||
"\"{0}\" access denied \"{1}\"",
|
||||
subject,
|
||||
sactions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public AuthorizingException(ISubject subject, IAction[] actions, ISubject[] denySubjects, IAction[] denyActions) =>
|
||||
_message = FormatErrorMessage(subject, actions, denySubjects, denyActions);
|
||||
|
||||
protected AuthorizingException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
_message = info.GetValue("_Message", typeof(string)) as string;
|
||||
Subject = info.GetValue("Subject", typeof(ISubject)) as ISubject;
|
||||
Actions = info.GetValue("Actions", typeof(IAction[])) as IAction[];
|
||||
}
|
||||
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
info.AddValue("Subject", Subject, typeof(ISubject));
|
||||
info.AddValue("_Message", _message, typeof(string));
|
||||
info.AddValue("Actions", Actions, typeof(IAction[]));
|
||||
base.GetObjectData(info, context);
|
||||
}
|
||||
|
||||
internal static string FormatErrorMessage(ISubject subject, IAction[] actions, ISubject[] denySubjects,
|
||||
IAction[] denyActions)
|
||||
{
|
||||
if (subject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(subject));
|
||||
}
|
||||
if (actions == null || actions.Length == 0)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(actions));
|
||||
}
|
||||
if (denySubjects == null || denySubjects.Length == 0)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(denySubjects));
|
||||
}
|
||||
if (denyActions == null || denyActions.Length == 0)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(denyActions));
|
||||
}
|
||||
if (actions.Length != denySubjects.Length || actions.Length != denyActions.Length)
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < actions.Length; i++)
|
||||
{
|
||||
var action = actions[i];
|
||||
var denyAction = denyActions[i];
|
||||
var denySubject = denySubjects[i];
|
||||
|
||||
string reason;
|
||||
if (denySubject != null && denyAction != null)
|
||||
{
|
||||
reason = $"{action.Name}:{(denySubject is IRole ? "role:" : "") + denySubject.Name} access denied {denyAction.Name}.";
|
||||
}
|
||||
else
|
||||
{
|
||||
reason = $"{action.Name}: access denied.";
|
||||
}
|
||||
if (i != actions.Length - 1)
|
||||
{
|
||||
reason += ", ";
|
||||
}
|
||||
|
||||
sb.Append(reason);
|
||||
}
|
||||
var reasons = sb.ToString();
|
||||
var sactions = "";
|
||||
Array.ForEach(actions, action => { sactions += action.ToString() + ", "; });
|
||||
|
||||
var message = $"\"{(subject is IRole ? "role:" : "") + subject.Name}\" access denied \"{sactions}\". Cause: {reasons}.";
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
@ -24,16 +24,19 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
{
|
||||
public static class AzObjectIdHelper
|
||||
{
|
||||
private static readonly string separator = "|";
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
public static string GetFullObjectId(ISecurityObjectId objectId)
|
||||
public static class AzObjectIdHelper
|
||||
{
|
||||
private static readonly string _separator = "|";
|
||||
|
||||
public static string GetFullObjectId(ISecurityObjectId objectId)
|
||||
{
|
||||
if (objectId == null)
|
||||
{
|
||||
if (objectId == null) return null;
|
||||
return string.Format("{0}{1}{2}", objectId.ObjectType.FullName, separator, objectId.SecurityId);
|
||||
return null;
|
||||
}
|
||||
|
||||
return $"{objectId.ObjectType.FullName}{_separator}{objectId.SecurityId}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,61 +23,67 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
#region usings
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
public class AzObjectSecurityProviderHelper
|
||||
{
|
||||
public class AzObjectSecurityProviderHelper
|
||||
public ISecurityObjectId CurrentObjectId { get; private set; }
|
||||
public bool ObjectRolesSupported => _currSecObjProvider != null && _currSecObjProvider.ObjectRolesSupported;
|
||||
|
||||
private readonly SecurityCallContext _callContext;
|
||||
private readonly bool _currObjIdAsProvider;
|
||||
private ISecurityObjectProvider _currSecObjProvider;
|
||||
|
||||
public AzObjectSecurityProviderHelper(ISecurityObjectId objectId, ISecurityObjectProvider secObjProvider)
|
||||
{
|
||||
private readonly SecurityCallContext callContext;
|
||||
private readonly bool currObjIdAsProvider;
|
||||
private ISecurityObjectProvider currSecObjProvider;
|
||||
_currObjIdAsProvider = false;
|
||||
CurrentObjectId = objectId ?? throw new ArgumentNullException(nameof(objectId));
|
||||
_currSecObjProvider = secObjProvider;
|
||||
|
||||
public AzObjectSecurityProviderHelper(ISecurityObjectId objectId, ISecurityObjectProvider secObjProvider)
|
||||
if (_currSecObjProvider == null && CurrentObjectId is ISecurityObjectProvider securityObjectProvider)
|
||||
{
|
||||
currObjIdAsProvider = false;
|
||||
CurrentObjectId = objectId ?? throw new ArgumentNullException(nameof(objectId));
|
||||
currSecObjProvider = secObjProvider;
|
||||
if (currSecObjProvider == null && CurrentObjectId is ISecurityObjectProvider securityObjectProvider)
|
||||
_currObjIdAsProvider = true;
|
||||
_currSecObjProvider = securityObjectProvider;
|
||||
}
|
||||
|
||||
_callContext = new SecurityCallContext();
|
||||
}
|
||||
|
||||
public IEnumerable<IRole> GetObjectRoles(ISubject account)
|
||||
{
|
||||
var roles = _currSecObjProvider.GetObjectRoles(account, CurrentObjectId, _callContext);
|
||||
|
||||
foreach (var role in roles)
|
||||
{
|
||||
if (!_callContext.RolesList.Contains(role))
|
||||
{
|
||||
currObjIdAsProvider = true;
|
||||
currSecObjProvider = securityObjectProvider;
|
||||
_callContext.RolesList.Add(role);
|
||||
}
|
||||
callContext = new SecurityCallContext();
|
||||
}
|
||||
|
||||
public ISecurityObjectId CurrentObjectId { get; private set; }
|
||||
return roles;
|
||||
}
|
||||
|
||||
public bool ObjectRolesSupported
|
||||
public bool NextInherit()
|
||||
{
|
||||
if (_currSecObjProvider == null || !_currSecObjProvider.InheritSupported)
|
||||
{
|
||||
get { return currSecObjProvider != null && currSecObjProvider.ObjectRolesSupported; }
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerable<IRole> GetObjectRoles(ISubject account)
|
||||
CurrentObjectId = _currSecObjProvider.InheritFrom(CurrentObjectId);
|
||||
if (CurrentObjectId == null)
|
||||
{
|
||||
var roles = currSecObjProvider.GetObjectRoles(account, CurrentObjectId, callContext);
|
||||
foreach (var role in roles)
|
||||
{
|
||||
if (!callContext.RolesList.Contains(role)) callContext.RolesList.Add(role);
|
||||
}
|
||||
return roles;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool NextInherit()
|
||||
if (_currObjIdAsProvider)
|
||||
{
|
||||
if (currSecObjProvider == null || !currSecObjProvider.InheritSupported) return false;
|
||||
CurrentObjectId = currSecObjProvider.InheritFrom(CurrentObjectId);
|
||||
if (CurrentObjectId == null) return false;
|
||||
if (currObjIdAsProvider)
|
||||
{
|
||||
currSecObjProvider = CurrentObjectId as ISecurityObjectProvider;
|
||||
}
|
||||
callContext.ObjectsStack.Insert(0, CurrentObjectId);
|
||||
return currSecObjProvider != null;
|
||||
_currSecObjProvider = CurrentObjectId as ISecurityObjectProvider;
|
||||
}
|
||||
|
||||
_callContext.ObjectsStack.Insert(0, CurrentObjectId);
|
||||
|
||||
return _currSecObjProvider != null;
|
||||
}
|
||||
}
|
@ -23,24 +23,23 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public sealed class Constants
|
||||
{
|
||||
public static readonly Role Admin = new Role(new Guid("cd84e66b-b803-40fc-99f9-b2969a54a1de"), "Admin");
|
||||
public static readonly Role Admin = new Role(new Guid("cd84e66b-b803-40fc-99f9-b2969a54a1de"), "Admin");
|
||||
|
||||
public static readonly Role Everyone = new Role(new Guid("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), "Everyone");
|
||||
public static readonly Role Everyone = new Role(new Guid("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), "Everyone");
|
||||
|
||||
|
||||
public static readonly Role User = new Role(new Guid("abef62db-11a8-4673-9d32-ef1d8af19dc0"), "User");
|
||||
public static readonly Role User = new Role(new Guid("abef62db-11a8-4673-9d32-ef1d8af19dc0"), "User");
|
||||
|
||||
public static readonly Role Visitor = new Role(new Guid("aced04fa-dd96-4b35-af3e-346bf1eb972d"), "Visitor");
|
||||
public static readonly Role Visitor = new Role(new Guid("aced04fa-dd96-4b35-af3e-346bf1eb972d"), "Visitor");
|
||||
|
||||
|
||||
public static readonly Role Member = new Role(new Guid("ba74ca02-873f-43dc-8470-8620c156bc67"), "Member");
|
||||
public static readonly Role Member = new Role(new Guid("ba74ca02-873f-43dc-8470-8620c156bc67"), "Member");
|
||||
|
||||
public static readonly Role Owner = new Role(new Guid("bba32183-a14d-48ed-9d39-c6b4d8925fbf"), "Owner");
|
||||
public static readonly Role Owner = new Role(new Guid("bba32183-a14d-48ed-9d39-c6b4d8925fbf"), "Owner");
|
||||
|
||||
public static readonly Role Self = new Role(new Guid("5d5b7260-f7f7-49f1-a1c9-95fbb6a12604"), "Self");
|
||||
}
|
||||
}
|
||||
public static readonly Role Self = new Role(new Guid("5d5b7260-f7f7-49f1-a1c9-95fbb6a12604"), "Self");
|
||||
}
|
||||
|
@ -23,20 +23,17 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
{
|
||||
[Serializable]
|
||||
public class Ace
|
||||
{
|
||||
public Guid ActionId { get; set; }
|
||||
|
||||
public AceType Reaction { get; set; }
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
[Serializable]
|
||||
public class Ace
|
||||
{
|
||||
public Guid ActionId { get; set; }
|
||||
public AceType Reaction { get; set; }
|
||||
|
||||
public Ace(Guid actionId, AceType reaction)
|
||||
{
|
||||
ActionId = actionId;
|
||||
Reaction = reaction;
|
||||
}
|
||||
}
|
||||
}
|
||||
public Ace(Guid actionId, AceType reaction)
|
||||
{
|
||||
ActionId = actionId;
|
||||
Reaction = reaction;
|
||||
}
|
||||
}
|
||||
|
@ -24,11 +24,10 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
public enum AceType
|
||||
{
|
||||
public enum AceType
|
||||
{
|
||||
Allow,
|
||||
Deny,
|
||||
}
|
||||
}
|
||||
Allow,
|
||||
Deny,
|
||||
}
|
||||
|
@ -23,48 +23,44 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
[Serializable]
|
||||
public class Action : IAction
|
||||
{
|
||||
[Serializable]
|
||||
public class Action : IAction
|
||||
public Guid ID { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public bool AdministratorAlwaysAllow { get; private set; }
|
||||
public bool Conjunction { get; private set; }
|
||||
|
||||
public Action(Guid id, string name)
|
||||
: this(id, name, true, true) { }
|
||||
|
||||
public Action(Guid id, string name, bool administratorAlwaysAllow, bool conjunction)
|
||||
{
|
||||
public Guid ID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public bool AdministratorAlwaysAllow { get; private set; }
|
||||
|
||||
public bool Conjunction { get; private set; }
|
||||
|
||||
|
||||
public Action(Guid id, string name)
|
||||
: this(id, name, true, true)
|
||||
if (id == Guid.Empty)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(id));
|
||||
}
|
||||
|
||||
public Action(Guid id, string name, bool administratorAlwaysAllow, bool conjunction)
|
||||
{
|
||||
if (id == Guid.Empty) throw new ArgumentNullException(nameof(id));
|
||||
|
||||
ID = id;
|
||||
Name = name;
|
||||
AdministratorAlwaysAllow = administratorAlwaysAllow;
|
||||
Conjunction = conjunction;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Action a && a.ID == ID;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
ID = id;
|
||||
Name = name;
|
||||
AdministratorAlwaysAllow = administratorAlwaysAllow;
|
||||
Conjunction = conjunction;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Action a && a.ID == ID;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
@ -23,56 +23,50 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
[Serializable]
|
||||
public sealed class Role : IRole
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class Role : IRole
|
||||
public const string Everyone = "Everyone";
|
||||
public const string Visitors = "Visitors";
|
||||
public const string Users = "Users";
|
||||
public const string Administrators = "Administrators";
|
||||
public const string System = "System";
|
||||
|
||||
public Guid ID { get; internal set; }
|
||||
public string Name { get; internal set; }
|
||||
public string AuthenticationType => "ASC";
|
||||
public bool IsAuthenticated => false;
|
||||
|
||||
|
||||
public Role(Guid id, string name)
|
||||
{
|
||||
public const string Everyone = "Everyone";
|
||||
public const string Visitors = "Visitors";
|
||||
public const string Users = "Users";
|
||||
public const string Administrators = "Administrators";
|
||||
public const string System = "System";
|
||||
|
||||
|
||||
public Guid ID { get; internal set; }
|
||||
|
||||
public string Name { get; internal set; }
|
||||
|
||||
public string AuthenticationType
|
||||
if (id == Guid.Empty)
|
||||
{
|
||||
get { return "ASC"; }
|
||||
throw new ArgumentException(nameof(id));
|
||||
}
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
public bool IsAuthenticated
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
|
||||
public Role(Guid id, string name)
|
||||
{
|
||||
if (id == Guid.Empty) throw new ArgumentException("id");
|
||||
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
|
||||
|
||||
ID = id;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Role r && r.ID == ID;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("Role: {0}", Name);
|
||||
}
|
||||
ID = id;
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Role r && r.ID == ID;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Role: {Name}";
|
||||
}
|
||||
}
|
||||
|
@ -23,16 +23,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
public interface IAction
|
||||
{
|
||||
public interface IAction
|
||||
{
|
||||
Guid ID { get; }
|
||||
|
||||
string Name { get; }
|
||||
|
||||
bool AdministratorAlwaysAllow { get; }
|
||||
|
||||
bool Conjunction { get; }
|
||||
}
|
||||
}
|
||||
Guid ID { get; }
|
||||
string Name { get; }
|
||||
bool AdministratorAlwaysAllow { get; }
|
||||
bool Conjunction { get; }
|
||||
}
|
||||
|
@ -24,9 +24,6 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
{
|
||||
public interface IRole : ISubject
|
||||
{
|
||||
}
|
||||
}
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
public interface IRole : ISubject { }
|
||||
|
@ -23,10 +23,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
public interface ISubject : IIdentity
|
||||
{
|
||||
public interface ISubject : IIdentity
|
||||
{
|
||||
Guid ID { get; }
|
||||
}
|
||||
}
|
||||
Guid ID { get; }
|
||||
}
|
||||
|
@ -24,16 +24,12 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Security.Cryptography
|
||||
namespace ASC.Security.Cryptography;
|
||||
|
||||
public enum HashAlg
|
||||
{
|
||||
public enum HashAlg
|
||||
{
|
||||
MD5,
|
||||
|
||||
SHA1,
|
||||
|
||||
SHA256,
|
||||
|
||||
SHA512
|
||||
}
|
||||
}
|
||||
MD5,
|
||||
SHA1,
|
||||
SHA256,
|
||||
SHA512
|
||||
}
|
||||
|
@ -23,132 +23,146 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Security.Cryptography
|
||||
{
|
||||
public sealed class Hasher
|
||||
{
|
||||
private const HashAlg DefaultAlg = HashAlg.SHA256;
|
||||
|
||||
|
||||
public static byte[] Hash(string data, HashAlg hashAlg)
|
||||
{
|
||||
return ComputeHash(data, hashAlg);
|
||||
}
|
||||
|
||||
public static byte[] Hash(string data)
|
||||
{
|
||||
return Hash(data, DefaultAlg);
|
||||
}
|
||||
|
||||
public static byte[] Hash(byte[] data, HashAlg hashAlg)
|
||||
{
|
||||
return ComputeHash(data, hashAlg);
|
||||
}
|
||||
|
||||
public static byte[] Hash(byte[] data)
|
||||
{
|
||||
return Hash(data, DefaultAlg);
|
||||
}
|
||||
|
||||
public static string Base64Hash(string data, HashAlg hashAlg)
|
||||
{
|
||||
return ComputeHash64(data, hashAlg);
|
||||
}
|
||||
|
||||
public static string Base64Hash(string data)
|
||||
{
|
||||
return Base64Hash(data, DefaultAlg);
|
||||
}
|
||||
|
||||
public static string Base64Hash(byte[] data, HashAlg hashAlg)
|
||||
{
|
||||
return ComputeHash64(data, hashAlg);
|
||||
}
|
||||
|
||||
public static string Base64Hash(byte[] data)
|
||||
{
|
||||
return Base64Hash(data, DefaultAlg);
|
||||
}
|
||||
|
||||
public static bool EqualHash(byte[] dataToCompare, byte[] hash, HashAlg hashAlg)
|
||||
{
|
||||
return string.Equals(
|
||||
ComputeHash64(dataToCompare, hashAlg),
|
||||
B2S64(hash)
|
||||
);
|
||||
}
|
||||
|
||||
public static bool EqualHash(byte[] dataToCompare, byte[] hash)
|
||||
{
|
||||
return EqualHash(dataToCompare, hash, DefaultAlg);
|
||||
}
|
||||
|
||||
public static bool EqualHash(string dataToCompare, string hash, HashAlg hashAlg)
|
||||
{
|
||||
return EqualHash(S2B(dataToCompare), S642B(hash), hashAlg);
|
||||
}
|
||||
|
||||
public static bool EqualHash(string dataToCompare, string hash)
|
||||
{
|
||||
return EqualHash(dataToCompare, hash, DefaultAlg);
|
||||
}
|
||||
|
||||
|
||||
private static HashAlgorithm GetAlg(HashAlg hashAlg)
|
||||
{
|
||||
return hashAlg switch
|
||||
{
|
||||
HashAlg.MD5 => MD5.Create(),
|
||||
HashAlg.SHA1 => SHA1.Create(),
|
||||
HashAlg.SHA256 => SHA256.Create(),
|
||||
HashAlg.SHA512 => SHA512.Create(),
|
||||
_ => SHA256.Create()
|
||||
};
|
||||
}
|
||||
|
||||
private static byte[] S2B(string str)
|
||||
{
|
||||
if (str == null) throw new ArgumentNullException(nameof(str));
|
||||
return Encoding.UTF8.GetBytes(str);
|
||||
}
|
||||
|
||||
private static string B2S(byte[] data)
|
||||
{
|
||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
||||
return Encoding.UTF8.GetString(data);
|
||||
}
|
||||
|
||||
private static byte[] S642B(string str)
|
||||
{
|
||||
if (str == null) throw new ArgumentNullException(nameof(str));
|
||||
return Convert.FromBase64String(str);
|
||||
}
|
||||
|
||||
private static string B2S64(byte[] data)
|
||||
{
|
||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
||||
return Convert.ToBase64String(data);
|
||||
}
|
||||
|
||||
private static byte[] ComputeHash(byte[] data, HashAlg hashAlg)
|
||||
namespace ASC.Security.Cryptography;
|
||||
|
||||
public static class Hasher
|
||||
{
|
||||
private const HashAlg DefaultAlg = HashAlg.SHA256;
|
||||
|
||||
public static byte[] Hash(string data, HashAlg hashAlg)
|
||||
{
|
||||
return ComputeHash(data, hashAlg);
|
||||
}
|
||||
|
||||
public static byte[] Hash(string data)
|
||||
{
|
||||
return Hash(data, DefaultAlg);
|
||||
}
|
||||
|
||||
public static byte[] Hash(byte[] data, HashAlg hashAlg)
|
||||
{
|
||||
return ComputeHash(data, hashAlg);
|
||||
}
|
||||
|
||||
public static byte[] Hash(byte[] data)
|
||||
{
|
||||
return Hash(data, DefaultAlg);
|
||||
}
|
||||
|
||||
public static string Base64Hash(string data, HashAlg hashAlg)
|
||||
{
|
||||
return ComputeHash64(data, hashAlg);
|
||||
}
|
||||
|
||||
public static string Base64Hash(string data)
|
||||
{
|
||||
return Base64Hash(data, DefaultAlg);
|
||||
}
|
||||
|
||||
public static string Base64Hash(byte[] data, HashAlg hashAlg)
|
||||
{
|
||||
return ComputeHash64(data, hashAlg);
|
||||
}
|
||||
|
||||
public static string Base64Hash(byte[] data)
|
||||
{
|
||||
return Base64Hash(data, DefaultAlg);
|
||||
}
|
||||
|
||||
public static bool EqualHash(byte[] dataToCompare, byte[] hash)
|
||||
{
|
||||
return EqualHash(dataToCompare, hash, DefaultAlg);
|
||||
}
|
||||
|
||||
public static bool EqualHash(string dataToCompare, string hash, HashAlg hashAlg)
|
||||
{
|
||||
return EqualHash(S2B(dataToCompare), S642B(hash), hashAlg);
|
||||
}
|
||||
|
||||
public static bool EqualHash(string dataToCompare, string hash)
|
||||
{
|
||||
return EqualHash(dataToCompare, hash, DefaultAlg);
|
||||
}
|
||||
|
||||
public static bool EqualHash(byte[] dataToCompare, byte[] hash, HashAlg hashAlg)
|
||||
{
|
||||
return string.Equals(
|
||||
ComputeHash64(dataToCompare, hashAlg),
|
||||
B2S64(hash)
|
||||
);
|
||||
}
|
||||
|
||||
private static HashAlgorithm GetAlg(HashAlg hashAlg)
|
||||
{
|
||||
return hashAlg switch
|
||||
{
|
||||
using var alg = GetAlg(hashAlg);
|
||||
return alg.ComputeHash(data);
|
||||
}
|
||||
|
||||
private static byte[] ComputeHash(string data, HashAlg hashAlg)
|
||||
{
|
||||
return ComputeHash(S2B(data), hashAlg);
|
||||
}
|
||||
|
||||
private static string ComputeHash64(byte[] data, HashAlg hashAlg)
|
||||
{
|
||||
return B2S64(ComputeHash(data, hashAlg));
|
||||
}
|
||||
|
||||
private static string ComputeHash64(string data, HashAlg hashAlg)
|
||||
{
|
||||
return ComputeHash64(S2B(data), hashAlg);
|
||||
}
|
||||
}
|
||||
HashAlg.MD5 => MD5.Create(),
|
||||
HashAlg.SHA1 => SHA1.Create(),
|
||||
HashAlg.SHA256 => SHA256.Create(),
|
||||
HashAlg.SHA512 => SHA512.Create(),
|
||||
_ => SHA256.Create()
|
||||
};
|
||||
}
|
||||
|
||||
private static byte[] S2B(string str)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(str));
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetBytes(str);
|
||||
}
|
||||
|
||||
private static string B2S(byte[] data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(data));
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetString(data);
|
||||
}
|
||||
|
||||
private static byte[] S642B(string str)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(str));
|
||||
}
|
||||
|
||||
return Convert.FromBase64String(str);
|
||||
}
|
||||
|
||||
private static string B2S64(byte[] data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(data));
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(data);
|
||||
}
|
||||
|
||||
private static byte[] ComputeHash(byte[] data, HashAlg hashAlg)
|
||||
{
|
||||
using var alg = GetAlg(hashAlg);
|
||||
|
||||
return alg.ComputeHash(data);
|
||||
}
|
||||
|
||||
private static byte[] ComputeHash(string data, HashAlg hashAlg)
|
||||
{
|
||||
return ComputeHash(S2B(data), hashAlg);
|
||||
}
|
||||
|
||||
private static string ComputeHash64(byte[] data, HashAlg hashAlg)
|
||||
{
|
||||
return B2S64(ComputeHash(data, hashAlg));
|
||||
}
|
||||
|
||||
private static string ComputeHash64(string data, HashAlg hashAlg)
|
||||
{
|
||||
return ComputeHash64(S2B(data), hashAlg);
|
||||
}
|
||||
}
|
@ -23,56 +23,54 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Security.Cryptography
|
||||
namespace ASC.Security.Cryptography;
|
||||
|
||||
[Singletone]
|
||||
public class InstanceCrypto
|
||||
{
|
||||
[Singletone]
|
||||
public class InstanceCrypto
|
||||
private readonly byte[] _eKey;
|
||||
|
||||
public InstanceCrypto(MachinePseudoKeys machinePseudoKeys)
|
||||
{
|
||||
private byte[] EKey { get; }
|
||||
|
||||
public InstanceCrypto(MachinePseudoKeys machinePseudoKeys)
|
||||
{
|
||||
EKey = machinePseudoKeys.GetMachineConstant(32);
|
||||
}
|
||||
|
||||
public string Encrypt(string data)
|
||||
{
|
||||
return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(data)));
|
||||
}
|
||||
|
||||
public byte[] Encrypt(byte[] data)
|
||||
{
|
||||
using var hasher = Aes.Create();
|
||||
hasher.Key = EKey;
|
||||
hasher.IV = new byte[hasher.BlockSize >> 3];
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
using var ss = new CryptoStream(ms, hasher.CreateEncryptor(), CryptoStreamMode.Write);
|
||||
using var plainTextStream = new MemoryStream(data);
|
||||
plainTextStream.CopyTo(ss);
|
||||
ss.FlushFinalBlock();
|
||||
hasher.Clear();
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
public string Decrypt(string data)
|
||||
{
|
||||
return Decrypt(Convert.FromBase64String(data));
|
||||
}
|
||||
|
||||
public string Decrypt(byte[] data)
|
||||
{
|
||||
using var hasher = Aes.Create();
|
||||
hasher.Key = EKey;
|
||||
hasher.IV = new byte[hasher.BlockSize >> 3];
|
||||
|
||||
using var msDecrypt = new MemoryStream(data);
|
||||
using var csDecrypt = new CryptoStream(msDecrypt, hasher.CreateDecryptor(), CryptoStreamMode.Read);
|
||||
using var srDecrypt = new StreamReader(csDecrypt);
|
||||
|
||||
// Read the decrypted bytes from the decrypting stream
|
||||
// and place them in a string.
|
||||
return srDecrypt.ReadToEnd();
|
||||
}
|
||||
_eKey = machinePseudoKeys.GetMachineConstant(32);
|
||||
}
|
||||
}
|
||||
|
||||
public string Encrypt(string data)
|
||||
{
|
||||
return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(data)));
|
||||
}
|
||||
|
||||
public byte[] Encrypt(byte[] data)
|
||||
{
|
||||
using var hasher = Aes.Create();
|
||||
hasher.Key = _eKey;
|
||||
hasher.IV = new byte[hasher.BlockSize >> 3];
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
using var ss = new CryptoStream(ms, hasher.CreateEncryptor(), CryptoStreamMode.Write);
|
||||
using var plainTextStream = new MemoryStream(data);
|
||||
|
||||
plainTextStream.CopyTo(ss);
|
||||
ss.FlushFinalBlock();
|
||||
hasher.Clear();
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
public string Decrypt(string data) => Decrypt(Convert.FromBase64String(data));
|
||||
|
||||
public string Decrypt(byte[] data)
|
||||
{
|
||||
using var hasher = Aes.Create();
|
||||
hasher.Key = _eKey;
|
||||
hasher.IV = new byte[hasher.BlockSize >> 3];
|
||||
|
||||
using var msDecrypt = new MemoryStream(data);
|
||||
using var csDecrypt = new CryptoStream(msDecrypt, hasher.CreateDecryptor(), CryptoStreamMode.Read);
|
||||
using var srDecrypt = new StreamReader(csDecrypt);
|
||||
|
||||
// Read the decrypted bytes from the decrypting stream
|
||||
// and place them in a string.
|
||||
return srDecrypt.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
@ -23,47 +23,50 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Security.Cryptography
|
||||
namespace ASC.Security.Cryptography;
|
||||
|
||||
[Singletone]
|
||||
public class MachinePseudoKeys
|
||||
{
|
||||
[Singletone]
|
||||
public class MachinePseudoKeys
|
||||
private readonly byte[] _confKey = null;
|
||||
|
||||
public MachinePseudoKeys(IConfiguration configuration)
|
||||
{
|
||||
private readonly byte[] confkey = null;
|
||||
var key = configuration["core:machinekey"];
|
||||
|
||||
public MachinePseudoKeys(IConfiguration configuration)
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
var key = configuration["core:machinekey"];
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
key = configuration["asc:common.machinekey"];
|
||||
}
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
confkey = Encoding.UTF8.GetBytes(key);
|
||||
}
|
||||
key = configuration["asc:common.machinekey"];
|
||||
}
|
||||
|
||||
|
||||
public byte[] GetMachineConstant()
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
if (confkey != null)
|
||||
{
|
||||
return confkey;
|
||||
}
|
||||
|
||||
var path = typeof(MachinePseudoKeys).Assembly.Location;
|
||||
var fi = new FileInfo(path);
|
||||
return BitConverter.GetBytes(fi.CreationTime.ToOADate());
|
||||
}
|
||||
|
||||
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));
|
||||
var rnd = new AscRandom(icnst);
|
||||
var buff = new byte[bytesCount];
|
||||
rnd.NextBytes(buff);
|
||||
return buff;
|
||||
_confKey = Encoding.UTF8.GetBytes(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public byte[] GetMachineConstant()
|
||||
{
|
||||
if (_confKey != null)
|
||||
{
|
||||
return _confKey;
|
||||
}
|
||||
|
||||
var path = typeof(MachinePseudoKeys).Assembly.Location;
|
||||
var fi = new FileInfo(path);
|
||||
|
||||
return BitConverter.GetBytes(fi.CreationTime.ToOADate());
|
||||
}
|
||||
|
||||
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));
|
||||
var rnd = new AscRandom(icnst);
|
||||
var buff = new byte[bytesCount];
|
||||
rnd.NextBytes(buff);
|
||||
|
||||
return buff;
|
||||
}
|
||||
}
|
||||
|
@ -23,69 +23,64 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Security.Cryptography
|
||||
namespace ASC.Security.Cryptography;
|
||||
|
||||
[Singletone]
|
||||
public class PasswordHasher
|
||||
{
|
||||
[Singletone]
|
||||
public class PasswordHasher
|
||||
public int Size { get; private set; }
|
||||
public int Iterations { get; private set; }
|
||||
public string Salt { get; private set; }
|
||||
|
||||
public PasswordHasher(IConfiguration configuration, MachinePseudoKeys machinePseudoKeys)
|
||||
{
|
||||
public PasswordHasher(IConfiguration configuration, MachinePseudoKeys machinePseudoKeys)
|
||||
if (!int.TryParse(configuration["core:password:size"], out var size))
|
||||
{
|
||||
if (!int.TryParse(configuration["core:password:size"], out var size)) size = 256;
|
||||
Size = size;
|
||||
|
||||
if (!int.TryParse(configuration["core.password.iterations"], out var iterations)) iterations = 100000;
|
||||
Iterations = iterations;
|
||||
|
||||
Salt = (configuration["core:password:salt"] ?? "").Trim();
|
||||
if (string.IsNullOrEmpty(Salt))
|
||||
{
|
||||
var salt = Hasher.Hash("{9450BEF7-7D9F-4E4F-A18A-971D8681722D}", HashAlg.SHA256);
|
||||
|
||||
var PasswordHashSaltBytes = KeyDerivation.Pbkdf2(
|
||||
Encoding.UTF8.GetString(machinePseudoKeys.GetMachineConstant()),
|
||||
salt,
|
||||
KeyDerivationPrf.HMACSHA256,
|
||||
Iterations,
|
||||
Size / 8);
|
||||
Salt = BitConverter.ToString(PasswordHashSaltBytes).Replace("-", string.Empty).ToLower();
|
||||
}
|
||||
size = 256;
|
||||
}
|
||||
|
||||
public int Size
|
||||
Size = size;
|
||||
|
||||
if (!int.TryParse(configuration["core.password.iterations"], out var iterations))
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
iterations = 100000;
|
||||
}
|
||||
|
||||
public int Iterations
|
||||
Iterations = iterations;
|
||||
|
||||
Salt = (configuration["core:password:salt"] ?? "").Trim();
|
||||
if (string.IsNullOrEmpty(Salt))
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
var salt = Hasher.Hash("{9450BEF7-7D9F-4E4F-A18A-971D8681722D}", HashAlg.SHA256);
|
||||
|
||||
public string Salt
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string GetClientPassword(string password)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(password)) password = Guid.NewGuid().ToString();
|
||||
|
||||
var salt = new UTF8Encoding(false).GetBytes(Salt);
|
||||
|
||||
var hashBytes = KeyDerivation.Pbkdf2(
|
||||
password,
|
||||
salt,
|
||||
KeyDerivationPrf.HMACSHA256,
|
||||
Iterations,
|
||||
Size / 8);
|
||||
|
||||
var hash = BitConverter.ToString(hashBytes).Replace("-", string.Empty).ToLower();
|
||||
|
||||
return hash;
|
||||
var PasswordHashSaltBytes = KeyDerivation.Pbkdf2(
|
||||
Encoding.UTF8.GetString(machinePseudoKeys.GetMachineConstant()),
|
||||
salt,
|
||||
KeyDerivationPrf.HMACSHA256,
|
||||
Iterations,
|
||||
Size / 8);
|
||||
Salt = BitConverter.ToString(PasswordHashSaltBytes).Replace("-", string.Empty).ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
public string GetClientPassword(string password)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
password = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
var salt = new UTF8Encoding(false).GetBytes(Salt);
|
||||
|
||||
var hashBytes = KeyDerivation.Pbkdf2(
|
||||
password,
|
||||
salt,
|
||||
KeyDerivationPrf.HMACSHA256,
|
||||
Iterations,
|
||||
Size / 8);
|
||||
|
||||
var hash = BitConverter.ToString(hashBytes).Replace("-", string.Empty).ToLower();
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
@ -24,9 +24,6 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security
|
||||
{
|
||||
public interface ISecurityObject : ISecurityObjectId, ISecurityObjectProvider
|
||||
{
|
||||
}
|
||||
}
|
||||
namespace ASC.Common.Security;
|
||||
|
||||
public interface ISecurityObject : ISecurityObjectId, ISecurityObjectProvider { }
|
||||
|
@ -24,11 +24,10 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security
|
||||
namespace ASC.Common.Security;
|
||||
|
||||
public interface ISecurityObjectId
|
||||
{
|
||||
public interface ISecurityObjectId
|
||||
{
|
||||
object SecurityId { get; }
|
||||
Type ObjectType { get; }
|
||||
}
|
||||
}
|
||||
object SecurityId { get; }
|
||||
Type ObjectType { get; }
|
||||
}
|
||||
|
@ -24,15 +24,13 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security
|
||||
{
|
||||
public interface ISecurityObjectProvider
|
||||
{
|
||||
bool InheritSupported { get; }
|
||||
|
||||
bool ObjectRolesSupported { get; }
|
||||
ISecurityObjectId InheritFrom(ISecurityObjectId objectId);
|
||||
|
||||
IEnumerable<IRole> GetObjectRoles(ISubject account, ISecurityObjectId objectId, SecurityCallContext callContext);
|
||||
}
|
||||
}
|
||||
namespace ASC.Common.Security;
|
||||
|
||||
public interface ISecurityObjectProvider
|
||||
{
|
||||
bool InheritSupported { get; }
|
||||
bool ObjectRolesSupported { get; }
|
||||
|
||||
ISecurityObjectId InheritFrom(ISecurityObjectId objectId);
|
||||
IEnumerable<IRole> GetObjectRoles(ISubject account, ISecurityObjectId objectId, SecurityCallContext callContext);
|
||||
}
|
||||
|
@ -23,20 +23,17 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security
|
||||
{
|
||||
public class SecurityCallContext
|
||||
{
|
||||
public SecurityCallContext()
|
||||
{
|
||||
ObjectsStack = new List<ISecurityObjectId>();
|
||||
RolesList = new List<IRole>();
|
||||
}
|
||||
|
||||
public List<ISecurityObjectId> ObjectsStack { get; private set; }
|
||||
|
||||
public List<IRole> RolesList { get; private set; }
|
||||
|
||||
public object UserData { get; set; }
|
||||
}
|
||||
}
|
||||
namespace ASC.Common.Security;
|
||||
|
||||
public class SecurityCallContext
|
||||
{
|
||||
public object UserData { get; set; }
|
||||
public List<ISecurityObjectId> ObjectsStack { get; private set; }
|
||||
public List<IRole> RolesList { get; private set; }
|
||||
|
||||
public SecurityCallContext()
|
||||
{
|
||||
ObjectsStack = new List<ISecurityObjectId>();
|
||||
RolesList = new List<IRole>();
|
||||
}
|
||||
}
|
||||
|
@ -23,31 +23,28 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security
|
||||
{
|
||||
[DebuggerDisplay("ObjectType: {ObjectType.Name}, SecurityId: {SecurityId}")]
|
||||
public class SecurityObjectId : ISecurityObjectId
|
||||
{
|
||||
public object SecurityId { get; private set; }
|
||||
|
||||
public Type ObjectType { get; private set; }
|
||||
|
||||
|
||||
public SecurityObjectId(object id, Type objType)
|
||||
{
|
||||
SecurityId = id;
|
||||
ObjectType = objType ?? throw new ArgumentNullException(nameof(objType));
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return AzObjectIdHelper.GetFullObjectId(this).GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is SecurityObjectId other &&
|
||||
Equals(AzObjectIdHelper.GetFullObjectId(other), AzObjectIdHelper.GetFullObjectId(this));
|
||||
}
|
||||
}
|
||||
namespace ASC.Common.Security;
|
||||
|
||||
[DebuggerDisplay("ObjectType: {ObjectType.Name}, SecurityId: {SecurityId}")]
|
||||
public class SecurityObjectId : ISecurityObjectId
|
||||
{
|
||||
public object SecurityId { get; private set; }
|
||||
public Type ObjectType { get; private set; }
|
||||
|
||||
public SecurityObjectId(object id, Type objType)
|
||||
{
|
||||
SecurityId = id;
|
||||
ObjectType = objType ?? throw new ArgumentNullException(nameof(objType));
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return AzObjectIdHelper.GetFullObjectId(this).GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is SecurityObjectId other &&
|
||||
Equals(AzObjectIdHelper.GetFullObjectId(other), AzObjectIdHelper.GetFullObjectId(this));
|
||||
}
|
||||
}
|
@ -25,110 +25,84 @@
|
||||
|
||||
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
||||
namespace ASC.Common.Threading
|
||||
{
|
||||
public class DistributedTask
|
||||
{
|
||||
public Action<DistributedTask> Publication { get; set; }
|
||||
namespace ASC.Common.Threading;
|
||||
|
||||
protected internal DistributedTaskCache DistributedTaskCache { get; internal set; }
|
||||
|
||||
public int InstanceId
|
||||
public class DistributedTask
|
||||
{
|
||||
public Action<DistributedTask> Publication { get; set; }
|
||||
public int InstanceId
|
||||
{
|
||||
get => DistributedTaskCache.InstanceId;
|
||||
set => DistributedTaskCache.InstanceId = value;
|
||||
}
|
||||
public string Id
|
||||
{
|
||||
get => DistributedTaskCache.Id;
|
||||
protected set => DistributedTaskCache.Id = value ?? "";
|
||||
}
|
||||
public DistributedTaskStatus Status
|
||||
{
|
||||
get => Enum.Parse<DistributedTaskStatus>(DistributedTaskCache.Status);
|
||||
set => DistributedTaskCache.Status = value.ToString();
|
||||
}
|
||||
public Exception Exception
|
||||
{
|
||||
get => new Exception(DistributedTaskCache.Exception);
|
||||
set => DistributedTaskCache.Exception = value?.ToString() ?? "";
|
||||
}
|
||||
protected internal DistributedTaskCache DistributedTaskCache { get; internal set; }
|
||||
|
||||
public DistributedTask()
|
||||
{
|
||||
DistributedTaskCache = new DistributedTaskCache
|
||||
{
|
||||
get
|
||||
{
|
||||
return DistributedTaskCache.InstanceId;
|
||||
}
|
||||
set
|
||||
{
|
||||
DistributedTaskCache.InstanceId = value;
|
||||
}
|
||||
}
|
||||
public string Id
|
||||
Id = Guid.NewGuid().ToString()
|
||||
};
|
||||
}
|
||||
|
||||
public DistributedTask(DistributedTaskCache distributedTaskCache)
|
||||
{
|
||||
DistributedTaskCache = distributedTaskCache;
|
||||
}
|
||||
|
||||
public T GetProperty<T>(string name)
|
||||
{
|
||||
var prop = DistributedTaskCache.Props.FirstOrDefault(r => r.Key == name);
|
||||
if (prop == null)
|
||||
{
|
||||
get
|
||||
{
|
||||
return DistributedTaskCache.Id;
|
||||
}
|
||||
protected set
|
||||
{
|
||||
DistributedTaskCache.Id = value?.ToString() ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
public DistributedTaskStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
return Enum.Parse<DistributedTaskStatus>(DistributedTaskCache.Status);
|
||||
}
|
||||
set
|
||||
{
|
||||
DistributedTaskCache.Status = value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public Exception Exception
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Exception(DistributedTaskCache.Exception);
|
||||
}
|
||||
set
|
||||
{
|
||||
DistributedTaskCache.Exception = value?.ToString() ?? "";
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
public DistributedTask()
|
||||
{
|
||||
DistributedTaskCache = new DistributedTaskCache
|
||||
{
|
||||
Id = Guid.NewGuid().ToString()
|
||||
};
|
||||
}
|
||||
public DistributedTask(DistributedTaskCache distributedTaskCache)
|
||||
{
|
||||
DistributedTaskCache = distributedTaskCache;
|
||||
}
|
||||
|
||||
|
||||
public T GetProperty<T>(string name)
|
||||
{
|
||||
var prop = DistributedTaskCache.Props.FirstOrDefault(r => r.Key == name);
|
||||
|
||||
if (prop == null) return default;
|
||||
|
||||
return JsonSerializer.Deserialize<T>(prop.Value);
|
||||
}
|
||||
|
||||
public void SetProperty(string name, object value)
|
||||
{
|
||||
var prop = new DistributedTaskCache.Types.DistributedTaskCacheProp()
|
||||
{
|
||||
Key = name,
|
||||
Value = JsonSerializer.Serialize(value)
|
||||
};
|
||||
return JsonSerializer.Deserialize<T>(prop.Value);
|
||||
}
|
||||
|
||||
var current = DistributedTaskCache.Props.SingleOrDefault(r => r.Key == name);
|
||||
if (current != null)
|
||||
{
|
||||
DistributedTaskCache.Props.Remove(current);
|
||||
}
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
DistributedTaskCache.Props.Add(prop);
|
||||
}
|
||||
}
|
||||
|
||||
public void PublishChanges()
|
||||
{
|
||||
if (Publication == null)
|
||||
{
|
||||
throw new InvalidOperationException("Publication not found.");
|
||||
}
|
||||
Publication(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void SetProperty(string name, object value)
|
||||
{
|
||||
var prop = new DistributedTaskCache.Types.DistributedTaskCacheProp()
|
||||
{
|
||||
Key = name,
|
||||
Value = JsonSerializer.Serialize(value)
|
||||
};
|
||||
|
||||
var current = DistributedTaskCache.Props.SingleOrDefault(r => r.Key == name);
|
||||
if (current != null)
|
||||
{
|
||||
DistributedTaskCache.Props.Remove(current);
|
||||
}
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
DistributedTaskCache.Props.Add(prop);
|
||||
}
|
||||
}
|
||||
|
||||
public void PublishChanges()
|
||||
{
|
||||
if (Publication == null)
|
||||
{
|
||||
throw new InvalidOperationException("Publication not found.");
|
||||
}
|
||||
|
||||
Publication(this);
|
||||
}
|
||||
}
|
@ -1,64 +1,41 @@
|
||||
namespace ASC.Common.Threading
|
||||
namespace ASC.Common.Threading;
|
||||
|
||||
[Transient]
|
||||
public class DistributedTaskProgress : DistributedTask
|
||||
{
|
||||
[Transient]
|
||||
public class DistributedTaskProgress : DistributedTask
|
||||
public double Percentage
|
||||
{
|
||||
public double Percentage
|
||||
{
|
||||
get
|
||||
{
|
||||
return Math.Min(100.0, Math.Max(0, DistributedTaskCache.Percentage));
|
||||
}
|
||||
set
|
||||
{
|
||||
DistributedTaskCache.Percentage = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
get
|
||||
{
|
||||
return DistributedTaskCache.IsCompleted;
|
||||
}
|
||||
set
|
||||
{
|
||||
DistributedTaskCache.IsCompleted = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected int StepCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return DistributedTaskCache.StepCount;
|
||||
}
|
||||
set
|
||||
{
|
||||
DistributedTaskCache.StepCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected void StepDone()
|
||||
{
|
||||
if (StepCount > 0)
|
||||
{
|
||||
Percentage += 100.0 / StepCount;
|
||||
}
|
||||
|
||||
PublishChanges();
|
||||
}
|
||||
|
||||
public void RunJob()
|
||||
{
|
||||
Percentage = 0;
|
||||
Status = DistributedTaskStatus.Running;
|
||||
DoJob();
|
||||
}
|
||||
|
||||
protected virtual void DoJob()
|
||||
{
|
||||
|
||||
}
|
||||
get => Math.Min(100.0, Math.Max(0, DistributedTaskCache.Percentage));
|
||||
set => DistributedTaskCache.Percentage = value;
|
||||
}
|
||||
}
|
||||
public bool IsCompleted
|
||||
{
|
||||
get => DistributedTaskCache.IsCompleted;
|
||||
set => DistributedTaskCache.IsCompleted = value;
|
||||
}
|
||||
protected int StepCount
|
||||
{
|
||||
get => DistributedTaskCache.StepCount;
|
||||
set => DistributedTaskCache.StepCount = value;
|
||||
}
|
||||
|
||||
public void RunJob()
|
||||
{
|
||||
Percentage = 0;
|
||||
Status = DistributedTaskStatus.Running;
|
||||
|
||||
DoJob();
|
||||
}
|
||||
|
||||
protected virtual void DoJob() { }
|
||||
|
||||
protected void StepDone()
|
||||
{
|
||||
if (StepCount > 0)
|
||||
{
|
||||
Percentage += 100.0 / StepCount;
|
||||
}
|
||||
|
||||
PublishChanges();
|
||||
}
|
||||
}
|
@ -23,307 +23,318 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Threading
|
||||
namespace ASC.Common.Threading;
|
||||
|
||||
[Singletone]
|
||||
public class DistributedTaskCacheNotify
|
||||
{
|
||||
[Singletone]
|
||||
public class DistributedTaskCacheNotify
|
||||
public ConcurrentDictionary<string, CancellationTokenSource> Cancelations { get; }
|
||||
public ICache Cache { get; }
|
||||
|
||||
private readonly ICacheNotify<DistributedTaskCancelation> _cancelTaskNotify;
|
||||
private readonly ICacheNotify<DistributedTaskCache> _taskCacheNotify;
|
||||
|
||||
public DistributedTaskCacheNotify(
|
||||
ICacheNotify<DistributedTaskCancelation> cancelTaskNotify,
|
||||
ICacheNotify<DistributedTaskCache> taskCacheNotify,
|
||||
ICache cache)
|
||||
{
|
||||
public ConcurrentDictionary<string, CancellationTokenSource> Cancelations { get; }
|
||||
public ICache Cache { get; }
|
||||
private readonly ICacheNotify<DistributedTaskCancelation> notify;
|
||||
private readonly ICacheNotify<DistributedTaskCache> notifyCache;
|
||||
Cancelations = new ConcurrentDictionary<string, CancellationTokenSource>();
|
||||
|
||||
public DistributedTaskCacheNotify(
|
||||
ICacheNotify<DistributedTaskCancelation> notify,
|
||||
ICacheNotify<DistributedTaskCache> notifyCache,
|
||||
ICache cache)
|
||||
Cache = cache;
|
||||
|
||||
_cancelTaskNotify = cancelTaskNotify;
|
||||
|
||||
cancelTaskNotify.Subscribe((c) =>
|
||||
{
|
||||
Cancelations = new ConcurrentDictionary<string, CancellationTokenSource>();
|
||||
|
||||
Cache = cache;
|
||||
|
||||
this.notify = notify;
|
||||
|
||||
notify.Subscribe((c) =>
|
||||
{
|
||||
if (Cancelations.TryGetValue(c.Id, out var s))
|
||||
{
|
||||
s.Cancel();
|
||||
}
|
||||
}, CacheNotifyAction.Remove);
|
||||
|
||||
this.notifyCache = notifyCache;
|
||||
|
||||
notifyCache.Subscribe((c) =>
|
||||
if (Cancelations.TryGetValue(c.Id, out var s))
|
||||
{
|
||||
Cache.HashSet(c.Key, c.Id, (DistributedTaskCache)null);
|
||||
}, CacheNotifyAction.Remove);
|
||||
s.Cancel();
|
||||
}
|
||||
}, CacheNotifyAction.Remove);
|
||||
|
||||
notifyCache.Subscribe((c) =>
|
||||
{
|
||||
Cache.HashSet(c.Key, c.Id, c);
|
||||
}, CacheNotifyAction.InsertOrUpdate);
|
||||
}
|
||||
_taskCacheNotify = taskCacheNotify;
|
||||
|
||||
public void CancelTask(string id)
|
||||
{
|
||||
notify.Publish(new DistributedTaskCancelation() { Id = id }, CacheNotifyAction.Remove);
|
||||
}
|
||||
|
||||
public void SetTask(DistributedTask task)
|
||||
taskCacheNotify.Subscribe((c) =>
|
||||
{
|
||||
notifyCache.Publish(task.DistributedTaskCache, CacheNotifyAction.InsertOrUpdate);
|
||||
}
|
||||
|
||||
public void RemoveTask(string id, string key)
|
||||
Cache.HashSet(c.Key, c.Id, (DistributedTaskCache)null);
|
||||
}, CacheNotifyAction.Remove);
|
||||
|
||||
taskCacheNotify.Subscribe((c) =>
|
||||
{
|
||||
notifyCache.Publish(new DistributedTaskCache() { Id = id, Key = key }, CacheNotifyAction.Remove);
|
||||
}
|
||||
Cache.HashSet(c.Key, c.Id, c);
|
||||
}, CacheNotifyAction.InsertOrUpdate);
|
||||
}
|
||||
|
||||
[Singletone(typeof(ConfigureDistributedTaskQueue))]
|
||||
public class DistributedTaskQueueOptionsManager : OptionsManager<DistributedTaskQueue>
|
||||
{
|
||||
public DistributedTaskQueueOptionsManager(IOptionsFactory<DistributedTaskQueue> factory) : base(factory)
|
||||
{
|
||||
}
|
||||
public void CancelTask(string id)
|
||||
{
|
||||
_cancelTaskNotify.Publish(new DistributedTaskCancelation() { Id = id }, CacheNotifyAction.Remove);
|
||||
}
|
||||
|
||||
public DistributedTaskQueue Get<T>() where T : DistributedTask
|
||||
{
|
||||
return Get(typeof(T).FullName);
|
||||
}
|
||||
public void SetTask(DistributedTask task)
|
||||
{
|
||||
_taskCacheNotify.Publish(task.DistributedTaskCache, CacheNotifyAction.InsertOrUpdate);
|
||||
}
|
||||
|
||||
public void RemoveTask(string id, string key)
|
||||
{
|
||||
_taskCacheNotify.Publish(new DistributedTaskCache() { Id = id, Key = key }, CacheNotifyAction.Remove);
|
||||
}
|
||||
}
|
||||
|
||||
[Singletone(typeof(ConfigureDistributedTaskQueue))]
|
||||
public class DistributedTaskQueueOptionsManager : OptionsManager<DistributedTaskQueue>
|
||||
{
|
||||
public DistributedTaskQueueOptionsManager(IOptionsFactory<DistributedTaskQueue> factory) : base(factory) { }
|
||||
|
||||
public DistributedTaskQueue Get<T>() where T : DistributedTask
|
||||
{
|
||||
return Get(typeof(T).FullName);
|
||||
}
|
||||
}
|
||||
|
||||
[Scope]
|
||||
public class ConfigureDistributedTaskQueue : IConfigureNamedOptions<DistributedTaskQueue>
|
||||
{
|
||||
private readonly DistributedTaskCacheNotify _distributedTaskCacheNotify;
|
||||
public readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public ConfigureDistributedTaskQueue(
|
||||
DistributedTaskCacheNotify distributedTaskCacheNotify,
|
||||
IServiceProvider serviceProvider)
|
||||
{
|
||||
_distributedTaskCacheNotify = distributedTaskCacheNotify;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public void Configure(DistributedTaskQueue queue)
|
||||
{
|
||||
queue.DistributedTaskCacheNotify = _distributedTaskCacheNotify;
|
||||
queue.ServiceProvider = _serviceProvider;
|
||||
}
|
||||
|
||||
public void Configure(string name, DistributedTaskQueue options)
|
||||
{
|
||||
Configure(options);
|
||||
options.Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public class DistributedTaskQueue
|
||||
{
|
||||
public IServiceProvider ServiceProvider { get; set; }
|
||||
public DistributedTaskCacheNotify DistributedTaskCacheNotify { get; set; }
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => _name = value + GetType().Name;
|
||||
}
|
||||
public int MaxThreadsCount
|
||||
{
|
||||
set => Scheduler = value <= 0
|
||||
? TaskScheduler.Default
|
||||
: new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, value).ConcurrentScheduler;
|
||||
}
|
||||
|
||||
[Scope]
|
||||
public class ConfigureDistributedTaskQueue : IConfigureNamedOptions<DistributedTaskQueue>
|
||||
private ICache Cache => DistributedTaskCacheNotify.Cache;
|
||||
private TaskScheduler Scheduler { get; set; } = TaskScheduler.Default;
|
||||
|
||||
public static readonly int InstanceId = Process.GetCurrentProcess().Id;
|
||||
private string _name;
|
||||
|
||||
private ConcurrentDictionary<string, CancellationTokenSource> Cancelations
|
||||
{
|
||||
private DistributedTaskCacheNotify DistributedTaskCacheNotify { get; }
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
|
||||
public ConfigureDistributedTaskQueue(DistributedTaskCacheNotify distributedTaskCacheNotify, IServiceProvider serviceProvider)
|
||||
get
|
||||
{
|
||||
DistributedTaskCacheNotify = distributedTaskCacheNotify;
|
||||
ServiceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public void Configure(DistributedTaskQueue queue)
|
||||
{
|
||||
queue.DistributedTaskCacheNotify = DistributedTaskCacheNotify;
|
||||
queue.ServiceProvider = ServiceProvider;
|
||||
}
|
||||
|
||||
public void Configure(string name, DistributedTaskQueue options)
|
||||
{
|
||||
Configure(options);
|
||||
options.Name = name;
|
||||
return DistributedTaskCacheNotify.Cancelations;
|
||||
}
|
||||
}
|
||||
|
||||
public class DistributedTaskQueue
|
||||
{
|
||||
public static readonly int InstanceId;
|
||||
|
||||
private string key;
|
||||
private TaskScheduler Scheduler { get; set; } = TaskScheduler.Default;
|
||||
|
||||
public IServiceProvider ServiceProvider { get; set; }
|
||||
public string Name { get { return key; } set { key = value + GetType().Name; } }
|
||||
private ICache Cache { get => DistributedTaskCacheNotify.Cache; }
|
||||
private ConcurrentDictionary<string, CancellationTokenSource> Cancelations { get => DistributedTaskCacheNotify.Cancelations; }
|
||||
|
||||
public int MaxThreadsCount
|
||||
{
|
||||
set
|
||||
{
|
||||
Scheduler = value <= 0
|
||||
? TaskScheduler.Default
|
||||
: new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, value).ConcurrentScheduler;
|
||||
}
|
||||
}
|
||||
|
||||
public DistributedTaskCacheNotify DistributedTaskCacheNotify { get; set; }
|
||||
|
||||
static DistributedTaskQueue()
|
||||
{
|
||||
InstanceId = Process.GetCurrentProcess().Id;
|
||||
}
|
||||
|
||||
|
||||
public void QueueTask(DistributedTaskProgress taskProgress)
|
||||
{
|
||||
QueueTask((a, b) => taskProgress.RunJob(), taskProgress);
|
||||
}
|
||||
|
||||
public void QueueTask(Action<DistributedTask, CancellationToken> action, DistributedTask distributedTask = null)
|
||||
{
|
||||
if (distributedTask == null)
|
||||
{
|
||||
distributedTask = new DistributedTask();
|
||||
}
|
||||
|
||||
distributedTask.InstanceId = InstanceId;
|
||||
|
||||
var cancelation = new CancellationTokenSource();
|
||||
var token = cancelation.Token;
|
||||
Cancelations[distributedTask.Id] = cancelation;
|
||||
|
||||
var task = new Task(() => { action(distributedTask, token); }, token, TaskCreationOptions.LongRunning);
|
||||
task
|
||||
.ConfigureAwait(false)
|
||||
.GetAwaiter()
|
||||
.OnCompleted(() => OnCompleted(task, distributedTask.Id));
|
||||
|
||||
distributedTask.Status = DistributedTaskStatus.Running;
|
||||
|
||||
if (distributedTask.Publication == null)
|
||||
{
|
||||
distributedTask.Publication = GetPublication();
|
||||
}
|
||||
distributedTask.PublishChanges();
|
||||
|
||||
task.Start(Scheduler);
|
||||
}
|
||||
|
||||
public IEnumerable<DistributedTask> GetTasks()
|
||||
{
|
||||
var tasks = Cache.HashGetAll<DistributedTaskCache>(key).Values.Select(r => new DistributedTask(r)).ToList();
|
||||
tasks.ForEach(t =>
|
||||
{
|
||||
if (t.Publication == null)
|
||||
{
|
||||
t.Publication = GetPublication();
|
||||
}
|
||||
});
|
||||
return tasks;
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetTasks<T>() where T : DistributedTask
|
||||
{
|
||||
var tasks = Cache.HashGetAll<DistributedTaskCache>(key).Values.Select(r =>
|
||||
{
|
||||
var result = ServiceProvider.GetService<T>();
|
||||
result.DistributedTaskCache = r;
|
||||
return result;
|
||||
}).ToList();
|
||||
|
||||
tasks.ForEach(t =>
|
||||
{
|
||||
if (t.Publication == null)
|
||||
{
|
||||
t.Publication = GetPublication();
|
||||
}
|
||||
});
|
||||
return tasks;
|
||||
}
|
||||
|
||||
public T GetTask<T>(string id) where T : DistributedTask
|
||||
{
|
||||
var cache = Cache.HashGet<DistributedTaskCache>(key, id);
|
||||
if (cache != null)
|
||||
{
|
||||
using var scope = ServiceProvider.CreateScope();
|
||||
var task = scope.ServiceProvider.GetService<T>();
|
||||
task.DistributedTaskCache = cache;
|
||||
if (task != null && task.Publication == null)
|
||||
{
|
||||
task.Publication = GetPublication();
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public DistributedTask GetTask(string id)
|
||||
{
|
||||
var cache = Cache.HashGet<DistributedTaskCache>(key, id);
|
||||
if (cache != null)
|
||||
{
|
||||
var task = new DistributedTask();
|
||||
task.DistributedTaskCache = cache;
|
||||
if (task != null && task.Publication == null)
|
||||
{
|
||||
task.Publication = GetPublication();
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetTask(DistributedTask task)
|
||||
{
|
||||
DistributedTaskCacheNotify.SetTask(task);
|
||||
}
|
||||
|
||||
public void RemoveTask(string id)
|
||||
{
|
||||
DistributedTaskCacheNotify.RemoveTask(id, key);
|
||||
}
|
||||
|
||||
public void CancelTask(string id)
|
||||
{
|
||||
DistributedTaskCacheNotify.CancelTask(id);
|
||||
}
|
||||
|
||||
private void OnCompleted(Task task, string id)
|
||||
{
|
||||
var distributedTask = GetTask(id);
|
||||
if (distributedTask != null)
|
||||
{
|
||||
distributedTask.Status = DistributedTaskStatus.Completed;
|
||||
distributedTask.Exception = task.Exception;
|
||||
if (task.IsFaulted)
|
||||
{
|
||||
distributedTask.Status = DistributedTaskStatus.Failted;
|
||||
}
|
||||
if (task.IsCanceled)
|
||||
{
|
||||
distributedTask.Status = DistributedTaskStatus.Canceled;
|
||||
}
|
||||
|
||||
Cancelations.TryRemove(id, out _);
|
||||
|
||||
distributedTask.PublishChanges();
|
||||
}
|
||||
}
|
||||
|
||||
private Action<DistributedTask> GetPublication()
|
||||
{
|
||||
return (t) =>
|
||||
{
|
||||
if (t.DistributedTaskCache != null)
|
||||
{
|
||||
t.DistributedTaskCache.Key = key;
|
||||
}
|
||||
DistributedTaskCacheNotify.SetTask(t);
|
||||
};
|
||||
}
|
||||
public void QueueTask(DistributedTaskProgress taskProgress)
|
||||
{
|
||||
QueueTask((a, b) => taskProgress.RunJob(), taskProgress);
|
||||
}
|
||||
|
||||
public static class DistributedTaskQueueExtention
|
||||
{
|
||||
public static DIHelper AddDistributedTaskQueueService<T>(this DIHelper services, int maxThreadsCount) where T : DistributedTask
|
||||
public void QueueTask(Action<DistributedTask, CancellationToken> action, DistributedTask distributedTask = null)
|
||||
{
|
||||
if (distributedTask == null)
|
||||
{
|
||||
services.TryAdd<DistributedTaskCacheNotify>();
|
||||
services.TryAdd<DistributedTaskQueueOptionsManager>();
|
||||
services.TryAdd<DistributedTaskQueue>();
|
||||
distributedTask = new DistributedTask();
|
||||
}
|
||||
|
||||
var type = typeof(T);
|
||||
if (!type.IsAbstract)
|
||||
distributedTask.InstanceId = InstanceId;
|
||||
|
||||
var cancelation = new CancellationTokenSource();
|
||||
var token = cancelation.Token;
|
||||
Cancelations[distributedTask.Id] = cancelation;
|
||||
|
||||
var task = new Task(() => { action(distributedTask, token); }, token, TaskCreationOptions.LongRunning);
|
||||
|
||||
task.ConfigureAwait(false)
|
||||
.GetAwaiter()
|
||||
.OnCompleted(() => OnCompleted(task, distributedTask.Id));
|
||||
|
||||
distributedTask.Status = DistributedTaskStatus.Running;
|
||||
|
||||
if (distributedTask.Publication == null)
|
||||
{
|
||||
distributedTask.Publication = GetPublication();
|
||||
}
|
||||
|
||||
distributedTask.PublishChanges();
|
||||
|
||||
task.Start(Scheduler);
|
||||
}
|
||||
|
||||
public IEnumerable<DistributedTask> GetTasks()
|
||||
{
|
||||
var tasks = Cache.HashGetAll<DistributedTaskCache>(_name).Values.Select(r => new DistributedTask(r)).ToList();
|
||||
|
||||
tasks.ForEach(t =>
|
||||
{
|
||||
if (t.Publication == null)
|
||||
{
|
||||
services.TryAdd<T>();
|
||||
t.Publication = GetPublication();
|
||||
}
|
||||
});
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetTasks<T>() where T : DistributedTask
|
||||
{
|
||||
var tasks = Cache.HashGetAll<DistributedTaskCache>(_name).Values.Select(r =>
|
||||
{
|
||||
var result = ServiceProvider.GetService<T>();
|
||||
result.DistributedTaskCache = r;
|
||||
|
||||
return result;
|
||||
}).ToList();
|
||||
|
||||
tasks.ForEach(t =>
|
||||
{
|
||||
if (t.Publication == null)
|
||||
{
|
||||
t.Publication = GetPublication();
|
||||
}
|
||||
});
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
public T GetTask<T>(string id) where T : DistributedTask
|
||||
{
|
||||
var cache = Cache.HashGet<DistributedTaskCache>(_name, id);
|
||||
if (cache != null)
|
||||
{
|
||||
using var scope = ServiceProvider.CreateScope();
|
||||
var task = scope.ServiceProvider.GetService<T>();
|
||||
task.DistributedTaskCache = cache;
|
||||
if (task.Publication == null)
|
||||
{
|
||||
task.Publication = GetPublication();
|
||||
}
|
||||
|
||||
services.TryAddSingleton<IConfigureOptions<DistributedTaskQueue>, ConfigureDistributedTaskQueue>();
|
||||
return task;
|
||||
}
|
||||
|
||||
_ = services.Configure<DistributedTaskQueue>(type.Name, r =>
|
||||
return null;
|
||||
}
|
||||
|
||||
public DistributedTask GetTask(string id)
|
||||
{
|
||||
var cache = Cache.HashGet<DistributedTaskCache>(_name, id);
|
||||
if (cache != null)
|
||||
{
|
||||
var task = new DistributedTask();
|
||||
task.DistributedTaskCache = cache;
|
||||
if (task.Publication == null)
|
||||
{
|
||||
r.MaxThreadsCount = maxThreadsCount;
|
||||
task.Publication = GetPublication();
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetTask(DistributedTask task)
|
||||
{
|
||||
DistributedTaskCacheNotify.SetTask(task);
|
||||
}
|
||||
|
||||
public void RemoveTask(string id)
|
||||
{
|
||||
DistributedTaskCacheNotify.RemoveTask(id, _name);
|
||||
}
|
||||
|
||||
public void CancelTask(string id)
|
||||
{
|
||||
DistributedTaskCacheNotify.CancelTask(id);
|
||||
}
|
||||
|
||||
private void OnCompleted(Task task, string id)
|
||||
{
|
||||
var distributedTask = GetTask(id);
|
||||
if (distributedTask != null)
|
||||
{
|
||||
distributedTask.Status = DistributedTaskStatus.Completed;
|
||||
distributedTask.Exception = task.Exception;
|
||||
|
||||
if (task.IsFaulted)
|
||||
{
|
||||
distributedTask.Status = DistributedTaskStatus.Failted;
|
||||
}
|
||||
|
||||
if (task.IsCanceled)
|
||||
{
|
||||
distributedTask.Status = DistributedTaskStatus.Canceled;
|
||||
}
|
||||
|
||||
Cancelations.TryRemove(id, out _);
|
||||
|
||||
distributedTask.PublishChanges();
|
||||
}
|
||||
}
|
||||
|
||||
private Action<DistributedTask> GetPublication()
|
||||
{
|
||||
return (t) =>
|
||||
{
|
||||
if (t.DistributedTaskCache != null)
|
||||
{
|
||||
t.DistributedTaskCache.Key = _name;
|
||||
}
|
||||
|
||||
DistributedTaskCacheNotify.SetTask(t);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class DistributedTaskQueueExtention
|
||||
{
|
||||
public static DIHelper AddDistributedTaskQueueService<T>(this DIHelper services, int maxThreadsCount) where T : DistributedTask
|
||||
{
|
||||
services.TryAdd<DistributedTaskCacheNotify>();
|
||||
services.TryAdd<DistributedTaskQueueOptionsManager>();
|
||||
services.TryAdd<DistributedTaskQueue>();
|
||||
|
||||
var type = typeof(T);
|
||||
|
||||
if (!type.IsAbstract)
|
||||
{
|
||||
services.TryAdd<T>();
|
||||
}
|
||||
|
||||
services.TryAddSingleton<IConfigureOptions<DistributedTaskQueue>, ConfigureDistributedTaskQueue>();
|
||||
|
||||
_ = services.Configure<DistributedTaskQueue>(type.Name, r =>
|
||||
{
|
||||
r.MaxThreadsCount = maxThreadsCount;
|
||||
//r.errorCount = 1;
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
@ -24,14 +24,13 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Threading
|
||||
namespace ASC.Common.Threading;
|
||||
|
||||
public enum DistributedTaskStatus
|
||||
{
|
||||
public enum DistributedTaskStatus
|
||||
{
|
||||
Created,
|
||||
Running,
|
||||
Completed,
|
||||
Canceled,
|
||||
Failted
|
||||
}
|
||||
Created,
|
||||
Running,
|
||||
Completed,
|
||||
Canceled,
|
||||
Failted
|
||||
}
|
||||
|
@ -23,16 +23,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Threading.Progress
|
||||
{
|
||||
public interface IProgressItem : ICloneable
|
||||
{
|
||||
string Id { get; }
|
||||
DistributedTaskStatus Status { get; set; }
|
||||
object Error { get; set; }
|
||||
double Percentage { get; set; }
|
||||
bool IsCompleted { get; set; }
|
||||
namespace ASC.Common.Threading.Progress;
|
||||
|
||||
void RunJob();
|
||||
}
|
||||
}
|
||||
public interface IProgressItem : ICloneable
|
||||
{
|
||||
string Id { get; }
|
||||
DistributedTaskStatus Status { get; set; }
|
||||
object Error { get; set; }
|
||||
double Percentage { get; set; }
|
||||
bool IsCompleted { get; set; }
|
||||
|
||||
void RunJob();
|
||||
}
|
||||
|
@ -1,86 +1,82 @@
|
||||
namespace ASC.Common.Utils
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
public class ConnectionStringCollection : IEnumerable<ConnectionStringSettings>
|
||||
{
|
||||
public class ConnectionStringCollection : IEnumerable<ConnectionStringSettings>
|
||||
private List<ConnectionStringSettings> _data;
|
||||
|
||||
public ConnectionStringSettings this[string name] => _data.FirstOrDefault(r => r.Name == name);
|
||||
|
||||
public ConnectionStringCollection(IEnumerable<ConnectionStringSettings> data)
|
||||
{
|
||||
private List<ConnectionStringSettings> Data { get; set; }
|
||||
|
||||
public ConnectionStringCollection(IEnumerable<ConnectionStringSettings> data) => Data = data.ToList();
|
||||
|
||||
public IEnumerator<ConnectionStringSettings> GetEnumerator()
|
||||
{
|
||||
return Data.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public ConnectionStringSettings this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Data.FirstOrDefault(r => r.Name == name);
|
||||
}
|
||||
}
|
||||
_data = data.ToList();
|
||||
}
|
||||
|
||||
[Singletone]
|
||||
public class ConfigurationExtension
|
||||
public IEnumerator<ConnectionStringSettings> GetEnumerator()
|
||||
{
|
||||
private IConfiguration Configuration { get; }
|
||||
private Lazy<ConnectionStringCollection> ConnectionStringSettings { get; }
|
||||
return _data.GetEnumerator();
|
||||
}
|
||||
|
||||
public ConfigurationExtension(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
ConnectionStringSettings = new Lazy<ConnectionStringCollection>(new ConnectionStringCollection(GetSettings<ConnectionStringSettings>("ConnectionStrings")));
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetSettings<T>(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 T GetSetting<T>(string section) where T : new()
|
||||
{
|
||||
return GetSetting(section, new T());
|
||||
}
|
||||
|
||||
public T GetSetting<T>(string section, T instance)
|
||||
{
|
||||
var sectionSettings = Configuration.GetSection(section);
|
||||
|
||||
sectionSettings.Bind(instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public ConnectionStringCollection GetConnectionStrings()
|
||||
{
|
||||
return ConnectionStringSettings.Value;
|
||||
}
|
||||
|
||||
public ConnectionStringSettings GetConnectionStrings(string key)
|
||||
{
|
||||
return GetConnectionStrings()[key];
|
||||
}
|
||||
|
||||
public string this[string key]
|
||||
{
|
||||
get => Configuration[key];
|
||||
set => Configuration[key] = value;
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
[Singletone]
|
||||
public class ConfigurationExtension
|
||||
{
|
||||
public string this[string key]
|
||||
{
|
||||
get => _configuration[key];
|
||||
set => _configuration[key] = value;
|
||||
}
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly Lazy<ConnectionStringCollection> _connectionStringSettings;
|
||||
|
||||
public ConfigurationExtension(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_connectionStringSettings = new Lazy<ConnectionStringCollection>(new ConnectionStringCollection(GetSettings<ConnectionStringSettings>("ConnectionStrings")));
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetSettings<T>(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 T GetSetting<T>(string section) where T : new()
|
||||
{
|
||||
return GetSetting(section, new T());
|
||||
}
|
||||
|
||||
public T GetSetting<T>(string section, T instance)
|
||||
{
|
||||
var sectionSettings = _configuration.GetSection(section);
|
||||
|
||||
sectionSettings.Bind(instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public ConnectionStringCollection GetConnectionStrings()
|
||||
{
|
||||
return _connectionStringSettings.Value;
|
||||
}
|
||||
|
||||
public ConnectionStringSettings GetConnectionStrings(string key)
|
||||
{
|
||||
return GetConnectionStrings()[key];
|
||||
}
|
||||
}
|
||||
|
@ -23,28 +23,29 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Utils
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
public static class CrossPlatform
|
||||
{
|
||||
public static class CrossPlatform
|
||||
private static char[] _pathSplitCharacters = new char[] { '/', '\\' };
|
||||
|
||||
public static string PathCombine(string basePath, params string[] additional)
|
||||
{
|
||||
public static string PathCombine(string basePath, params string[] additional)
|
||||
var splits = additional.Select(s => s.Split(_pathSplitCharacters)).ToArray();
|
||||
var totalLength = splits.Sum(arr => arr.Length);
|
||||
var segments = new string[totalLength + 1];
|
||||
segments[0] = basePath;
|
||||
var i = 0;
|
||||
|
||||
foreach (var split in splits)
|
||||
{
|
||||
var splits = additional.Select(s => s.Split(pathSplitCharacters)).ToArray();
|
||||
var totalLength = splits.Sum(arr => arr.Length);
|
||||
var segments = new string[totalLength + 1];
|
||||
segments[0] = basePath;
|
||||
var i = 0;
|
||||
foreach (var split in splits)
|
||||
foreach (var value in split)
|
||||
{
|
||||
foreach (var value in split)
|
||||
{
|
||||
i++;
|
||||
segments[i] = value;
|
||||
}
|
||||
i++;
|
||||
segments[i] = value;
|
||||
}
|
||||
return Path.Combine(segments);
|
||||
}
|
||||
|
||||
static char[] pathSplitCharacters = new char[] { '/', '\\' };
|
||||
return Path.Combine(segments);
|
||||
}
|
||||
}
|
||||
|
@ -23,228 +23,247 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Utils
|
||||
{
|
||||
public class DnsLookup
|
||||
{
|
||||
private readonly IDnsResolver _sDnsResolver;
|
||||
private readonly DnsClient _dnsClient;
|
||||
|
||||
public DnsLookup()
|
||||
{
|
||||
_dnsClient = DnsClient.Default;
|
||||
_sDnsResolver = new DnsStubResolver(_dnsClient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get domain MX records
|
||||
/// </summary>
|
||||
/// <param name="domainName">domain name</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>list of MxRecord</returns>
|
||||
public List<MxRecord> GetDomainMxRecords(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
|
||||
var mxRecords = DnsResolve<MxRecord>(domainName, RecordType.Mx);
|
||||
|
||||
return mxRecords;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check existance of MX record in domain DNS
|
||||
/// </summary>
|
||||
/// <param name="domainName">domain name</param>
|
||||
/// <param name="mxRecord">MX record value</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>true if exists and vice versa</returns>
|
||||
public bool IsDomainMxRecordExists(string domainName, string mxRecord)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
|
||||
if (string.IsNullOrEmpty(mxRecord))
|
||||
throw new ArgumentNullException(nameof(mxRecord));
|
||||
|
||||
var mxDomain = DomainName.Parse(mxRecord);
|
||||
|
||||
var records = GetDomainMxRecords(domainName);
|
||||
|
||||
return records.Any(
|
||||
mx => mx.ExchangeDomainName.Equals(mxDomain));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check domain existance
|
||||
/// </summary>
|
||||
/// <param name="domainName"></param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <exception cref="SystemException">if DNS request failed</exception>
|
||||
/// <returns>true if any DNS record exists and vice versa</returns>
|
||||
public bool IsDomainExists(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
|
||||
var dnsMessage = GetDnsMessage(domainName);
|
||||
|
||||
return dnsMessage.AnswerRecords.Any();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get domain A records
|
||||
/// </summary>
|
||||
/// <param name="domainName">domain name</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>list of ARecord</returns>
|
||||
public List<ARecord> GetDomainARecords(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
|
||||
var aRecords = DnsResolve<ARecord>(domainName, RecordType.A);
|
||||
|
||||
return aRecords;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get domain IP addresses list
|
||||
/// </summary>
|
||||
/// <param name="domainName">domain name</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>list of IPAddress</returns>
|
||||
public List<IPAddress> GetDomainIPs(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
|
||||
var addresses = _sDnsResolver.ResolveHost(domainName);
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get domain TXT records
|
||||
/// </summary>
|
||||
/// <param name="domainName">domain name</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>list of TxtRecord</returns>
|
||||
public List<TxtRecord> GetDomainTxtRecords(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
|
||||
var txtRecords = DnsResolve<TxtRecord>(domainName, RecordType.Txt);
|
||||
|
||||
return txtRecords;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check existance of TXT record in domain DNS
|
||||
/// </summary>
|
||||
/// <param name="domainName">domain name</param>
|
||||
/// <param name="recordValue">TXT record value</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>true if exists and vice versa</returns>
|
||||
public bool IsDomainTxtRecordExists(string domainName, string recordValue)
|
||||
{
|
||||
var txtRecords = GetDomainTxtRecords(domainName);
|
||||
|
||||
return
|
||||
txtRecords.Any(
|
||||
txtRecord =>
|
||||
txtRecord.TextData.Trim('\"').Equals(recordValue, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check existance of DKIM record in domain DNS
|
||||
/// </summary>
|
||||
/// <param name="domainName">domain name</param>
|
||||
/// <param name="dkimSelector">DKIM selector (example is "dkim")</param>
|
||||
/// <param name="dkimValue">DKIM record value</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>true if exists and vice versa</returns>
|
||||
public bool IsDomainDkimRecordExists(string domainName, string dkimSelector, string dkimValue)
|
||||
{
|
||||
var dkimRecordName = dkimSelector + "._domainkey." + domainName;
|
||||
|
||||
var txtRecords = GetDomainTxtRecords(dkimRecordName);
|
||||
|
||||
return txtRecords.Any(txtRecord => txtRecord.TextData.Trim('\"').Equals(dkimValue));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check existance Domain in PTR record
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">IP address for PTR check</param>
|
||||
/// <param name="domainName">PTR domain name</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName or ipAddress is empty/null</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>true if exists and vice versa</returns>
|
||||
public bool IsDomainPtrRecordExists(IPAddress ipAddress, string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
|
||||
if (ipAddress == null)
|
||||
throw new ArgumentNullException(nameof(ipAddress));
|
||||
|
||||
var domain = DomainName.Parse(domainName);
|
||||
|
||||
var ptrDomain = _sDnsResolver.ResolvePtr(ipAddress);
|
||||
|
||||
return ptrDomain.Equals(domain);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check existance Domain in PTR record
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">IP address for PTR check</param>
|
||||
/// <param name="domainName">PTR domain name</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName or ipAddress is empty/null</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <exception cref="FormatException">if ipAddress is invalid</exception>
|
||||
/// <returns>true if exists and vice versa</returns>
|
||||
public bool IsDomainPtrRecordExists(string ipAddress, string domainName)
|
||||
{
|
||||
return IsDomainPtrRecordExists(IPAddress.Parse(ipAddress), domainName);
|
||||
}
|
||||
|
||||
private DnsMessage GetDnsMessage(string domainName, RecordType? type = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
|
||||
var domain = DomainName.Parse(domainName);
|
||||
|
||||
var dnsMessage = type.HasValue ? _dnsClient.Resolve(domain, type.Value) : _dnsClient.Resolve(domain);
|
||||
|
||||
if ((dnsMessage == null) ||
|
||||
((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain)))
|
||||
{
|
||||
throw new SystemException(); // DNS request failed
|
||||
}
|
||||
|
||||
return dnsMessage;
|
||||
}
|
||||
|
||||
private List<T> DnsResolve<T>(string domainName, RecordType type)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
|
||||
var dnsMessage = GetDnsMessage(domainName, type);
|
||||
|
||||
return dnsMessage.AnswerRecords.Where(r => r.RecordType == type).Cast<T>().ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
public class DnsLookup
|
||||
{
|
||||
private readonly IDnsResolver _sDnsResolver;
|
||||
private readonly DnsClient _dnsClient;
|
||||
|
||||
public DnsLookup()
|
||||
{
|
||||
_dnsClient = DnsClient.Default;
|
||||
_sDnsResolver = new DnsStubResolver(_dnsClient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get domain MX records
|
||||
/// </summary>
|
||||
/// <param name="domainName">domain name</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>list of MxRecord</returns>
|
||||
public List<MxRecord> GetDomainMxRecords(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
var mxRecords = DnsResolve<MxRecord>(domainName, RecordType.Mx);
|
||||
|
||||
return mxRecords;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check existance of MX record in domain DNS
|
||||
/// </summary>
|
||||
/// <param name="domainName">domain name</param>
|
||||
/// <param name="mxRecord">MX record value</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>true if exists and vice versa</returns>
|
||||
public bool IsDomainMxRecordExists(string domainName, string mxRecord)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(mxRecord))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(mxRecord));
|
||||
}
|
||||
|
||||
var mxDomain = DomainName.Parse(mxRecord);
|
||||
|
||||
var records = GetDomainMxRecords(domainName);
|
||||
|
||||
return records.Any(
|
||||
mx => mx.ExchangeDomainName.Equals(mxDomain));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check domain existance
|
||||
/// </summary>
|
||||
/// <param name="domainName"></param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <exception cref="SystemException">if DNS request failed</exception>
|
||||
/// <returns>true if any DNS record exists and vice versa</returns>
|
||||
public bool IsDomainExists(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
var dnsMessage = GetDnsMessage(domainName);
|
||||
|
||||
return dnsMessage.AnswerRecords.Count != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get domain A records
|
||||
/// </summary>
|
||||
/// <param name="domainName">domain name</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>list of ARecord</returns>
|
||||
public List<ARecord> GetDomainARecords(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
var aRecords = DnsResolve<ARecord>(domainName, RecordType.A);
|
||||
|
||||
return aRecords;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get domain IP addresses list
|
||||
/// </summary>
|
||||
/// <param name="domainName">domain name</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>list of IPAddress</returns>
|
||||
public List<IPAddress> GetDomainIPs(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
var addresses = _sDnsResolver.ResolveHost(domainName);
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get domain TXT records
|
||||
/// </summary>
|
||||
/// <param name="domainName">domain name</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>list of TxtRecord</returns>
|
||||
public List<TxtRecord> GetDomainTxtRecords(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
var txtRecords = DnsResolve<TxtRecord>(domainName, RecordType.Txt);
|
||||
|
||||
return txtRecords;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check existance of TXT record in domain DNS
|
||||
/// </summary>
|
||||
/// <param name="domainName">domain name</param>
|
||||
/// <param name="recordValue">TXT record value</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>true if exists and vice versa</returns>
|
||||
public bool IsDomainTxtRecordExists(string domainName, string recordValue)
|
||||
{
|
||||
var txtRecords = GetDomainTxtRecords(domainName);
|
||||
|
||||
return
|
||||
txtRecords.Any(
|
||||
txtRecord =>
|
||||
txtRecord.TextData.Trim('\"').Equals(recordValue, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check existance of DKIM record in domain DNS
|
||||
/// </summary>
|
||||
/// <param name="domainName">domain name</param>
|
||||
/// <param name="dkimSelector">DKIM selector (example is "dkim")</param>
|
||||
/// <param name="dkimValue">DKIM record value</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName is empty</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>true if exists and vice versa</returns>
|
||||
public bool IsDomainDkimRecordExists(string domainName, string dkimSelector, string dkimValue)
|
||||
{
|
||||
var dkimRecordName = dkimSelector + "._domainkey." + domainName;
|
||||
|
||||
var txtRecords = GetDomainTxtRecords(dkimRecordName);
|
||||
|
||||
return txtRecords.Any(txtRecord => txtRecord.TextData.Trim('\"').Equals(dkimValue));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check existance Domain in PTR record
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">IP address for PTR check</param>
|
||||
/// <param name="domainName">PTR domain name</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName or ipAddress is empty/null</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <returns>true if exists and vice versa</returns>
|
||||
public bool IsDomainPtrRecordExists(IPAddress ipAddress, string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
if (ipAddress == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ipAddress));
|
||||
}
|
||||
|
||||
var domain = DomainName.Parse(domainName);
|
||||
|
||||
var ptrDomain = _sDnsResolver.ResolvePtr(ipAddress);
|
||||
|
||||
return ptrDomain.Equals(domain);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check existance Domain in PTR record
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">IP address for PTR check</param>
|
||||
/// <param name="domainName">PTR domain name</param>
|
||||
/// <exception cref="ArgumentNullException">if domainName or ipAddress is empty/null</exception>
|
||||
/// <exception cref="ArgumentException">if domainName is invalid</exception>
|
||||
/// <exception cref="FormatException">if ipAddress is invalid</exception>
|
||||
/// <returns>true if exists and vice versa</returns>
|
||||
public bool IsDomainPtrRecordExists(string ipAddress, string domainName)
|
||||
{
|
||||
return IsDomainPtrRecordExists(IPAddress.Parse(ipAddress), domainName);
|
||||
}
|
||||
|
||||
private DnsMessage GetDnsMessage(string domainName, RecordType? type = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
var domain = DomainName.Parse(domainName);
|
||||
|
||||
var dnsMessage = type.HasValue ? _dnsClient.Resolve(domain, type.Value) : _dnsClient.Resolve(domain);
|
||||
if ((dnsMessage == null) ||
|
||||
((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain)))
|
||||
{
|
||||
throw new SystemException(); // DNS request failed
|
||||
}
|
||||
|
||||
return dnsMessage;
|
||||
}
|
||||
|
||||
private List<T> DnsResolve<T>(string domainName, RecordType type)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
var dnsMessage = GetDnsMessage(domainName, type);
|
||||
|
||||
return dnsMessage.AnswerRecords.Where(r => r.RecordType == type).Cast<T>().ToList();
|
||||
}
|
||||
}
|
@ -23,82 +23,94 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Utils
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
public static class HtmlUtil
|
||||
{
|
||||
public static class HtmlUtil
|
||||
private static readonly Regex _tagReplacer
|
||||
= new Regex("<[^>]*>", RegexOptions.Multiline | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex _commentsReplacer
|
||||
= new Regex("<!--(?s).*?-->", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex _xssReplacer
|
||||
= new Regex(@"<\s*(style|script)[^>]*>(.*?)<\s*/\s*(style|script)>", RegexOptions.IgnoreCase
|
||||
| RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.Singleline);
|
||||
|
||||
private static readonly Regex _worder =
|
||||
new Regex(@"\S+", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
|
||||
public static string GetText(string html, int maxLength = 0, string endBlockTemplate = "...")
|
||||
{
|
||||
private static readonly Regex tagReplacer = new Regex("<[^>]*>", RegexOptions.Multiline | RegexOptions.Compiled);
|
||||
private static readonly Regex commentsReplacer = new Regex("<!--(?s).*?-->", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
private static readonly Regex xssReplacer = new Regex(@"<\s*(style|script)[^>]*>(.*?)<\s*/\s*(style|script)>", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.Singleline);
|
||||
private static readonly Regex Worder = new Regex(@"\S+", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
var unformatedText = string.Empty;
|
||||
|
||||
|
||||
public static string GetText(string html, int maxLength = 0, string endBlockTemplate = "...")
|
||||
if (!string.IsNullOrEmpty(html))
|
||||
{
|
||||
var unformatedText = string.Empty;
|
||||
if (!string.IsNullOrEmpty(html))
|
||||
html = _xssReplacer.Replace(html, string.Empty); //Clean malicious tags. <script> <style>
|
||||
|
||||
if (string.IsNullOrEmpty(html))
|
||||
{
|
||||
html = xssReplacer.Replace(html, string.Empty); //Clean malicious tags. <script> <style>
|
||||
return html;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(html))
|
||||
{
|
||||
return html;
|
||||
}
|
||||
unformatedText = _tagReplacer.Replace(html, string.Empty);
|
||||
|
||||
unformatedText = tagReplacer.Replace(html, string.Empty);
|
||||
if (!string.IsNullOrEmpty(unformatedText))
|
||||
{
|
||||
// kill comments
|
||||
unformatedText = _commentsReplacer.Replace(unformatedText, string.Empty);
|
||||
unformatedText = unformatedText.Trim();
|
||||
|
||||
if (!string.IsNullOrEmpty(unformatedText))
|
||||
{
|
||||
// kill comments
|
||||
unformatedText = commentsReplacer.Replace(unformatedText, string.Empty);
|
||||
unformatedText = unformatedText.Trim();
|
||||
|
||||
if (!string.IsNullOrEmpty(unformatedText))
|
||||
if (maxLength == 0 || unformatedText.Length < maxLength)
|
||||
{
|
||||
if (maxLength == 0 || unformatedText.Length < maxLength)
|
||||
{
|
||||
return HttpUtility.HtmlDecode(unformatedText);
|
||||
}
|
||||
return HttpUtility.HtmlDecode(unformatedText);
|
||||
}
|
||||
|
||||
//Set maximum length with end block
|
||||
maxLength = Math.Max(0, maxLength - endBlockTemplate.Length);
|
||||
var startIndex = Math.Max(0, Math.Min(unformatedText.Length - 1, maxLength));
|
||||
var countToScan = Math.Max(0, startIndex - 1);
|
||||
//Set maximum length with end block
|
||||
maxLength = Math.Max(0, maxLength - endBlockTemplate.Length);
|
||||
var startIndex = Math.Max(0, Math.Min(unformatedText.Length - 1, maxLength));
|
||||
var countToScan = Math.Max(0, startIndex - 1);
|
||||
|
||||
var lastSpaceIndex = unformatedText.LastIndexOf(' ', startIndex, countToScan);
|
||||
var lastSpaceIndex = unformatedText.LastIndexOf(' ', startIndex, countToScan);
|
||||
|
||||
unformatedText = lastSpaceIndex > 0 && lastSpaceIndex < unformatedText.Length
|
||||
? unformatedText.Remove(lastSpaceIndex)
|
||||
: unformatedText.Substring(0, maxLength);
|
||||
if (!string.IsNullOrEmpty(endBlockTemplate))
|
||||
{
|
||||
unformatedText += endBlockTemplate;
|
||||
}
|
||||
unformatedText = lastSpaceIndex > 0 && lastSpaceIndex < unformatedText.Length
|
||||
? unformatedText.Remove(lastSpaceIndex)
|
||||
: unformatedText.Substring(0, maxLength);
|
||||
|
||||
if (!string.IsNullOrEmpty(endBlockTemplate))
|
||||
{
|
||||
unformatedText += endBlockTemplate;
|
||||
}
|
||||
}
|
||||
}
|
||||
return HttpUtility.HtmlDecode(unformatedText);//TODO:!!!
|
||||
}
|
||||
|
||||
public static string ToPlainText(string html)
|
||||
return HttpUtility.HtmlDecode(unformatedText);//TODO:!!!
|
||||
}
|
||||
|
||||
public static string ToPlainText(string html)
|
||||
{
|
||||
return GetText(html);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The function highlight all words in htmlText by searchText.
|
||||
/// </summary>
|
||||
/// <param name="searchText">the space separated string</param>
|
||||
/// <param name="htmlText">html for highlight</param>
|
||||
/// <param name="withoutLink"></param>
|
||||
/// <returns>highlighted html</returns>
|
||||
public static string SearchTextHighlight(string searchText, string htmlText, bool withoutLink = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(searchText) || string.IsNullOrEmpty(htmlText))
|
||||
{
|
||||
return GetText(html);
|
||||
return htmlText;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The function highlight all words in htmlText by searchText.
|
||||
/// </summary>
|
||||
/// <param name="searchText">the space separated string</param>
|
||||
/// <param name="htmlText">html for highlight</param>
|
||||
/// <param name="withoutLink"></param>
|
||||
/// <returns>highlighted html</returns>
|
||||
public static string SearchTextHighlight(string searchText, string htmlText, bool withoutLink = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(searchText) || string.IsNullOrEmpty(htmlText)) return htmlText;
|
||||
|
||||
var regexpstr = Worder.Matches(searchText).Cast<Match>().Select(m => m.Value).Distinct().Aggregate((r, n) => r + "|" + n);
|
||||
var wordsFinder = new Regex(Regex.Escape(regexpstr), RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Multiline);
|
||||
return wordsFinder.Replace(htmlText, m => string.Format("<span class='searchTextHighlight{1}'>{0}</span>", m.Value, withoutLink ? " bold" : string.Empty));
|
||||
}
|
||||
var regexpstr = _worder.Matches(searchText).Select(m => m.Value).Distinct().Aggregate((r, n) => r + "|" + n);
|
||||
var wordsFinder = new Regex(Regex.Escape(regexpstr), RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Multiline);
|
||||
return wordsFinder.Replace(htmlText, m => "<span class='searchTextHighlight" + (withoutLink ? " bold" : string.Empty) + "'>" + m.Value + "</span>");
|
||||
}
|
||||
}
|
@ -26,186 +26,190 @@
|
||||
|
||||
using HttpContext = Microsoft.AspNetCore.Http.HttpContext;
|
||||
|
||||
namespace System.Web
|
||||
{
|
||||
public static class HttpRequestExtensions
|
||||
{
|
||||
public static readonly string UrlRewriterHeader = "X-REWRITER-URL";
|
||||
|
||||
|
||||
public static Uri GetUrlRewriter(this HttpRequest request)
|
||||
{
|
||||
return request != null ? GetUrlRewriter(request.Headers, request) : null;
|
||||
}
|
||||
public static Uri Url(this HttpRequest request)
|
||||
{
|
||||
return request != null ? new Uri(request.GetDisplayUrl()) : null;
|
||||
}
|
||||
|
||||
/*public static Uri GetUrlRewriter(this HttpRequestBase request)
|
||||
{
|
||||
return request != null ? GetUrlRewriter(request.Headers, request.Url) : null;
|
||||
}*/
|
||||
|
||||
public static Uri GetUrlRewriter(IHeaderDictionary headers, HttpRequest request)
|
||||
{
|
||||
if (headers != null)
|
||||
{
|
||||
var h = headers[UrlRewriterHeader];
|
||||
var rewriterUri = !string.IsNullOrEmpty(h) ? ParseRewriterUrl(h) : null;
|
||||
if (request != null && rewriterUri != null)
|
||||
{
|
||||
var result = new UriBuilder()
|
||||
{
|
||||
Scheme = rewriterUri.Scheme,
|
||||
Host = rewriterUri.Host,
|
||||
Port = rewriterUri.Port
|
||||
};
|
||||
result.Query = request.QueryString.Value;
|
||||
result.Path = request.Path.Value;
|
||||
return result.Uri;
|
||||
}
|
||||
}
|
||||
namespace System.Web;
|
||||
|
||||
if (request != null && request.Query != null)
|
||||
{
|
||||
var h = request.Query[UrlRewriterHeader];
|
||||
var rewriterUri = !string.IsNullOrEmpty(h) ? ParseRewriterUrl(h) : null;
|
||||
if (rewriterUri != null)
|
||||
{
|
||||
var result = new UriBuilder()
|
||||
{
|
||||
Scheme = rewriterUri.Scheme,
|
||||
Host = rewriterUri.Host,
|
||||
Port = rewriterUri.Port
|
||||
};
|
||||
result.Query = request.QueryString.Value;
|
||||
result.Path = request.Path.Value;
|
||||
|
||||
return result.Uri;
|
||||
}
|
||||
}
|
||||
|
||||
return request.Url();
|
||||
}
|
||||
|
||||
public static Uri PushRewritenUri(this HttpContext context)
|
||||
{
|
||||
return context != null ? PushRewritenUri(context, GetUrlRewriter(context.Request)) : null;
|
||||
}
|
||||
|
||||
public static Uri PushRewritenUri(this HttpContext context, Uri rewrittenUri)
|
||||
{
|
||||
Uri oldUri = null;
|
||||
if (context != null)
|
||||
{
|
||||
var request = context.Request;
|
||||
|
||||
var url = new Uri(request.GetDisplayUrl());
|
||||
|
||||
if (url != rewrittenUri)
|
||||
{
|
||||
var requestUri = url;
|
||||
try
|
||||
{
|
||||
//Push it
|
||||
request.Headers.SetCommaSeparatedValues("HTTPS", rewrittenUri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase) ? "on" : "off");
|
||||
request.Headers.SetCommaSeparatedValues("SERVER_NAME", rewrittenUri.Host);
|
||||
request.Headers.SetCommaSeparatedValues("SERVER_PORT",
|
||||
rewrittenUri.Port.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
if (rewrittenUri.IsDefaultPort)
|
||||
{
|
||||
request.Headers.SetCommaSeparatedValues("HTTP_HOST",
|
||||
rewrittenUri.Host);
|
||||
}
|
||||
else
|
||||
{
|
||||
request.Headers.SetCommaSeparatedValues("HTTP_HOST",
|
||||
rewrittenUri.Host + ":" + requestUri.Port);
|
||||
}
|
||||
//Hack:
|
||||
typeof(HttpRequest).InvokeMember("_url",
|
||||
BindingFlags.NonPublic | BindingFlags.SetField |
|
||||
BindingFlags.Instance,
|
||||
null, request,
|
||||
new object[] { null });
|
||||
oldUri = requestUri;
|
||||
context.Items["oldUri"] = oldUri;
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
return oldUri;
|
||||
}
|
||||
|
||||
public static Uri PopRewritenUri(this HttpContext context)
|
||||
{
|
||||
if (context != null && context.Items["oldUri"] != null)
|
||||
{
|
||||
var rewriteTo = context.Items["oldUri"] as Uri;
|
||||
if (rewriteTo != null)
|
||||
{
|
||||
return PushRewritenUri(context, rewriteTo);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool DesktopApp(this HttpRequest request)
|
||||
public static class HttpRequestExtensions
|
||||
{
|
||||
public static readonly string UrlRewriterHeader = "X-REWRITER-URL";
|
||||
|
||||
public static Uri GetUrlRewriter(this HttpRequest request)
|
||||
{
|
||||
return request != null ? GetUrlRewriter(request.Headers, request) : null;
|
||||
}
|
||||
|
||||
public static Uri Url(this HttpRequest request)
|
||||
{
|
||||
return request != null ? new Uri(request.GetDisplayUrl()) : null;
|
||||
}
|
||||
|
||||
/*public static Uri GetUrlRewriter(this HttpRequestBase request)
|
||||
{
|
||||
return request != null ? GetUrlRewriter(request.Headers, request.Url) : null;
|
||||
}*/
|
||||
|
||||
public static Uri GetUrlRewriter(IHeaderDictionary headers, HttpRequest request)
|
||||
{
|
||||
if (headers != null)
|
||||
{
|
||||
return request != null
|
||||
&& (!string.IsNullOrEmpty(request.Query["desktop"])
|
||||
|| !string.IsNullOrEmpty(request.Headers[HeaderNames.UserAgent]) && request.Headers[HeaderNames.UserAgent].ToString().Contains("AscDesktopEditor"));
|
||||
}
|
||||
|
||||
public static bool SailfishApp(this HttpRequest request)
|
||||
{
|
||||
return request != null
|
||||
&& (!string.IsNullOrEmpty(request.Headers["sailfish"])
|
||||
|| !string.IsNullOrEmpty(request.Headers[HeaderNames.UserAgent]) && request.Headers[HeaderNames.UserAgent].ToString().Contains("SailfishOS"));
|
||||
}
|
||||
|
||||
|
||||
private static Uri ParseRewriterUrl(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
const StringComparison cmp = StringComparison.OrdinalIgnoreCase;
|
||||
if (0 < s.Length && (s.StartsWith("0", cmp)))
|
||||
{
|
||||
s = Uri.UriSchemeHttp + s.Substring(1);
|
||||
}
|
||||
else if (3 < s.Length && s.StartsWith("OFF", cmp))
|
||||
{
|
||||
s = Uri.UriSchemeHttp + s.Substring(3);
|
||||
}
|
||||
else if (0 < s.Length && (s.StartsWith("1", cmp)))
|
||||
{
|
||||
s = Uri.UriSchemeHttps + s.Substring(1);
|
||||
}
|
||||
else if (2 < s.Length && s.StartsWith("ON", cmp))
|
||||
{
|
||||
s = Uri.UriSchemeHttps + s.Substring(2);
|
||||
}
|
||||
else if (s.StartsWith(Uri.UriSchemeHttp + "%3A%2F%2F", cmp) || s.StartsWith(Uri.UriSchemeHttps + "%3A%2F%2F", cmp))
|
||||
{
|
||||
s = HttpUtility.UrlDecode(s);
|
||||
}
|
||||
|
||||
Uri.TryCreate(s, UriKind.Absolute, out var result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string GetUserHostAddress(this HttpRequest request)
|
||||
{
|
||||
return request.HttpContext.Features.Get<IHttpConnectionFeature>()?.RemoteIpAddress.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
var h = headers[UrlRewriterHeader];
|
||||
var rewriterUri = !string.IsNullOrEmpty(h) ? ParseRewriterUrl(h) : null;
|
||||
|
||||
if (request != null && rewriterUri != null)
|
||||
{
|
||||
var result = new UriBuilder()
|
||||
{
|
||||
Scheme = rewriterUri.Scheme,
|
||||
Host = rewriterUri.Host,
|
||||
Port = rewriterUri.Port
|
||||
};
|
||||
|
||||
result.Query = request.QueryString.Value;
|
||||
result.Path = request.Path.Value;
|
||||
|
||||
return result.Uri;
|
||||
}
|
||||
}
|
||||
|
||||
if (request != null && request.Query != null)
|
||||
{
|
||||
var h = request.Query[UrlRewriterHeader];
|
||||
var rewriterUri = !string.IsNullOrEmpty(h) ? ParseRewriterUrl(h) : null;
|
||||
if (rewriterUri != null)
|
||||
{
|
||||
var result = new UriBuilder()
|
||||
{
|
||||
Scheme = rewriterUri.Scheme,
|
||||
Host = rewriterUri.Host,
|
||||
Port = rewriterUri.Port
|
||||
};
|
||||
result.Query = request.QueryString.Value;
|
||||
result.Path = request.Path.Value;
|
||||
|
||||
return result.Uri;
|
||||
}
|
||||
}
|
||||
|
||||
return request.Url();
|
||||
}
|
||||
|
||||
public static Uri PushRewritenUri(this HttpContext context)
|
||||
{
|
||||
return context != null ? PushRewritenUri(context, GetUrlRewriter(context.Request)) : null;
|
||||
}
|
||||
|
||||
public static Uri PushRewritenUri(this HttpContext context, Uri rewrittenUri)
|
||||
{
|
||||
Uri oldUri = null;
|
||||
|
||||
if (context != null)
|
||||
{
|
||||
var request = context.Request;
|
||||
|
||||
var url = new Uri(request.GetDisplayUrl());
|
||||
|
||||
if (url != rewrittenUri)
|
||||
{
|
||||
var requestUri = url;
|
||||
try
|
||||
{
|
||||
//Push it
|
||||
request.Headers.SetCommaSeparatedValues("HTTPS", rewrittenUri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase) ? "on" : "off");
|
||||
request.Headers.SetCommaSeparatedValues("SERVER_NAME", rewrittenUri.Host);
|
||||
request.Headers.SetCommaSeparatedValues("SERVER_PORT",
|
||||
rewrittenUri.Port.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
if (rewrittenUri.IsDefaultPort)
|
||||
{
|
||||
request.Headers.SetCommaSeparatedValues("HTTP_HOST",
|
||||
rewrittenUri.Host);
|
||||
}
|
||||
else
|
||||
{
|
||||
request.Headers.SetCommaSeparatedValues("HTTP_HOST",
|
||||
rewrittenUri.Host + ":" + requestUri.Port);
|
||||
}
|
||||
//Hack:
|
||||
typeof(HttpRequest).InvokeMember("_url",
|
||||
BindingFlags.NonPublic | BindingFlags.SetField |
|
||||
BindingFlags.Instance,
|
||||
null, request,
|
||||
new object[] { null });
|
||||
oldUri = requestUri;
|
||||
context.Items["oldUri"] = oldUri;
|
||||
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
|
||||
return oldUri;
|
||||
}
|
||||
|
||||
public static Uri PopRewritenUri(this HttpContext context)
|
||||
{
|
||||
if (context != null && context.Items["oldUri"] != null)
|
||||
{
|
||||
var rewriteTo = context.Items["oldUri"] as Uri;
|
||||
|
||||
if (rewriteTo != null)
|
||||
{
|
||||
return PushRewritenUri(context, rewriteTo);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool DesktopApp(this HttpRequest request)
|
||||
{
|
||||
return request != null
|
||||
&& (!string.IsNullOrEmpty(request.Query["desktop"])
|
||||
|| !string.IsNullOrEmpty(request.Headers[HeaderNames.UserAgent]) && request.Headers[HeaderNames.UserAgent].ToString().Contains("AscDesktopEditor"));
|
||||
}
|
||||
|
||||
public static bool SailfishApp(this HttpRequest request)
|
||||
{
|
||||
return request != null
|
||||
&& (!string.IsNullOrEmpty(request.Headers["sailfish"])
|
||||
|| !string.IsNullOrEmpty(request.Headers[HeaderNames.UserAgent]) && request.Headers[HeaderNames.UserAgent].ToString().Contains("SailfishOS"));
|
||||
}
|
||||
|
||||
public static string GetUserHostAddress(this HttpRequest request)
|
||||
{
|
||||
return request.HttpContext.Features.Get<IHttpConnectionFeature>()?.RemoteIpAddress.ToString();
|
||||
}
|
||||
|
||||
private static Uri ParseRewriterUrl(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
const StringComparison cmp = StringComparison.OrdinalIgnoreCase;
|
||||
if (0 < s.Length && s.StartsWith('0'))
|
||||
{
|
||||
s = Uri.UriSchemeHttp + s.Substring(1);
|
||||
}
|
||||
else if (3 < s.Length && s.StartsWith("OFF", cmp))
|
||||
{
|
||||
s = Uri.UriSchemeHttp + s.Substring(3);
|
||||
}
|
||||
else if (0 < s.Length && s.StartsWith('1'))
|
||||
{
|
||||
s = Uri.UriSchemeHttps + s.Substring(1);
|
||||
}
|
||||
else if (2 < s.Length && s.StartsWith("ON", cmp))
|
||||
{
|
||||
s = Uri.UriSchemeHttps + s.Substring(2);
|
||||
}
|
||||
else if (s.StartsWith(Uri.UriSchemeHttp + "%3A%2F%2F", cmp) || s.StartsWith(Uri.UriSchemeHttps + "%3A%2F%2F", cmp))
|
||||
{
|
||||
s = HttpUtility.UrlDecode(s);
|
||||
}
|
||||
|
||||
Uri.TryCreate(s, UriKind.Absolute, out var result);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -27,70 +27,69 @@
|
||||
using Formatting = Newtonsoft.Json.Formatting;
|
||||
using IJsonSerializer = JWT.IJsonSerializer;
|
||||
|
||||
namespace ASC.Web.Core.Files
|
||||
namespace ASC.Web.Core.Files;
|
||||
|
||||
public static class JsonWebToken
|
||||
{
|
||||
public class JsonWebToken
|
||||
public static string Encode(object payload, string key)
|
||||
{
|
||||
public static string Encode(object payload, string key)
|
||||
{
|
||||
var (serializer, algorithm, urlEncoder) = GetSettings();
|
||||
var encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
|
||||
var (serializer, algorithm, urlEncoder) = GetSettings();
|
||||
var encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
|
||||
|
||||
return encoder.Encode(payload, key);
|
||||
}
|
||||
|
||||
public static string Decode(string token, string key, bool verify = true, bool baseSerializer = false)
|
||||
{
|
||||
var (serializer, algorithm, urlEncoder) = GetSettings(baseSerializer);
|
||||
|
||||
var provider = new UtcDateTimeProvider();
|
||||
IJwtValidator validator = new JwtValidator(serializer, provider);
|
||||
|
||||
var decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
|
||||
|
||||
return decoder.Decode(token, key, verify);
|
||||
}
|
||||
|
||||
private static (IJsonSerializer, IJwtAlgorithm, IBase64UrlEncoder) GetSettings(bool baseSerializer = false)
|
||||
{
|
||||
return (baseSerializer ? (IJsonSerializer)new JsonNetSerializer() : new JwtSerializer(), new HMACSHA256Algorithm(), new JwtBase64UrlEncoder());
|
||||
}
|
||||
return encoder.Encode(payload, key);
|
||||
}
|
||||
|
||||
public class JwtSerializer : IJsonSerializer
|
||||
public static string Decode(string token, string key, bool verify = true, bool baseSerializer = false)
|
||||
{
|
||||
private class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver
|
||||
{
|
||||
protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
|
||||
{
|
||||
var contract = base.CreateDictionaryContract(objectType);
|
||||
var (serializer, algorithm, urlEncoder) = GetSettings(baseSerializer);
|
||||
|
||||
contract.DictionaryKeyResolver = propertyName => propertyName;
|
||||
var provider = new UtcDateTimeProvider();
|
||||
IJwtValidator validator = new JwtValidator(serializer, provider);
|
||||
|
||||
return contract;
|
||||
}
|
||||
}
|
||||
var decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
|
||||
|
||||
public string Serialize(object obj)
|
||||
{
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCaseExceptDictionaryKeysResolver(),
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
};
|
||||
return decoder.Decode(token, key, verify);
|
||||
}
|
||||
|
||||
return JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
|
||||
}
|
||||
|
||||
public T Deserialize<T>(string json)
|
||||
{
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCaseExceptDictionaryKeysResolver(),
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
};
|
||||
|
||||
return JsonConvert.DeserializeObject<T>(json, settings);
|
||||
}
|
||||
private static (IJsonSerializer, IJwtAlgorithm, IBase64UrlEncoder) GetSettings(bool baseSerializer = false)
|
||||
{
|
||||
return (baseSerializer ? (IJsonSerializer)new JsonNetSerializer() : new JwtSerializer(), new HMACSHA256Algorithm(), new JwtBase64UrlEncoder());
|
||||
}
|
||||
}
|
||||
|
||||
public class JwtSerializer : IJsonSerializer
|
||||
{
|
||||
private class CamelCaseExceptDictionaryKeysResolver : CamelCasePropertyNamesContractResolver
|
||||
{
|
||||
protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
|
||||
{
|
||||
var contract = base.CreateDictionaryContract(objectType);
|
||||
|
||||
contract.DictionaryKeyResolver = propertyName => propertyName;
|
||||
|
||||
return contract;
|
||||
}
|
||||
}
|
||||
|
||||
public string Serialize(object obj)
|
||||
{
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCaseExceptDictionaryKeysResolver(),
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
};
|
||||
|
||||
return JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
|
||||
}
|
||||
|
||||
public T Deserialize<T>(string json)
|
||||
{
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCaseExceptDictionaryKeysResolver(),
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
};
|
||||
|
||||
return JsonConvert.DeserializeObject<T>(json, settings);
|
||||
}
|
||||
}
|
||||
|
@ -23,46 +23,48 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Utils
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
public static class MailAddressUtils
|
||||
{
|
||||
public static class MailAddressUtils
|
||||
public static MailAddress Create(string address)
|
||||
{
|
||||
public static MailAddress Create(string address)
|
||||
if (!string.IsNullOrEmpty(address))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(address))
|
||||
var firstPos = address.IndexOf('"');
|
||||
var lastPos = address.LastIndexOf('"');
|
||||
|
||||
if (firstPos != -1 && firstPos < lastPos && address.IndexOf('"', firstPos + 1, lastPos - firstPos - 1) != -1)
|
||||
{
|
||||
var firstPos = address.IndexOf('"');
|
||||
var lastPos = address.LastIndexOf('"');
|
||||
if (firstPos != -1 && firstPos < lastPos && address.IndexOf('"', firstPos + 1, lastPos - firstPos - 1) != -1)
|
||||
{
|
||||
address = new StringBuilder(address).Replace("\"", string.Empty, firstPos + 1, lastPos - firstPos - 1).ToString();
|
||||
}
|
||||
address = new StringBuilder(address).Replace("\"", string.Empty, firstPos + 1, lastPos - firstPos - 1).ToString();
|
||||
}
|
||||
return new MailAddress(address);
|
||||
}
|
||||
|
||||
public static MailAddress Create(string address, string displayName)
|
||||
return new MailAddress(address);
|
||||
}
|
||||
|
||||
public static MailAddress Create(string address, string displayName)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(displayName))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(displayName))
|
||||
displayName = displayName.Replace("\"", string.Empty);
|
||||
|
||||
if (125 < displayName.Length)
|
||||
{
|
||||
displayName = displayName.Replace("\"", string.Empty);
|
||||
if (125 < displayName.Length)
|
||||
{
|
||||
displayName = displayName.Substring(0, 125);
|
||||
}
|
||||
displayName = displayName.Substring(0, 125);
|
||||
}
|
||||
return Create(ToSmtpAddress(address, displayName));
|
||||
}
|
||||
|
||||
public static string ToEncodedString(this MailAddress m)
|
||||
{
|
||||
return ToSmtpAddress(m.Address, MimeHeaderUtils.EncodeMime(m.DisplayName));
|
||||
}
|
||||
return Create(ToSmtpAddress(address, displayName));
|
||||
}
|
||||
|
||||
public static string ToEncodedString(this MailAddress m)
|
||||
{
|
||||
return ToSmtpAddress(m.Address, MimeHeaderUtils.EncodeMime(m.DisplayName));
|
||||
}
|
||||
|
||||
private static string ToSmtpAddress(string address, string displayName)
|
||||
{
|
||||
return string.Format("\"{0}\" <{1}>", displayName, address);
|
||||
}
|
||||
private static string ToSmtpAddress(string address, string displayName)
|
||||
{
|
||||
return $"\"{displayName}\" <{address}>";
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user