Beispiel #1
0
        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));
        }
Beispiel #2
0
        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));
        }
Beispiel #5
0
        /// <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));
        }
Beispiel #6
0
        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);
     }
 }
Beispiel #9
0
 /// <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));
        }
Beispiel #11
0
        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));
        }
Beispiel #14
0
        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));
        }
Beispiel #15
0
        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);
        }
Beispiel #17
0
        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"));
        }
Beispiel #18
0
        /// <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);
        }
Beispiel #21
0
        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)));
        }
Beispiel #22
0
        /// <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));
        }
Beispiel #25
0
        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)));
        }
Beispiel #27
0
        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;
        }
Beispiel #29
0
        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));
        }