From 1a86b80c61d0555eabe7a06c5db7af8d76f7c8ad Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Sat, 19 Mar 2022 13:42:19 -0400 Subject: [PATCH] More improvements to OIDC support --- Oqtane.Client/Modules/Admin/Login/Index.razor | 27 ++++-- Oqtane.Client/Modules/Admin/Users/Index.razor | 48 +++++++++-- .../Services/Interfaces/ISettingService.cs | 8 +- Oqtane.Client/Services/SettingService.cs | 5 ++ .../Controllers/SettingController.cs | 16 +++- .../Extensions/DictionaryExtensions.cs | 19 ++++ ...taneSiteAuthenticationBuilderExtensions.cs | 86 ++++++++++++++----- .../OqtaneSiteIdentityBuilderExtensions.cs | 40 ++------- .../Middleware/TenantMiddleware.cs | 16 ++-- Oqtane.Server/Pages/Logout.cshtml.cs | 33 ++++--- Oqtane.Server/Pages/OIDC.cshtml.cs | 6 +- Oqtane.Server/Repository/SettingRepository.cs | 19 +++- 12 files changed, 230 insertions(+), 93 deletions(-) create mode 100644 Oqtane.Server/Extensions/DictionaryExtensions.cs diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index f5b1cf46..234ebb0c 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -19,11 +19,13 @@ {
-
+
-
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+

@@ -180,6 +201,9 @@ else private string _authority; private string _clientid; private string _clientsecret; + private string _metadata; + private string _logouturl; + private string _allowsitelogin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; @@ -203,6 +227,9 @@ else _authority = SettingService.GetSetting(settings, "OpenIdConnectOptions:Authority", ""); _clientid = SettingService.GetSetting(settings, "OpenIdConnectOptions:ClientId", ""); _clientsecret = SettingService.GetSetting(settings, "OpenIdConnectOptions:ClientSecret", ""); + _metadata = SettingService.GetSetting(settings, "OpenIdConnectOptions:MetadataAddress", ""); + _logouturl = SettingService.GetSetting(settings, "OpenIdConnectOptions:LogoutUrl", ""); + _allowsitelogin = SettingService.GetSetting(settings, "AllowSiteLogin", "True"); } private List Search(string search) @@ -285,7 +312,11 @@ else settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:Authority", _authority, true); settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:ClientId", _clientid, true); settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:ClientSecret", _clientsecret, true); + settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:MetadataAddress", _metadata, true); + settings = SettingService.SetSetting(settings, "OpenIdConnectOptions:LogoutUrl", _logouturl, true); + settings = SettingService.SetSetting(settings, "AllowSiteLogin", _allowsitelogin, false); await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId); + await SettingService.ClearSiteSettingsCacheAsync(site.SiteId); AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success); } @@ -295,5 +326,4 @@ else AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error); } } - } diff --git a/Oqtane.Client/Services/Interfaces/ISettingService.cs b/Oqtane.Client/Services/Interfaces/ISettingService.cs index 60099fd5..98f733d1 100644 --- a/Oqtane.Client/Services/Interfaces/ISettingService.cs +++ b/Oqtane.Client/Services/Interfaces/ISettingService.cs @@ -38,6 +38,12 @@ namespace Oqtane.Services /// Task UpdateSiteSettingsAsync(Dictionary siteSettings, int siteId); + /// + /// Clears site option cache + /// + /// + Task ClearSiteSettingsCacheAsync(int siteId); + /// /// Returns a key-value dictionary of all page settings for the given page /// @@ -149,7 +155,6 @@ namespace Oqtane.Services /// Task> GetSettingsAsync(string entityName, int entityId); - /// /// Updates settings for a given entityName and Id /// @@ -166,7 +171,6 @@ namespace Oqtane.Services /// Task GetSettingAsync(string entityName, int settingId); - /// /// Creates a new setting /// diff --git a/Oqtane.Client/Services/SettingService.cs b/Oqtane.Client/Services/SettingService.cs index 1c62a41c..343920d2 100644 --- a/Oqtane.Client/Services/SettingService.cs +++ b/Oqtane.Client/Services/SettingService.cs @@ -42,6 +42,11 @@ namespace Oqtane.Services await UpdateSettingsAsync(siteSettings, EntityNames.Site, siteId); } + public async Task ClearSiteSettingsCacheAsync(int siteId) + { + await DeleteAsync($"{Apiurl}/clear/{siteId}"); + } + public async Task> GetPageSettingsAsync(int pageId) { return await GetSettingsAsync(EntityNames.Page, pageId); diff --git a/Oqtane.Server/Controllers/SettingController.cs b/Oqtane.Server/Controllers/SettingController.cs index 6165a07e..d968e2e9 100644 --- a/Oqtane.Server/Controllers/SettingController.cs +++ b/Oqtane.Server/Controllers/SettingController.cs @@ -8,6 +8,9 @@ using Oqtane.Enums; using Oqtane.Infrastructure; using Oqtane.Repository; using System.Net; +using Microsoft.Extensions.Options; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.AspNetCore.Authorization; namespace Oqtane.Controllers { @@ -20,14 +23,16 @@ namespace Oqtane.Controllers private readonly ISyncManager _syncManager; private readonly ILogManager _logger; private readonly Alias _alias; + private readonly IOptionsMonitorCache _optionsMonitorCache; private readonly string _visitorCookie; - public SettingController(ISettingRepository settings, IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger) + public SettingController(ISettingRepository settings, IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, IOptionsMonitorCache optionsMonitorCache, ILogManager logger) { _settings = settings; _pageModules = pageModules; _userPermissions = userPermissions; _syncManager = syncManager; + _optionsMonitorCache = optionsMonitorCache; _logger = logger; _alias = tenantManager.GetAlias(); _visitorCookie = "APP_VISITOR_" + _alias.SiteId.ToString(); @@ -131,6 +136,15 @@ namespace Oqtane.Controllers } } + // DELETE api//clear + [HttpDelete("clear/{id}")] + [Authorize(Roles = RoleNames.Admin)] + public void Clear(int id) + { + _optionsMonitorCache.Clear(); + _logger.Log(LogLevel.Information, this, LogFunction.Other, "Site Options Cache Cleared"); + } + private bool IsAuthorized(string entityName, int entityId, string permissionName) { bool authorized = false; diff --git a/Oqtane.Server/Extensions/DictionaryExtensions.cs b/Oqtane.Server/Extensions/DictionaryExtensions.cs new file mode 100644 index 00000000..cdb86173 --- /dev/null +++ b/Oqtane.Server/Extensions/DictionaryExtensions.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace Oqtane.Extensions +{ + public static class DictionaryExtensions + { + public static TValue GetValue(this Dictionary dictionary, TKey key, TValue defaultValue, bool nullOrEmptyValueIsValid = false) + { + if (dictionary != null && key != null && dictionary.ContainsKey(key)) + { + if (nullOrEmptyValueIsValid || (dictionary[key] != null && !string.IsNullOrEmpty(dictionary[key].ToString()))) + { + return dictionary[key]; + } + } + return defaultValue; + } + } +} diff --git a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs index e71a7142..5e9f7d05 100644 --- a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs @@ -15,6 +15,8 @@ using Oqtane.Repository; using System.IO; using System.Collections.Generic; using Oqtane.Security; +using System.Net; +using Microsoft.AspNetCore.Http; namespace Oqtane.Extensions { @@ -47,38 +49,41 @@ namespace Oqtane.Extensions // site OpenIdConnect options builder.AddSiteOptions((options, alias) => { - if (alias.SiteSettings.ContainsKey("OpenIdConnectOptions:Authority")) - { - options.Authority = alias.SiteSettings["OpenIdConnectOptions:Authority"]; - } - if (alias.SiteSettings.ContainsKey("OpenIdConnectOptions:ClientId")) - { - options.ClientId = alias.SiteSettings["OpenIdConnectOptions:ClientId"]; - } - if (alias.SiteSettings.ContainsKey("OpenIdConnectOptions:ClientSecret")) - { - options.ClientSecret = alias.SiteSettings["OpenIdConnectOptions:ClientSecret"]; - } - // default options options.SignInScheme = Constants.AuthenticationScheme; // identity cookie options.RequireHttpsMetadata = true; + options.SaveTokens = true; + options.GetClaimsFromUserInfoEndpoint = true; + options.CallbackPath = string.IsNullOrEmpty(alias.Path) ? "/signin-oidc" : "/" + alias.Path + "/signin-oidc"; + options.ResponseType = OpenIdConnectResponseType.Code; // authorization code flow + options.ResponseMode = OpenIdConnectResponseMode.FormPost; // recommended as most secure options.UsePkce = true; options.Scope.Add("openid"); // core claims options.Scope.Add("profile"); // name claims options.Scope.Add("email"); // email claim - //options.Scope.Add("offline_access"); // get refresh token - options.SaveTokens = true; - options.GetClaimsFromUserInfoEndpoint = true; - options.CallbackPath = string.IsNullOrEmpty(alias.Path) ? "/signin-oidc" : "/" + alias.Path + "/signin-oidc"; - options.ResponseType = OpenIdConnectResponseType.Code; + //options.Scope.Add("offline_access"); // refresh token + + // cookie config is required to avoid Correlation Failed errors + options.NonceCookie.SameSite = SameSiteMode.Unspecified; + options.CorrelationCookie.SameSite = SameSiteMode.Unspecified; + + // site options + options.Authority = alias.SiteSettings.GetValue("OpenIdConnectOptions:Authority", options.Authority); + options.ClientId = alias.SiteSettings.GetValue("OpenIdConnectOptions:ClientId", options.ClientId); + options.ClientSecret = alias.SiteSettings.GetValue("OpenIdConnectOptions:ClientSecret", options.ClientSecret); + options.MetadataAddress = alias.SiteSettings.GetValue("OpenIdConnectOptions:MetadataAddress", options.MetadataAddress); + + // openid connect events options.Events.OnTokenValidated = OnTokenValidated; + options.Events.OnRedirectToIdentityProvider = OnRedirectToIdentityProvider; + options.Events.OnRedirectToIdentityProviderForSignOut = OnRedirectToIdentityProviderForSignOut; + options.Events.OnRemoteFailure = OnRemoteFailure; }); // site ChallengeScheme options builder.AddSiteOptions((options, alias) => { - if (alias.SiteSettings.ContainsKey("OpenIdConnectOptions:Authority") && !string.IsNullOrEmpty(alias.SiteSettings["OpenIdConnectOptions:Authority"])) + if (alias.SiteSettings.GetValue("OpenIdConnectOptions:Authority", "") != "") { options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; } @@ -175,7 +180,7 @@ namespace Oqtane.Extensions else { // provider keys do not match - _logger.Log(LogLevel.Error, nameof(OqtaneSiteAuthenticationBuilderExtensions), Enums.LogFunction.Security, "OpenId Connect Server Provider Key Does Not Match For User {Email}", email); + _logger.Log(LogLevel.Error, nameof(OqtaneSiteAuthenticationBuilderExtensions), Enums.LogFunction.Security, "OpenId Connect Provider Key Does Not Match For User {Email}", email); } } else @@ -208,14 +213,53 @@ namespace Oqtane.Extensions List userroles = _userRoles.GetUserRoles(user.UserId, context.HttpContext.GetAlias().SiteId).ToList(); var identity = UserSecurity.CreateClaimsIdentity(context.HttpContext.GetAlias(), user, userroles); principal.AddClaims(identity.Claims); + + // add provider + principal.AddClaim(new Claim("Provider", context.HttpContext.GetAlias().SiteSettings["OpenIdConnectOptions:Authority"])); } } else { - _logger.Log(LogLevel.Information, nameof(OqtaneSiteAuthenticationBuilderExtensions), Enums.LogFunction.Security, "OpenId Connect Server Did Not Return An Email Claim"); + _logger.Log(LogLevel.Information, nameof(OqtaneSiteAuthenticationBuilderExtensions), Enums.LogFunction.Security, "OpenId Connect Provider Did Not Return An Email Claim"); } } + private static Task OnRedirectToIdentityProvider(RedirectContext context) + { + //context.ProtocolMessage.SetParameter("key", "value"); + return Task.CompletedTask; + } + + private static Task OnRedirectToIdentityProviderForSignOut(RedirectContext context) + { + var logoutUrl = context.HttpContext.GetAlias().SiteSettings.GetValue("OpenIdConnectOptions:LogoutUrl", ""); + if (logoutUrl != "") + { + var postLogoutUri = context.Properties.RedirectUri; + if (!string.IsNullOrEmpty(postLogoutUri)) + { + if (postLogoutUri.StartsWith("/")) + { + var request = context.Request; + postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri; + } + logoutUrl += $"&returnTo={Uri.EscapeDataString(postLogoutUri)}"; + } + context.Response.Redirect(logoutUrl); + context.HandleResponse(); + } + return Task.CompletedTask; + } + + private static Task OnRemoteFailure(RemoteFailureContext context) + { + var _logger = context.HttpContext.RequestServices.GetRequiredService(); + _logger.Log(LogLevel.Error, nameof(OqtaneSiteAuthenticationBuilderExtensions), Enums.LogFunction.Security, "OpenId Connect Remote Failure {Error}", context.Failure.Message); + context.Response.Redirect(context.Properties.RedirectUri); + context.HandleResponse(); + return Task.CompletedTask; + } + public static bool DecorateService(this IServiceCollection services, params object[] parameters) { var existingService = services.SingleOrDefault(s => s.ServiceType == typeof(TService)); diff --git a/Oqtane.Server/Extensions/OqtaneSiteIdentityBuilderExtensions.cs b/Oqtane.Server/Extensions/OqtaneSiteIdentityBuilderExtensions.cs index 14a766e3..8b9ec5d6 100644 --- a/Oqtane.Server/Extensions/OqtaneSiteIdentityBuilderExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneSiteIdentityBuilderExtensions.cs @@ -15,40 +15,16 @@ namespace Oqtane.Extensions builder.AddSiteOptions((options, alias) => { // password options - if (alias.SiteSettings.ContainsKey("IdentityOptions:Password:RequiredLength")) - { - options.Password.RequiredLength = int.Parse(alias.SiteSettings["IdentityOptions:Password:RequiredLength"]); - } - if (alias.SiteSettings.ContainsKey("IdentityOptions:Password:RequiredUniqueChars")) - { - options.Password.RequiredUniqueChars = int.Parse(alias.SiteSettings["IdentityOptions:Password:RequiredUniqueChars"]); - } - if (alias.SiteSettings.ContainsKey("IdentityOptions:Password:RequireDigit")) - { - options.Password.RequireDigit = bool.Parse(alias.SiteSettings["IdentityOptions:Password:RequireDigit"]); - } - if (alias.SiteSettings.ContainsKey("IdentityOptions:Password:RequireUppercase")) - { - options.Password.RequireUppercase = bool.Parse(alias.SiteSettings["IdentityOptions:Password:RequireUppercase"]); - } - if (alias.SiteSettings.ContainsKey("IdentityOptions:Password:RequireLowercase")) - { - options.Password.RequireLowercase = bool.Parse(alias.SiteSettings["IdentityOptions:Password:RequireLowercase"]); - } - if (alias.SiteSettings.ContainsKey("IdentityOptions:Password:RequireNonAlphanumeric")) - { - options.Password.RequireNonAlphanumeric = bool.Parse(alias.SiteSettings["IdentityOptions:Password:RequireNonAlphanumeric"]); - } + options.Password.RequiredLength = int.Parse(alias.SiteSettings.GetValue("IdentityOptions:Password:RequiredLength", options.Password.RequiredLength.ToString())); + options.Password.RequiredUniqueChars = int.Parse(alias.SiteSettings.GetValue("IdentityOptions:Password:RequiredUniqueChars", options.Password.RequiredUniqueChars.ToString())); + options.Password.RequireDigit = bool.Parse(alias.SiteSettings.GetValue("IdentityOptions:Password:RequireDigit", options.Password.RequireDigit.ToString())); + options.Password.RequireUppercase = bool.Parse(alias.SiteSettings.GetValue("IdentityOptions:Password:RequireUppercase", options.Password.RequireUppercase.ToString())); + options.Password.RequireLowercase = bool.Parse(alias.SiteSettings.GetValue("IdentityOptions:Password:RequireLowercase", options.Password.RequireLowercase.ToString())); + options.Password.RequireNonAlphanumeric = bool.Parse(alias.SiteSettings.GetValue("IdentityOptions:Password:RequireNonAlphanumeric", options.Password.RequireNonAlphanumeric.ToString())); // lockout options - if (alias.SiteSettings.ContainsKey("IdentityOptions:Password:MaxFailedAccessAttempts")) - { - options.Lockout.MaxFailedAccessAttempts = int.Parse(alias.SiteSettings["IdentityOptions:Password:MaxFailedAccessAttempts"]); - } - if (alias.SiteSettings.ContainsKey("IdentityOptions:Password:DefaultLockoutTimeSpan")) - { - options.Lockout.DefaultLockoutTimeSpan = TimeSpan.Parse(alias.SiteSettings["IdentityOptions:Password:DefaultLockoutTimeSpan"]); - } + options.Lockout.MaxFailedAccessAttempts = int.Parse(alias.SiteSettings.GetValue("IdentityOptions:Password:MaxFailedAccessAttempts", options.Lockout.MaxFailedAccessAttempts.ToString())); + options.Lockout.DefaultLockoutTimeSpan = TimeSpan.Parse(alias.SiteSettings.GetValue("IdentityOptions:Password:DefaultLockoutTimeSpan", options.Lockout.DefaultLockoutTimeSpan.ToString())); }); return builder; diff --git a/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs b/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs index e4238a62..ff16773e 100644 --- a/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs +++ b/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Caching.Memory; using Oqtane.Repository; using Oqtane.Shared; @@ -27,10 +28,15 @@ namespace Oqtane.Infrastructure if (alias != null) { - // get site settings and store alias in HttpContext - var settingRepository = context.RequestServices.GetService(typeof(ISettingRepository)) as ISettingRepository; - alias.SiteSettings = settingRepository.GetSettings(EntityNames.Site) - .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); + // get site settings + var cache = context.RequestServices.GetService(typeof(IMemoryCache)) as IMemoryCache; + alias.SiteSettings = cache.GetOrCreate("sitesettings:" + alias.SiteKey, entry => + { + var settingRepository = context.RequestServices.GetService(typeof(ISettingRepository)) as ISettingRepository; + return settingRepository.GetSettings(EntityNames.Site) + .ToDictionary(setting => setting.SettingName, setting => setting.SettingValue); + }); + // save alias in HttpContext context.Items.Add(Constants.HttpContextAliasKey, alias); // rewrite path by removing alias path prefix from api and pages requests (for consistent routing) @@ -42,9 +48,7 @@ namespace Oqtane.Infrastructure context.Request.Path = path.Replace("/" + alias.Path, ""); } } - } - } // continue processing diff --git a/Oqtane.Server/Pages/Logout.cshtml.cs b/Oqtane.Server/Pages/Logout.cshtml.cs index eef4dfb7..ab9ed7ad 100644 --- a/Oqtane.Server/Pages/Logout.cshtml.cs +++ b/Oqtane.Server/Pages/Logout.cshtml.cs @@ -1,32 +1,37 @@ +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; +using Oqtane.Extensions; using Oqtane.Shared; namespace Oqtane.Pages { - [AllowAnonymous] + [Authorize] public class LogoutModel : PageModel { public async Task OnGetAsync(string returnurl) { - if (HttpContext.User.Identity.IsAuthenticated) - { - await HttpContext.SignOutAsync(Constants.AuthenticationScheme); - } + await HttpContext.SignOutAsync(Constants.AuthenticationScheme); - if (returnurl == null) - { - returnurl = ""; - } - if (!returnurl.StartsWith("/")) - { - returnurl = "/" + returnurl; - } + returnurl = (returnurl == null) ? "/" : returnurl; + returnurl = (!returnurl.StartsWith("/")) ? "/" + returnurl : returnurl; - return LocalRedirect(Url.Content("~" + returnurl)); + var provider = HttpContext.User.Claims.FirstOrDefault(item => item.Type == "Provider"); + var authority = HttpContext.GetAlias().SiteSettings.GetValue("OpenIdConnectOptions:Authority", ""); + var logoutUrl = HttpContext.GetAlias().SiteSettings.GetValue("OpenIdConnectOptions:LogoutUrl", ""); + if (provider != null && provider.Value == authority && logoutUrl != "") + { + return new SignOutResult(OpenIdConnectDefaults.AuthenticationScheme, + new AuthenticationProperties { RedirectUri = returnurl }); + } + else + { + return LocalRedirect(Url.Content("~" + returnurl)); + } } } } diff --git a/Oqtane.Server/Pages/OIDC.cshtml.cs b/Oqtane.Server/Pages/OIDC.cshtml.cs index 433d1bea..d70f3931 100644 --- a/Oqtane.Server/Pages/OIDC.cshtml.cs +++ b/Oqtane.Server/Pages/OIDC.cshtml.cs @@ -9,7 +9,11 @@ namespace Oqtane.Pages { public IActionResult OnGetAsync(string returnurl) { - return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = !string.IsNullOrEmpty(returnurl) ? returnurl : "/" }); + returnurl = (returnurl == null) ? "/" : returnurl; + returnurl = (!returnurl.StartsWith("/")) ? "/" + returnurl : returnurl; + + return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme, + new AuthenticationProperties { RedirectUri = returnurl }); } } } diff --git a/Oqtane.Server/Repository/SettingRepository.cs b/Oqtane.Server/Repository/SettingRepository.cs index 95b55de6..031acf4d 100644 --- a/Oqtane.Server/Repository/SettingRepository.cs +++ b/Oqtane.Server/Repository/SettingRepository.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using Oqtane.Models; using Oqtane.Shared; @@ -10,11 +11,15 @@ namespace Oqtane.Repository { private TenantDBContext _tenant; private MasterDBContext _master; + private readonly SiteState _siteState; + private readonly IMemoryCache _cache; - public SettingRepository(TenantDBContext tenant, MasterDBContext master) + public SettingRepository(TenantDBContext tenant, MasterDBContext master, SiteState siteState, IMemoryCache cache) { _tenant = tenant; _master = master; + _siteState = siteState; + _cache = cache; } public IEnumerable GetSettings(string entityName) @@ -47,6 +52,7 @@ namespace Oqtane.Repository _tenant.Setting.Add(setting); _tenant.SaveChanges(); } + ManageCache(setting.EntityName); return setting; } @@ -62,6 +68,7 @@ namespace Oqtane.Repository _tenant.Entry(setting).State = EntityState.Modified; _tenant.SaveChanges(); } + ManageCache(setting.EntityName); return setting; } @@ -103,6 +110,7 @@ namespace Oqtane.Repository _tenant.Setting.Remove(setting); _tenant.SaveChanges(); } + ManageCache(entityName); } public void DeleteSettings(string entityName, int entityId) @@ -129,11 +137,20 @@ namespace Oqtane.Repository } _tenant.SaveChanges(); } + ManageCache(entityName); } private bool IsMaster(string EntityName) { return (EntityName == EntityNames.ModuleDefinition || EntityName == EntityNames.Host); } + + private void ManageCache(string EntityName) + { + if (EntityName == EntityNames.Site) + { + _cache.Remove("sitesettings:" + _siteState.Alias.SiteKey); + } + } } }