Api: CspStartupTask. Reset csp settings on webApi restart

This commit is contained in:
pavelbannov 2023-09-20 18:42:24 +03:00
parent 64185f4bdb
commit 42d30840ec
7 changed files with 126 additions and 11 deletions

View File

@ -292,4 +292,9 @@ class CachedTenantService : ITenantService
_cacheNotifySettings.Publish(new TenantSetting { Key = cacheKey }, CacheNotifyAction.Remove);
}
public IEnumerable<Tenant> GetTenantsWithCsp()
{
return _service.GetTenantsWithCsp();
}
}

View File

@ -30,6 +30,7 @@ namespace ASC.Core;
public interface ITenantService
{
byte[] GetTenantSettings(int tenant, string key);
IEnumerable<Tenant> GetTenantsWithCsp();
IEnumerable<Tenant> GetTenants(DateTime from, bool active = true);
IEnumerable<Tenant> GetTenants(List<int> ids);
IEnumerable<Tenant> GetTenants(string login, string passwordHash);

View File

@ -32,6 +32,7 @@ public class DbTenantService : ITenantService
private readonly TenantDomainValidator _tenantDomainValidator;
private readonly IDbContextFactory<TenantDbContext> _dbContextFactory;
private readonly IDbContextFactory<UserDbContext> _userDbContextFactory;
private readonly IDbContextFactory<WebstudioDbContext> _webstudioDbContext;
private readonly IMapper _mapper;
private readonly MachinePseudoKeys _machinePseudoKeys;
private List<string> _forbiddenDomains;
@ -41,13 +42,15 @@ public class DbTenantService : ITenantService
IDbContextFactory<UserDbContext> userDbContextFactory,
TenantDomainValidator tenantDomainValidator,
MachinePseudoKeys machinePseudoKeys,
IMapper mapper)
IMapper mapper,
IDbContextFactory<WebstudioDbContext> webstudioDbContext)
{
_dbContextFactory = dbContextFactory;
_userDbContextFactory = userDbContextFactory;
_tenantDomainValidator = tenantDomainValidator;
_machinePseudoKeys = machinePseudoKeys;
_mapper = mapper;
_webstudioDbContext = webstudioDbContext;
}
public void ValidateDomain(string domain)
@ -75,6 +78,18 @@ public class DbTenantService : ITenantService
return q.ProjectTo<Tenant>(_mapper.ConfigurationProvider).ToList();
}
public IEnumerable<Tenant> GetTenantsWithCsp()
{
var cspSettingsId = new CspSettings().ID;
using var webstudioDbContext = _webstudioDbContext.CreateDbContext();
var q = webstudioDbContext.Tenants
.Join(webstudioDbContext.WebstudioSettings.DefaultIfEmpty(), r => r.Id, r => r.TenantId, (tenant, settings) => new { settings, tenant })
.Where(r => r.settings.Id == cspSettingsId)
.Select(r => r.tenant);
return q.ProjectTo<Tenant>(_mapper.ConfigurationProvider).ToList();
}
public IEnumerable<Tenant> GetTenants(List<int> ids)
{
using var tenantDbContext = _dbContextFactory.CreateDbContext();

View File

@ -35,6 +35,6 @@ public class CspSettings : ISettings<CspSettings>
public CspSettings GetDefault()
{
return new CspSettings();
return new CspSettings() { Domains = new List<string>() };
}
}

View File

@ -75,15 +75,34 @@ public class CspSettingsHelper
if (domain == Tenant.LocalHost && tenant.Alias == Tenant.LocalHost)
{
headerKeys.Add(GetKey(Tenant.HostName));
var ips = Dns.GetHostAddresses(Dns.GetHostName(), AddressFamily.InterNetwork);
foreach (var ip in ips)
var domainsKey = $"{GetKey(domain)}:keys";
if (_httpContextAccessor.HttpContext != null)
{
headerKeys.Add(GetKey(ip.ToString()));
}
var keys = new List<string>()
{
GetKey(Tenant.HostName)
};
headerKeys.Add(GetKey(_httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString()));
var ips = Dns.GetHostAddresses(Dns.GetHostName(), AddressFamily.InterNetwork);
foreach (var ip in ips)
{
keys.Add(GetKey(ip.ToString()));
}
keys.Add(GetKey(_httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString()));
await _distributedCache.SetStringAsync(domainsKey, string.Join(';', keys));
headerKeys.AddRange(keys);
}
else
{
var domainsValue = await _distributedCache.GetStringAsync(domainsKey);
if (!string.IsNullOrEmpty(domainsValue))
{
headerKeys.AddRange(domainsValue.Split(';'));
}
}
}
var headerValue = CreateHeader(domains, setDefaultIfEmpty);
@ -121,7 +140,7 @@ public class CspSettingsHelper
}
}
public string CreateHeader(IEnumerable<string> domains, bool setDefaultIfEmpty = false)
public string CreateHeader(IEnumerable<string> domains, bool setDefaultIfEmpty = false, bool currentTenant = true)
{
if (domains == null || !domains.Any())
{
@ -143,7 +162,7 @@ public class CspSettingsHelper
defaultOptions.Def.Add($"*.{_coreBaseSettings.Basedomain}");
}
if (_globalStore.GetStore() is S3Storage s3Storage && !string.IsNullOrEmpty(s3Storage.CdnDistributionDomain))
if (_globalStore.GetStore(currentTenant) is S3Storage s3Storage && !string.IsNullOrEmpty(s3Storage.CdnDistributionDomain))
{
defaultOptions.Img.Add(s3Storage.CdnDistributionDomain);
}

View File

@ -0,0 +1,68 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL 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 details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Api.Core.Core;
public class CspStartupTask : IStartupTask
{
private readonly IServiceProvider _provider;
private readonly IDistributedCache _distributedCache;
private const string HeaderKey = $"csp";
public CspStartupTask(IServiceProvider provider, IDistributedCache distributedCache)
{
_provider = provider;
_distributedCache = distributedCache;
}
public async Task ExecuteAsync(CancellationToken cancellationToken)
{
await using var scope = _provider.CreateAsyncScope();
var serviceProvider = scope.ServiceProvider;
var helper = serviceProvider.GetService<CspSettingsHelper>();
var tenantManager = serviceProvider.GetService<TenantManager>();
var settingsManager = serviceProvider.GetService<SettingsManager>();
var oldHeaderValue = await _distributedCache.GetStringAsync(HeaderKey);
var currentHeaderValue = helper.CreateHeader(null, true, false);
if (oldHeaderValue != currentHeaderValue)
{
var tenantService = serviceProvider.GetService<ITenantService>();
foreach (var t in tenantService.GetTenantsWithCsp())
{
tenantManager.SetCurrentTenant(t);
var current = settingsManager.Load<CspSettings>();
await helper.Save(current.Domains, current.SetDefaultIfEmpty);
}
await _distributedCache.SetStringAsync(HeaderKey, currentHeaderValue);
}
}
}

View File

@ -24,6 +24,10 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using ASC.Api.Core.Core;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace ASC.Web.Api;
public class Startup : BaseStartup
@ -54,6 +58,9 @@ public class Startup : BaseStartup
services.AddScoped<CountRoomCheckerStatistic>();
DIHelper.TryAdd<AdditionalWhiteLabelSettingsConverter>();
services.AddStartupTask<CspStartupTask>()
.TryAddSingleton(services);
}
public override void Configure(IApplicationBuilder app, IWebHostEnvironment env)