public override async Task <IDisplayResult> UpdateAsync(ReCaptchaSettings section, BuildEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageReCaptchaSettings)) { return(null); } if (context.GroupId == GroupId) { var model = new ReCaptchaSettingsViewModel(); if (await context.Updater.TryUpdateModelAsync(model, Prefix)) { section.SiteKey = model.SiteKey?.Trim(); section.SecretKey = model.SecretKey?.Trim(); // Release the tenant to apply settings. await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(await EditAsync(section, context)); }
public override async Task <IDisplayResult> UpdateAsync(SmtpSettings section, BuildEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageEmailSettings)) { return(null); } if (context.GroupId == GroupId) { var previousPassword = section.Password; await context.Updater.TryUpdateModelAsync(section, Prefix); // Restore password if the input is empty, meaning that it has not been reset. if (string.IsNullOrWhiteSpace(section.Password)) { section.Password = previousPassword; } else { // encrypt the password var protector = _dataProtectionProvider.CreateProtector(nameof(SmtpSettingsConfiguration)); section.Password = protector.Protect(section.Password); } // Release the tenant to apply the settings await _shellHost.ReleaseShellContextAsync(_shellSettings); } return(await EditAsync(section, context)); }
public override async Task <IDisplayResult> UpdateAsync(ReverseProxySettings section, BuildEditorContext context) { if (context.GroupId == SettingsGroupId) { var model = new ReverseProxySettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); section.ForwardedHeaders = ForwardedHeaders.None; if (model.EnableXForwardedFor) { section.ForwardedHeaders |= ForwardedHeaders.XForwardedFor; } if (model.EnableXForwardedHost) { section.ForwardedHeaders |= ForwardedHeaders.XForwardedHost; } if (model.EnableXForwardedProto) { section.ForwardedHeaders |= ForwardedHeaders.XForwardedProto; } // If the settings are valid, release the current tenant. if (context.Updater.ModelState.IsValid) { await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(await EditAsync(section, context)); }
public override async Task <IDisplayResult> UpdateAsync(SecuritySettings section, BuildEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; if (!await _authorizationService.AuthorizeAsync(user, SecurityPermissions.ManageSecurityHeadersSettings)) { return(null); } if (context.GroupId == SettingsGroupId) { var model = new SecuritySettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); PrepareContentSecurityPolicyValues(model); section.ContentTypeOptions = SecurityHeaderDefaults.ContentTypeOptions; section.ContentSecurityPolicy = model.ContentSecurityPolicy; section.PermissionsPolicy = model.PermissionsPolicy; section.ReferrerPolicy = model.ReferrerPolicy; if (context.Updater.ModelState.IsValid) { await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(await EditAsync(section, context)); }
/// <inheritdocs /> public override async Task <IDisplayResult> UpdateAsync(LocalizationSettings section, BuildEditorContext context) { if (context.GroupId == GroupId) { var model = new LocalizationSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); var supportedCulture = JsonConvert.DeserializeObject <string[]>(model.SupportedCultures); if (!supportedCulture.Any()) { context.Updater.ModelState.AddModelError("SupportedCultures", S["A culture is required"]); } if (context.Updater.ModelState.IsValid) { // Invariant culture name is empty so a null value is bound. section.DefaultCulture = model.DefaultCulture ?? ""; section.SupportedCultures = supportedCulture; if (!section.SupportedCultures.Contains(section.DefaultCulture)) { section.DefaultCulture = section.SupportedCultures[0]; } // We always release the tenant for the default culture and also supported cultures to take effect await _shellHost.ReleaseShellContextAsync(_shellSettings); _notifier.Warning(H["The site has been restarted for the settings to take effect"]); } } return(await EditAsync(section, context)); }
public override async Task <IDisplayResult> UpdateAsync(FacebookSettings settings, BuildEditorContext context) { if (context.GroupId == FacebookConstants.Features.Core) { var user = _httpContextAccessor.HttpContext?.User; if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageFacebookApp)) { return(null); } var model = new FacebookSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); if (context.Updater.ModelState.IsValid) { var protector = _dataProtectionProvider.CreateProtector(FacebookConstants.Features.Core); settings.AppId = model.AppId; settings.AppSecret = protector.Protect(model.AppSecret); settings.FBInit = model.FBInit; settings.SdkJs = model.SdkJs; if (!string.IsNullOrWhiteSpace(model.FBInitParams)) { settings.FBInitParams = model.FBInitParams; } settings.Version = model.Version; await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(await EditAsync(settings, context)); }
/// <inheritdoc/> public override async Task <IDisplayResult> UpdateAsync(GmailSettings section, BuildEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGmailSettings)) { return(null); } if (context.GroupId == GroupId) { var previousPassword = section.Password; await context.Updater.TryUpdateModelAsync(section, Prefix); if (string.IsNullOrWhiteSpace(section.Password)) { section.Password = previousPassword; } else { var protector = _dataProtectionProvider.CreateProtector(nameof(GmailSettingsConfiguration)); section.Password = protector.Protect(section.Password); } await _shellHost.ReleaseShellContextAsync(_shellSettings); } return(await EditAsync(section, context)); }
/// <summary> /// Releases all shells so that new ones will be built for subsequent requests. /// Note: Can be used to free up resources after a given period of inactivity. /// </summary> public async static Task ReleaseAllShellContextsAsync(this IShellHost shellHost) { foreach (var shell in shellHost.ListShellContexts()) { await shellHost.ReleaseShellContextAsync(shell.Settings); } }
/// <summary> /// Releases all shells so that new ones will be built for subsequent requests. /// Note: Can be used to free up resources after a given period of inactivity. /// </summary> public async static Task ReleaseAllShellContextsAsync(this IShellHost shellHost) { foreach (var settings in shellHost.GetAllSettings()) { await shellHost.ReleaseShellContextAsync(settings); } }
public override async Task <IDisplayResult> UpdateAsync(GitHubAuthenticationSettings settings, BuildEditorContext context) { if (context.GroupId == GitHubConstants.Features.GitHubAuthentication) { var user = _httpContextAccessor.HttpContext?.User; if (user == null || !await _authorizationService.AuthorizeAsync(user, Permissions.ManageGitHubAuthentication)) { return(null); } var model = new GitHubAuthenticationSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); if (context.Updater.ModelState.IsValid) { var protector = _dataProtectionProvider.CreateProtector(GitHubConstants.Features.GitHubAuthentication); settings.ClientID = model.ClientID; settings.ClientSecret = protector.Protect(model.ClientSecret); settings.CallbackPath = model.CallbackUrl; await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(await EditAsync(settings, context)); }
public override async Task <IDisplayResult> UpdateAsync(MicrosoftAccountSettings settings, BuildEditorContext context) { if (context.GroupId == MicrosoftAuthenticationConstants.Features.MicrosoftAccount) { var user = _httpContextAccessor.HttpContext?.User; if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageMicrosoftAuthentication)) { return(null); } var model = new MicrosoftAccountSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); if (context.Updater.ModelState.IsValid) { var protector = _dataProtectionProvider.CreateProtector(MicrosoftAuthenticationConstants.Features.MicrosoftAccount); settings.AppId = model.AppId; settings.AppSecret = protector.Protect(model.AppSecret); settings.CallbackPath = model.CallbackPath; settings.SaveTokens = model.SaveTokens; await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(await EditAsync(settings, context)); }
public override async Task <IDisplayResult> UpdateAsync(HttpsSettings settings, BuildEditorContext context) { if (context.GroupId == SettingsGroupId) { var user = _httpContextAccessor.HttpContext?.User; if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageHttps)) { return(null); } var model = new HttpsSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); settings.EnableStrictTransportSecurity = model.EnableStrictTransportSecurity; settings.RequireHttps = model.RequireHttps; settings.RequireHttpsPermanent = model.RequireHttpsPermanent; settings.SslPort = model.SslPort; // If the settings are valid, release the current tenant. if (context.Updater.ModelState.IsValid) { await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(await EditAsync(settings, context)); }
public override async Task <IDisplayResult> UpdateAsync(ISite site, UpdateEditorContext context) { if (context.GroupId == GroupId) { var model = new SiteSettingsViewModel(); if (await context.Updater.TryUpdateModelAsync(model, Prefix)) { site.SiteName = model.SiteName; site.PageTitleFormat = model.PageTitleFormat; site.BaseUrl = model.BaseUrl; site.TimeZoneId = model.TimeZone; site.PageSize = model.PageSize; site.UseCdn = model.UseCdn; site.CdnBaseUrl = model.CdnBaseUrl; site.ResourceDebugMode = model.ResourceDebugMode; site.AppendVersion = model.AppendVersion; } if (!String.IsNullOrEmpty(site.BaseUrl) && !Uri.TryCreate(site.BaseUrl, UriKind.Absolute, out var baseUrl)) { context.Updater.ModelState.AddModelError(Prefix, nameof(site.BaseUrl), S["The Base url must be a fully qualified URL."]); } if (context.Updater.ModelState.IsValid) { await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(Edit(site)); }
public override async Task <IDisplayResult> UpdateAsync(TwitterSettings settings, BuildEditorContext context) { if (context.GroupId == TwitterConstants.Features.Twitter) { var user = _httpContextAccessor.HttpContext?.User; if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageTwitter)) { return(null); } var model = new TwitterSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); if (context.Updater.ModelState.IsValid) { var protector = _dataProtectionProvider.CreateProtector(TwitterConstants.Features.Twitter); settings.ConsumerKey = model.APIKey; settings.ConsumerSecret = protector.Protect(model.APISecretKey); settings.AccessToken = model.AccessToken; settings.AccessTokenSecret = protector.Protect(model.AccessTokenSecret); await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(await EditAsync(settings, context)); }
private async Task UpdateConfiguration() { var siteSettings = await _siteService.LoadSiteSettingsAsync(); if (NeedsUpdate(siteSettings)) { SetConfiguration(siteSettings); SetHash(siteSettings); await _siteService.UpdateSiteSettingsAsync(siteSettings); await _shellHost.ReleaseShellContextAsync(_shellSettings); } }
public async Task ImportFromFileAsync(IFileProvider fileProvider) { var executionId = Guid.NewGuid().ToString("n"); var recipeDescriptor = new RecipeDescriptor { FileProvider = fileProvider, BasePath = "", RecipeFileInfo = fileProvider.GetFileInfo("Recipe.json") }; await _recipeExecutor.ExecuteAsync(executionId, recipeDescriptor, new object(), CancellationToken.None); await _shellHost.ReleaseShellContextAsync(_shellSettings); }
public async Task <ActionResult> Execute(string basePath, string fileName) { if (!await _authorizationService.AuthorizeAsync(User, StandardPermissions.SiteOwner)) { return(Forbid()); } var recipeCollections = await Task.WhenAll(_recipeHarvesters.Select(x => x.HarvestRecipesAsync())); var recipes = recipeCollections.SelectMany(x => x); var recipe = recipes.FirstOrDefault(c => c.RecipeFileInfo.Name == fileName && c.BasePath == basePath); if (recipe == null) { _notifier.Error(H["Recipe was not found"]); return(RedirectToAction("Index")); } var site = await _siteService.GetSiteSettingsAsync(); var executionId = Guid.NewGuid().ToString("n"); // Set shell state to "Initializing" so that subsequent HTTP requests // are responded to with "Service Unavailable" while running the recipe. _shellSettings.State = TenantState.Initializing; try { await _recipeExecutor.ExecuteAsync(executionId, recipe, new { site.SiteName, AdminUsername = User.Identity.Name, AdminUserId = User.FindFirstValue(ClaimTypes.NameIdentifier) }, CancellationToken.None); } finally { // Don't lock the tenant if the recipe fails. _shellSettings.State = TenantState.Running; } await _shellHost.ReleaseShellContextAsync(_shellSettings); _notifier.Success(H["The recipe '{0}' has been run successfully", recipe.DisplayName]); return(RedirectToAction("Index")); }
/// <inheritdocs /> public override async Task <IDisplayResult> UpdateAsync(LocalizationSettings section, BuildEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageCultures)) { return(null); } if (context.GroupId == GroupId) { var model = new LocalizationSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); var supportedCulture = JsonConvert.DeserializeObject <string[]>(model.SupportedCultures); if (!supportedCulture.Any()) { context.Updater.ModelState.AddModelError("SupportedCultures", S["A culture is required"]); } if (context.Updater.ModelState.IsValid) { // Invariant culture name is empty so a null value is bound. section.DefaultCulture = model.DefaultCulture ?? ""; section.SupportedCultures = supportedCulture; if (!section.SupportedCultures.Contains(section.DefaultCulture)) { section.DefaultCulture = section.SupportedCultures[0]; } // We always release the tenant for the default culture and also supported cultures to take effect await _shellHost.ReleaseShellContextAsync(_shellSettings); // We create a transient scope with the newly selected culture to create a notification that will use it instead of the previous culture using (CultureScope.Create(section.DefaultCulture)) { await _notifier.WarningAsync(H["The site has been restarted for the settings to take effect."]); } } } return(await EditAsync(section, context)); }
public override async Task <IDisplayResult> UpdateAsync(RestreamSettings section, BuildEditorContext context) { if (context.GroupId == GroupId) { var model = new RestreamSettingsViewModel(); if (await context.Updater.TryUpdateModelAsync(model, Prefix)) { section.TokenKey = model.TokenKey?.Trim(); // Release the tenant to apply settings. await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(await EditAsync(section, context)); }
public async Task ImportFromFileAsync(IFileProvider fileProvider) { var executionId = Guid.NewGuid().ToString("n"); var recipeDescriptor = new RecipeDescriptor { FileProvider = fileProvider, BasePath = "", RecipeFileInfo = fileProvider.GetFileInfo("Recipe.json") }; var environment = new Dictionary <string, object>(); await _environmentProviders.OrderBy(x => x.Order).InvokeAsync((provider, env) => provider.PopulateEnvironmentAsync(env), environment, _logger); await _recipeExecutor.ExecuteAsync(executionId, recipeDescriptor, environment, CancellationToken.None); await _shellHost.ReleaseShellContextAsync(_shellSettings); }
public async Task <ActionResult> Execute(string basePath, string fileName) { if (!await _authorizationService.AuthorizeAsync(User, StandardPermissions.SiteOwner)) { return(Forbid()); } var features = await _shellFeaturesManager.GetAvailableFeaturesAsync(); var recipes = await GetRecipesAsync(features); var recipe = recipes.FirstOrDefault(c => c.RecipeFileInfo.Name == fileName && c.BasePath == basePath); if (recipe == null) { await _notifier.ErrorAsync(H["Recipe was not found."]); return(RedirectToAction(nameof(Index))); } var environment = new Dictionary <string, object>(); await _environmentProviders.OrderBy(x => x.Order).InvokeAsync((provider, env) => provider.PopulateEnvironmentAsync(env), environment, _logger); var executionId = Guid.NewGuid().ToString("n"); // Set shell state to "Initializing" so that subsequent HTTP requests // are responded to with "Service Unavailable" while running the recipe. _shellSettings.State = TenantState.Initializing; try { await _recipeExecutor.ExecuteAsync(executionId, recipe, environment, CancellationToken.None); } finally { // Don't lock the tenant if the recipe fails. _shellSettings.State = TenantState.Running; } await _shellHost.ReleaseShellContextAsync(_shellSettings); await _notifier.SuccessAsync(H["The recipe '{0}' has been run successfully.", recipe.DisplayName]); return(RedirectToAction(nameof(Index))); }
/// <inheritdoc/> public override async Task <IDisplayResult> UpdateAsync(GdprSettings section, BuildEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGdprSettings)) { return(null); } if (context.GroupId == GroupId) { await context.Updater.TryUpdateModelAsync(section, Prefix); await _shellHost.ReleaseShellContextAsync(_shellSettings); } return(await EditAsync(section, context)); }
public override async Task <IDisplayResult> UpdateAsync(ReverseProxySettings section, BuildEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageReverseProxySettings)) { return(null); } if (context.GroupId == SettingsGroupId) { var model = new ReverseProxySettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); section.ForwardedHeaders = ForwardedHeaders.None; if (model.EnableXForwardedFor) { section.ForwardedHeaders |= ForwardedHeaders.XForwardedFor; } if (model.EnableXForwardedHost) { section.ForwardedHeaders |= ForwardedHeaders.XForwardedHost; } if (model.EnableXForwardedProto) { section.ForwardedHeaders |= ForwardedHeaders.XForwardedProto; } // If the settings are valid, release the current tenant. if (context.Updater.ModelState.IsValid) { await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(await EditAsync(section, context)); }
public override async Task <IDisplayResult> UpdateAsync(FacebookLoginSettings settings, BuildEditorContext context) { if (context.GroupId == FacebookConstants.Features.Login) { var user = _httpContextAccessor.HttpContext?.User; if (user == null || !await _authorizationService.AuthorizeAsync(user, Permissions.ManageFacebookApp)) { return(null); } var model = new FacebookLoginSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); if (context.Updater.ModelState.IsValid) { settings.CallbackPath = model.CallbackPath; await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(await EditAsync(settings, context)); }
public override async Task <IDisplayResult> UpdateAsync(HttpsSettings settings, BuildEditorContext context) { if (context.GroupId == SettingsGroupId) { var model = new HttpsSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); settings.EnableStrictTransportSecurity = model.EnableStrictTransportSecurity; settings.RequireHttps = model.RequireHttps; settings.RequireHttpsPermanent = model.RequireHttpsPermanent; settings.SslPort = model.SslPort; // If the settings are valid, release the current tenant. if (context.Updater.ModelState.IsValid) { await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(await EditAsync(settings, context)); }
public async Task <IActionResult> IndexPost() { if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageServerSettings)) { return(Forbid()); } var settings = await _serverService.GetSettingsAsync(); var shape = await _serverSettingsDisplayManager.UpdateEditorAsync(settings, updater : _updateModelAccessor.ModelUpdater, isNew : false); if (!ModelState.IsValid) { return(View(shape)); } foreach (var result in await _serverService.ValidateSettingsAsync(settings)) { if (result != ValidationResult.Success) { var key = result.MemberNames.FirstOrDefault() ?? string.Empty; ModelState.AddModelError(key, result.ErrorMessage); } } if (!ModelState.IsValid) { return(View(shape)); } await _serverService.UpdateSettingsAsync(settings); _notifier.Success(H["OpenID server configuration successfully updated."]); await _shellHost.ReleaseShellContextAsync(_shellSettings); return(RedirectToAction(nameof(Index))); }
public override async Task <IDisplayResult> UpdateAsync(AzureADSettings settings, BuildEditorContext context) { if (context.GroupId == MicrosoftAuthenticationConstants.Features.AAD) { var user = _httpContextAccessor.HttpContext?.User; if (user == null || !await _authorizationService.AuthorizeAsync(user, Permissions.ManageMicrosoftAuthentication)) { return(null); } var model = new AzureADSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); if (context.Updater.ModelState.IsValid) { settings.DisplayName = model.DisplayName; settings.AppId = model.AppId; settings.TenantId = model.TenantId; settings.CallbackPath = model.CallbackPath; await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(await EditAsync(settings, context)); }
/// <summary> /// Keep in sync tenants by sharing shell identifiers through an <see cref="IDistributedCache"/>. /// </summary> protected override async Task ExecuteAsync(CancellationToken stoppingToken) { stoppingToken.Register(() => { _logger.LogInformation("'{ServiceName}' is stopping.", nameof(DistributedShellHostedService)); }); // Init the idle time. var idleTime = MinIdleTime; while (!stoppingToken.IsCancellationRequested) { try { // Wait for the current idle time on each loop. if (!await TryWaitAsync(idleTime, stoppingToken)) { break; } // If there is no default tenant or it is not running, nothing to do. if (!_shellHost.TryGetShellContext(ShellHelper.DefaultShellName, out var defaultContext) || defaultContext.Settings.State != TenantState.Running) { continue; } // Get or create a new distributed context if the default tenant has changed. var context = await GetOrCreateDistributedContextAsync(defaultContext); // If the required distributed features are not enabled, nothing to do. var distributedCache = context?.DistributedCache; if (distributedCache == null) { continue; } // Try to retrieve the tenant changed global identifier from the distributed cache. string shellChangedId; try { shellChangedId = await distributedCache.GetStringAsync(ShellChangedIdKey); } catch (Exception ex) when(!ex.IsFatal()) { // Get the next idle time before retrying to read the distributed cache. idleTime = NextIdleTimeBeforeRetry(idleTime, ex); continue; } // Reset the idle time. idleTime = MinIdleTime; // Check if at least one tenant has changed. if (shellChangedId == null || _shellChangedId == shellChangedId) { continue; } // Try to retrieve the tenant created global identifier from the distributed cache. string shellCreatedId; try { shellCreatedId = await distributedCache.GetStringAsync(ShellCreatedIdKey); } catch (Exception ex) when(!ex.IsFatal()) { _logger.LogError(ex, "Unable to read the distributed cache before checking if a tenant has been created."); continue; } // Retrieve all tenant settings that are already loaded. var allSettings = _shellHost.GetAllSettings().ToList(); // Check if at least one tenant has been created. if (shellCreatedId != null && _shellCreatedId != shellCreatedId) { // Retrieve all new created tenants that are not already loaded. var names = (await _shellSettingsManager.LoadSettingsNamesAsync()) .Except(allSettings.Select(s => s.Name)) .ToArray(); // Load and enlist the settings of all new created tenant. foreach (var name in names) { allSettings.Add(await _shellSettingsManager.LoadSettingsAsync(name)); } } // Init the busy start time. var _busyStartTime = DateTime.UtcNow; var syncingSuccess = true; // Keep in sync all tenants by checking their specific identifiers. foreach (var settings in allSettings) { // Wait for the min idle time after the max busy time. if (!await TryWaitAfterBusyTime(stoppingToken)) { break; } var semaphore = _semaphores.GetOrAdd(settings.Name, name => new SemaphoreSlim(1)); await semaphore.WaitAsync(); try { // Try to retrieve the release identifier of this tenant from the distributed cache. var releaseId = await distributedCache.GetStringAsync(ReleaseIdKey(settings.Name)); if (releaseId != null) { // Check if the release identifier of this tenant has changed. var identifier = _identifiers.GetOrAdd(settings.Name, name => new ShellIdentifier()); if (identifier.ReleaseId != releaseId) { // Upate the local identifier. identifier.ReleaseId = releaseId; // Keep in sync this tenant by releasing it locally. await _shellHost.ReleaseShellContextAsync(settings, eventSource : false); } } // Try to retrieve the reload identifier of this tenant from the distributed cache. var reloadId = await distributedCache.GetStringAsync(ReloadIdKey(settings.Name)); if (reloadId != null) { // Check if the reload identifier of this tenant has changed. var identifier = _identifiers.GetOrAdd(settings.Name, name => new ShellIdentifier()); if (identifier.ReloadId != reloadId) { // Upate the local identifier. identifier.ReloadId = reloadId; // Keep in sync this tenant by reloading it locally. await _shellHost.ReloadShellContextAsync(settings, eventSource : false); } } } catch (Exception ex) when(!ex.IsFatal()) { syncingSuccess = false; _logger.LogError(ex, "Unable to read the distributed cache while syncing the tenant '{TenantName}'.", settings.Name); break; } finally { semaphore.Release(); } } // Keep in sync the tenant global identifiers. if (syncingSuccess) { _shellChangedId = shellChangedId; _shellCreatedId = shellCreatedId; } } catch (Exception ex) when(!ex.IsFatal()) { _logger.LogError(ex, "Error while executing '{ServiceName}'", nameof(DistributedShellHostedService)); } } _terminated = true; _context?.Release(); _defaultContext = null; _context = null; }
public override async Task <IDisplayResult> UpdateAsync(OpenIdClientSettings settings, BuildEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; if (user == null || !await _authorizationService.AuthorizeAsync(user, Permissions.ManageClientSettings)) { return(null); } if (context.GroupId == SettingsGroupId) { var previousClientSecret = settings.ClientSecret; var model = new OpenIdClientSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); model.Scopes = model.Scopes ?? string.Empty; settings.DisplayName = model.DisplayName; settings.Scopes = model.Scopes.Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries); settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null; settings.CallbackPath = model.CallbackPath; settings.ClientId = model.ClientId; settings.SignedOutCallbackPath = model.SignedOutCallbackPath; settings.SignedOutRedirectUri = model.SignedOutRedirectUri; settings.ResponseMode = model.ResponseMode; settings.StoreExternalTokens = model.StoreExternalTokens; bool useClientSecret = true; if (model.UseCodeFlow) { settings.ResponseType = OpenIdConnectResponseType.Code; } else if (model.UseCodeIdTokenFlow) { settings.ResponseType = OpenIdConnectResponseType.CodeIdToken; } else if (model.UseCodeIdTokenTokenFlow) { settings.ResponseType = OpenIdConnectResponseType.CodeIdTokenToken; } else if (model.UseCodeTokenFlow) { settings.ResponseType = OpenIdConnectResponseType.CodeToken; } else if (model.UseIdTokenFlow) { settings.ResponseType = OpenIdConnectResponseType.IdToken; useClientSecret = false; } else if (model.UseIdTokenTokenFlow) { settings.ResponseType = OpenIdConnectResponseType.IdTokenToken; useClientSecret = false; } else { settings.ResponseType = OpenIdConnectResponseType.None; useClientSecret = false; } if (!useClientSecret) { model.ClientSecret = previousClientSecret = null; } // Restore the client secret if the input is empty (i.e if it hasn't been reset). if (string.IsNullOrEmpty(model.ClientSecret)) { settings.ClientSecret = previousClientSecret; } else { var protector = _dataProtectionProvider.CreateProtector(nameof(OpenIdClientConfiguration)); settings.ClientSecret = protector.Protect(model.ClientSecret); } foreach (var result in await _clientService.ValidateSettingsAsync(settings)) { if (result != ValidationResult.Success) { var key = result.MemberNames.FirstOrDefault() ?? string.Empty; context.Updater.ModelState.AddModelError(key, result.ErrorMessage); } } // If the settings are valid, release the current tenant. if (context.Updater.ModelState.IsValid) { await _shellHost.ReleaseShellContextAsync(_shellSettings); } } return(await EditAsync(settings, context)); }