public async Task <IActionResult> Reload(string id) { if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTenants)) { return(Unauthorized()); } if (!IsDefaultShell()) { return(Unauthorized()); } var shellSettings = _shellHost.GetAllSettings() .OrderBy(x => x.Name) .Where(x => string.Equals(x.Name, id, StringComparison.OrdinalIgnoreCase)) .FirstOrDefault(); if (shellSettings == null) { return(NotFound()); } // Generating routes can fail while the tenant is recycled as routes can use services. // It could be fixed by waiting for the next request or the end of the current one // to actually release the tenant. Right now we render the url before recycling the tenant. var redirectUrl = Url.Action(nameof(Index)); await _shellHost.ReloadShellContextAsync(shellSettings); return(Redirect(redirectUrl)); }
public override async Task <IDisplayResult> UpdateAsync(GoogleAuthenticationSettings settings, BuildEditorContext context) { if (context.GroupId == GoogleConstants.Features.GoogleAuthentication) { var user = _httpContextAccessor.HttpContext?.User; if (user == null || !await _authorizationService.AuthorizeAsync(user, Permissions.ManageGoogleAuthentication)) { return(null); } var model = new GoogleAuthenticationSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); if (context.Updater.ModelState.IsValid) { var protector = _dataProtectionProvider.CreateProtector(GoogleConstants.Features.GoogleAuthentication); settings.ClientID = model.ClientID; settings.ClientSecret = protector.Protect(model.ClientSecret); settings.CallbackPath = model.CallbackPath; await _shellHost.ReloadShellContextAsync(_shellSettings); } } return(await EditAsync(settings, context)); }
/// <summary> /// The auto setup middleware invoke. /// </summary> /// <param name="httpContext"> /// The http context. /// </param> /// <returns> /// The <see cref="Task"/>. /// </returns> public async Task InvokeAsync(HttpContext httpContext) { if (_setupOptions != null && _shellSettings.State == TenantState.Uninitialized) { // Try to acquire a lock before starting installation, it guaranties an atomic setup in multi instances environment. (var locker, var locked) = await _distributedLock.TryAcquireAutoSetupLockAsync(_lockOptions); if (!locked) { throw new TimeoutException($"Fails to acquire an auto setup lock for the tenant: {_setupOptions.ShellName}"); } await using var acquiredLock = locker; if (_shellSettings.State == TenantState.Uninitialized) { var pathBase = httpContext.Request.PathBase; if (!pathBase.HasValue) { pathBase = "/"; } // Check if the tenant was installed by another instance. var settings = await _shellSettingsManager.LoadSettingsAsync(_shellSettings.Name); if (settings.State != TenantState.Uninitialized) { await _shellHost.ReloadShellContextAsync(_shellSettings, eventSource : false); httpContext.Response.Redirect(pathBase); return; } var setupService = httpContext.RequestServices.GetRequiredService <ISetupService>(); if (await SetupTenantAsync(setupService, _setupOptions, _shellSettings)) { if (_setupOptions.IsDefault) { // Create the rest of the shells for further on demand setup. foreach (var setupOptions in _options.Tenants) { if (_setupOptions != setupOptions) { await CreateTenantSettingsAsync(setupOptions); } } } httpContext.Response.Redirect(pathBase); return; } } } await _next.Invoke(httpContext); }
public async Task UpdateCustomStyleSiteSettingsAsync(ISite site) { // Persists new data. Session.Save(site); // Invalidates the cache after the session is committed. _memoryCache.Remove(CacheKey); _signal.DeferredSignalToken(CacheKey); //Reset ShellHost to apply settings await _orchardHost.ReloadShellContextAsync(_currentShellSettings); }
public override async Task <IDisplayResult> UpdateAsync(ReCaptchaSettings section, BuildEditorContext context) { 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(); // Reload tenant to apply settings. await _shellHost.ReloadShellContextAsync(_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.ReloadShellContextAsync(_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, reload the current tenant. if (context.Updater.ModelState.IsValid) { await _shellHost.ReloadShellContextAsync(_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.BaseUrl = model.BaseUrl; site.TimeZoneId = model.TimeZone; site.UseCdn = model.UseCdn; site.CdnBaseUrl = model.CdnBaseUrl; site.ResourceDebugMode = model.ResourceDebugMode; site.AppendVersion = model.AppendVersion; await _shellHost.ReloadShellContextAsync(_shellSettings); } } return(Edit(site)); }
public override async Task <IDisplayResult> UpdateAsync(ISite model, UpdateEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageNotificationSettings)) { return(null); } if (context.GroupId == GroupId) { var viewmodel = new AzureHubSettingsViewModel(); await context.Updater.TryUpdateModelAsync(viewmodel, Prefix); if (!string.IsNullOrWhiteSpace(viewmodel.NotificationTags)) { model.Properties.TryAdd(nameof(AzureHubSettingsViewModel.NotificationTags), JArray.FromObject(viewmodel.NotificationTags.Split(',').Select(t => t.Trim()))); } else { model.Properties.Remove(nameof(AzureHubSettingsViewModel.NotificationTags)); } var settings = new AzureHubSettings { Hub = viewmodel.Hub, Connection = viewmodel.Connection }; await _tenantConfigStore.SaveAsync(GroupId, settings); // Reload the tenant to apply the settings await _orchardHost.ReloadShellContextAsync(_currentShellSettings); } return(await EditAsync(model, 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.ReloadShellContextAsync(_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.ReloadShellContextAsync(_shellSettings); } } return(await EditAsync(settings, context)); }
public override async Task <IDisplayResult> UpdateAsync(OpenIdValidationSettings settings, BuildEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; if (user == null || !await _authorizationService.AuthorizeAsync(user, Permissions.ManageValidationSettings)) { return(null); } if (context.GroupId == SettingsGroupId) { var model = new OpenIdValidationSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); settings.Authority = model.Authority?.Trim(); settings.Audience = model.Audience?.Trim(); settings.Tenant = model.Tenant; foreach (var result in await _validationService.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, reload the current tenant. if (context.Updater.ModelState.IsValid) { await _shellHost.ReloadShellContextAsync(_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.BaseUrl = model.BaseUrl; site.TimeZoneId = model.TimeZone; site.Culture = model.Culture; site.UseCdn = model.UseCdn; site.ResourceDebugMode = model.ResourceDebugMode; } // We always reset the tenant for the default culture and also supported cultures to take effect await _shellHost.ReloadShellContextAsync(_shellSettings); _notifier.Warning(H["The site has been restarted for the settings to take effect"]); } return(Edit(site)); }
public override async Task <IDisplayResult> UpdateAsync(CommerceSettings section, BuildEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageCommerceSettings)) { return(null); } if (context.GroupId == GroupId) { var model = new CommerceSettingsViewModel(); if (await context.Updater.TryUpdateModelAsync(model, Prefix)) { section.DefaultCurrency = model.DefaultCurrency; } // Reload the tenant to apply the settings await _orchardHost.ReloadShellContextAsync(_currentShellSettings); } return(await EditAsync(section, context)); }
public override async Task <IDisplayResult> UpdateAsync(OpenIdServerSettings settings, BuildEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; if (user == null || !await _authorizationService.AuthorizeAsync(user, Permissions.ManageServerSettings)) { return(null); } if (context.GroupId == SettingsGroupId) { var model = new OpenIdServerSettingsViewModel(); await context.Updater.TryUpdateModelAsync(model, Prefix); settings.TestingModeEnabled = model.TestingModeEnabled; settings.AccessTokenFormat = model.AccessTokenFormat; settings.Authority = model.Authority; settings.CertificateStoreLocation = model.CertificateStoreLocation; settings.CertificateStoreName = model.CertificateStoreName; settings.CertificateThumbprint = model.CertificateThumbprint; settings.AuthorizationEndpointPath = model.EnableAuthorizationEndpoint ? new PathString("/connect/authorize") : PathString.Empty; settings.LogoutEndpointPath = model.EnableLogoutEndpoint ? new PathString("/connect/logout") : PathString.Empty; settings.TokenEndpointPath = model.EnableTokenEndpoint ? new PathString("/connect/token") : PathString.Empty; settings.UserinfoEndpointPath = model.EnableUserInfoEndpoint ? new PathString("/connect/userinfo") : PathString.Empty; if (model.AllowAuthorizationCodeFlow) { settings.GrantTypes.Add(GrantTypes.AuthorizationCode); } else { settings.GrantTypes.Remove(GrantTypes.AuthorizationCode); } if (model.AllowImplicitFlow) { settings.GrantTypes.Add(GrantTypes.Implicit); } else { settings.GrantTypes.Remove(GrantTypes.Implicit); } if (model.AllowClientCredentialsFlow) { settings.GrantTypes.Add(GrantTypes.ClientCredentials); } else { settings.GrantTypes.Remove(GrantTypes.ClientCredentials); } if (model.AllowPasswordFlow) { settings.GrantTypes.Add(GrantTypes.Password); } else { settings.GrantTypes.Remove(GrantTypes.Password); } if (model.AllowRefreshTokenFlow) { settings.GrantTypes.Add(GrantTypes.RefreshToken); } else { settings.GrantTypes.Remove(GrantTypes.RefreshToken); } settings.UseRollingTokens = model.UseRollingTokens; foreach (var result in await _serverService.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, reload the current tenant. if (context.Updater.ModelState.IsValid) { await _shellHost.ReloadShellContextAsync(_shellSettings); } } return(await EditAsync(settings, context)); }
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 = model.Authority; settings.CallbackPath = model.CallbackPath; settings.ClientId = model.ClientId; settings.SignedOutCallbackPath = model.SignedOutCallbackPath; settings.SignedOutRedirectUri = model.SignedOutRedirectUri; settings.ResponseMode = model.ResponseMode; 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, reload the current tenant. if (context.Updater.ModelState.IsValid) { await _shellHost.ReloadShellContextAsync(_shellSettings); } } return(await EditAsync(settings, context)); }
public override async Task PublishedAsync(PublishContentContext context, CustomStyleSettingsPart part) { // Reload the tenant to apply the settings await _orchardHost.ReloadShellContextAsync(_currentShellSettings); }
/// <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; }