public async Task Invoke(HttpContext httpContext) { await _next.Invoke(httpContext); // Register the shell settings as a custom feature. var shellSettings = httpContext.Features.Get <ShellSettings>(); // We only serve the next request if the tenant has been resolved. if (shellSettings != null) { var deferredTaskEngine = httpContext.RequestServices.GetService <IDeferredTaskEngine>(); // Create a new scope only if there are pending tasks if (deferredTaskEngine != null && deferredTaskEngine.HasPendingTasks) { // Dispose the scoped services for the current request, and create a new one (httpContext.RequestServices as IDisposable).Dispose(); var shellContext = _orchardHost.GetOrCreateShellContext(shellSettings); if (!shellContext.Released) { using (var scope = shellContext.EnterServiceScope()) { var context = new DeferredTaskContext(scope.ServiceProvider); await deferredTaskEngine.ExecuteTasksAsync(context); } } } } }
public async Task Invoke(HttpContext httpContext) { // Ensure all ShellContext are loaded and available. _orchardHost.Initialize(); var shellSetting = _runningShellTable.Match(httpContext); // Register the shell settings as a custom feature. httpContext.Features.Set(shellSetting); // We only serve the next request if the tenant has been resolved. if (shellSetting != null) { var shellContext = _orchardHost.GetOrCreateShellContext(shellSetting); var existingRequestServices = httpContext.RequestServices; var scope = shellContext.CreateServiceScope(); try { httpContext.RequestServices = scope.ServiceProvider; if (!shellContext.IsActivated) { lock (shellContext) { // The tenant gets activated here if (!shellContext.IsActivated) { var tenantEvents = scope.ServiceProvider .GetServices <IModularTenantEvents>(); foreach (var tenantEvent in tenantEvents) { tenantEvent.ActivatingAsync().Wait(); } httpContext.Items["BuildPipeline"] = true; shellContext.IsActivated = true; foreach (var tenantEvent in tenantEvents) { tenantEvent.ActivatedAsync().Wait(); } } } } shellContext.RequestStarted(); await _next.Invoke(httpContext); } finally { // We dispose httpContext.RequestServices and not a local reference in case another middleware changed it (httpContext.RequestServices as IDisposable)?.Dispose(); shellContext.RequestEnded(); httpContext.RequestServices = existingRequestServices; } } }
public void Configure(string name, OpenIddictValidationOptions options) { // Ignore validation handler instances that don't correspond to the instance managed by the OpenID module. if (!string.Equals(name, OpenIddictValidationDefaults.AuthenticationScheme, StringComparison.Ordinal)) { return; } var settings = GetValidationSettingsAsync().GetAwaiter().GetResult(); if (settings == null) { return; } // If the tokens are issued by an authorization server located in a separate tenant, // resolve the isolated data protection provider associated with the specified tenant. if (!string.IsNullOrEmpty(settings.Tenant) && !string.Equals(settings.Tenant, _shellSettings.Name, StringComparison.Ordinal)) { var context = _shellHost.GetOrCreateShellContext(_shellSettingsManager.GetSettings(settings.Tenant)); using (var scope = context.EnterServiceScope()) { // If the other tenant is released, ensure the current tenant is also restarted as it // relies on a data protection provider whose lifetime is managed by the other tenant. // To make sure the other tenant is not disposed before all the pending requests are // processed by the current tenant, a tenant dependency is manually added. context.AddDependentShell(_shellHost.GetOrCreateShellContext(_shellSettings)); // Note: the data protection provider is always registered as a singleton and thus will // survive the current scope, which is mainly used to prevent the other tenant from being // released before we have a chance to declare the current tenant as a dependent tenant. options.DataProtectionProvider = scope.ServiceProvider.GetDataProtectionProvider(); } } // Don't allow the current tenant to choose the valid audiences, as this would // otherwise allow it to introspect tokens meant to be used with another tenant. options.Audiences.Add(OpenIdConstants.Prefixes.Tenant + _shellSettings.Name); }
public async Task Invoke(HttpContext httpContext) { // Ensure all ShellContext are loaded and available. _orchardHost.Initialize(); var shellSetting = _runningShellTable.Match(httpContext); // Register the shell settings as a custom feature. httpContext.Features.Set(shellSetting); // We only serve the next request if the tenant has been resolved. if (shellSetting != null) { var shellContext = _orchardHost.GetOrCreateShellContext(shellSetting); using (var scope = shellContext.CreateServiceScope()) { httpContext.RequestServices = scope.ServiceProvider; if (!shellContext.IsActivated) { lock (shellContext) { // The tenant gets activated here if (!shellContext.IsActivated) { var tenantEvents = scope.ServiceProvider .GetServices <IModularTenantEvents>(); foreach (var tenantEvent in tenantEvents) { tenantEvent.ActivatingAsync().Wait(); } httpContext.Items["BuildPipeline"] = true; shellContext.IsActivated = true; foreach (var tenantEvent in tenantEvents) { tenantEvent.ActivatedAsync().Wait(); } } } } await _next.Invoke(httpContext); } } }
public async Task Invoke(HttpContext httpContext) { //_logger.LogInformation($"DeferredTaskMiddleware"); await _next.Invoke(httpContext); //_logger.LogInformation($"httpContext.Features"); // Register the shell settings as a custom feature. var shellSettings = httpContext.Features.Get <ShellSettings>(); //_logger.LogInformation("eeewew2"); // We only serve the next request if the tenant has been resolved. if (shellSettings != null) { var deferredTaskEngine = httpContext.RequestServices.GetService <IDeferredTaskEngine>(); // Create a new scope only if there are pending tasks if (deferredTaskEngine != null && deferredTaskEngine.HasPendingTasks) { // Dispose the scoped services for the current request, and create a new one (httpContext.RequestServices as IDisposable).Dispose(); var shellContext = _OCoreHost.GetOrCreateShellContext(shellSettings); if (!shellContext.Released) { //var scope = shellContext.CreateServiceScope(); //httpContext.RequestServices = scope.ServiceProvider; //var context = new DeferredTaskContext(scope.ServiceProvider); //await deferredTaskEngine.ExecuteTasksAsync(context); //// We don't dispose the newly created request services scope as it will //// be done by ModularTenantContainerMiddleware using (var scope = shellContext.EnterServiceScope()) { var context = new DeferredTaskContext(scope.ServiceProvider); await deferredTaskEngine.ExecuteTasksAsync(context); } } } } }
public async Task <IActionResult> Create(EditTenantViewModel model) { if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTenants)) { return(Unauthorized()); } if (!IsDefaultShell()) { return(Unauthorized()); } if (ModelState.IsValid) { ValidateViewModel(model, true); } if (ModelState.IsValid) { var shellSettings = new ShellSettings { Name = model.Name, RequestUrlPrefix = model.RequestUrlPrefix?.Trim(), RequestUrlHost = model.RequestUrlHost, ConnectionString = model.ConnectionString, TablePrefix = model.TablePrefix, DatabaseProvider = model.DatabaseProvider, State = TenantState.Uninitialized }; _shellSettingsManager.SaveSettings(shellSettings); var shellContext = _orchardHost.GetOrCreateShellContext(shellSettings); return(RedirectToAction(nameof(Index))); } // If we got this far, something failed, redisplay form return(View(model)); }
public override async Task <IDisplayResult> EditAsync(OpenIdValidationSettings settings, BuildEditorContext context) { var user = _httpContextAccessor.HttpContext?.User; if (user == null || !await _authorizationService.AuthorizeAsync(user, Permissions.ManageValidationSettings)) { return(null); } return(Initialize <OpenIdValidationSettingsViewModel>("OpenIdValidationSettings_Edit", model => { model.Authority = settings.Authority; model.Audience = settings.Audience; model.Tenant = settings.Tenant; model.AvailableTenants = (from tenant in _shellSettingsManager.LoadSettings().AsParallel() let provider = _shellHost.GetOrCreateShellContext(tenant).ServiceProvider let descriptor = provider.GetRequiredService <ShellDescriptor>() where descriptor.Features.Any(feature => feature.Id == OpenIdConstants.Features.Server) select tenant.Name).ToList(); }).Location("Content:2").OnGroup(SettingsGroupId)); }
public async Task Invoke(HttpContext httpContext) { await _next.Invoke(httpContext); // Register the shell settings as a custom feature. var shellSettings = httpContext.Features.Get <ShellSettings>(); // We only serve the next request if the tenant has been resolved. if (shellSettings != null) { var shellContext = _orchardHost.GetOrCreateShellContext(shellSettings); using (var scope = shellContext.CreateServiceScope()) { var deferredTaskEngine = scope.ServiceProvider.GetService <IDeferredTaskEngine>(); if (deferredTaskEngine != null && deferredTaskEngine.HasPendingTasks) { var context = new DeferredTaskContext(scope.ServiceProvider); await deferredTaskEngine.ExecuteTasksAsync(context); } } } }
private async Task ExecuteStepAsync(RecipeExecutionContext recipeStep) { var shellContext = _orchardHost.GetOrCreateShellContext(_shellSettings); using (var scope = shellContext.EnterServiceScope()) { if (!shellContext.IsActivated) { var tenantEvents = scope.ServiceProvider .GetServices <IModularTenantEvents>(); foreach (var tenantEvent in tenantEvents) { tenantEvent.ActivatingAsync().Wait(); } shellContext.IsActivated = true; foreach (var tenantEvent in tenantEvents) { tenantEvent.ActivatedAsync().Wait(); } } var recipeStepHandlers = scope.ServiceProvider.GetServices <IRecipeStepHandler>(); var scriptingManager = scope.ServiceProvider.GetRequiredService <IScriptingManager>(); scriptingManager.GlobalMethodProviders.Add(_environmentMethodProvider); // Substitutes the script elements by their actual values EvaluateScriptNodes(recipeStep, scriptingManager); foreach (var recipeStepHandler in recipeStepHandlers) { if (Logger.IsEnabled(LogLevel.Information)) { Logger.LogInformation("Executing recipe step '{0}'.", recipeStep.Name); } await _recipeEventHandlers.InvokeAsync(e => e.RecipeStepExecutingAsync(recipeStep), Logger); await recipeStepHandler.ExecuteAsync(recipeStep); await _recipeEventHandlers.InvokeAsync(e => e.RecipeStepExecutedAsync(recipeStep), Logger); if (Logger.IsEnabled(LogLevel.Information)) { Logger.LogInformation("Finished executing recipe step '{0}'.", recipeStep.Name); } } } // The recipe execution might have invalidated the shell by enabling new features, // so the deferred tasks need to run on an updated shell context if necessary. shellContext = _orchardHost.GetOrCreateShellContext(_shellSettings); using (var scope = shellContext.EnterServiceScope()) { var deferredTaskEngine = scope.ServiceProvider.GetService <IDeferredTaskEngine>(); // The recipe might have added some deferred tasks to process if (deferredTaskEngine != null && deferredTaskEngine.HasPendingTasks) { var taskContext = new DeferredTaskContext(scope.ServiceProvider); await deferredTaskEngine.ExecuteTasksAsync(taskContext); } } }
// NB: Async void should be avoided; it should only be used for event handlers.Timer.Elapsed is an event handler.So, it's not necessarily wrong here. // c.f. http://stackoverflow.com/questions/25007670/using-async-await-inside-the-timer-elapsed-event-handler-within-a-windows-servic private async void DoWorkAsync(object group) { // DoWork needs to be re-entrant as Timer may call the callback before the previous callback has returned. // So, because a task may take longer than the period itself, DoWork needs to check if it's still running. ShellContext shellContext = _orchardHost.GetOrCreateShellContext(_shellSettings); var groupName = group as string ?? ""; foreach (var task in _tasks[groupName]) { var taskName = task.GetType().FullName; using (var scope = shellContext.EnterServiceScope()) { try { if (_states[task] != BackgroundTaskState.Idle) { return; } lock (_states) { // Ensure Terminate() was not called before if (_states[task] != BackgroundTaskState.Idle) { return; } _states[task] = BackgroundTaskState.Running; } if (Logger.IsEnabled(LogLevel.Information)) { Logger.LogInformation("Start processing background task \"{0}\".", taskName); } await task.DoWorkAsync(scope.ServiceProvider, _applicationLifetime.ApplicationStopping); if (Logger.IsEnabled(LogLevel.Information)) { Logger.LogInformation("Finished processing background task \"{0}\".", taskName); } } catch (Exception ex) { if (Logger.IsEnabled(LogLevel.Error)) { Logger.LogError(ex, $"Error while processing background task \"{taskName}\""); } } finally { lock (_states) { // Ensure Terminate() was not called during the task if (_states[task] != BackgroundTaskState.Stopped) { _states[task] = BackgroundTaskState.Idle; } } } } } }
public async Task <ImmutableArray <ValidationResult> > ValidateSettingsAsync(OpenIdValidationSettings settings) { if (settings == null) { throw new ArgumentNullException(nameof(settings)); } var results = ImmutableArray.CreateBuilder <ValidationResult>(); if (!(string.IsNullOrEmpty(settings.Authority) ^ string.IsNullOrEmpty(settings.Tenant))) { results.Add(new ValidationResult(T["Either a tenant or an authority must be registered."], new[] { nameof(settings.Authority), nameof(settings.Tenant) })); } if (!string.IsNullOrEmpty(settings.Authority)) { if (!Uri.TryCreate(settings.Authority, UriKind.Absolute, out Uri uri) || !uri.IsWellFormedOriginalString()) { results.Add(new ValidationResult(T["The specified authority is not valid."], new[] { nameof(settings.Authority) })); } if (!string.IsNullOrEmpty(uri.Query) || !string.IsNullOrEmpty(uri.Fragment)) { results.Add(new ValidationResult(T["The authority cannot contain a query string or a fragment."], new[] { nameof(settings.Authority) })); } } if (string.IsNullOrEmpty(settings.Authority) && !string.IsNullOrEmpty(settings.Audience)) { results.Add(new ValidationResult(T["No audience can be set when using another tenant."], new[] { nameof(settings.Audience) })); } if (!string.IsNullOrEmpty(settings.Authority) && string.IsNullOrEmpty(settings.Audience)) { results.Add(new ValidationResult(T["An audience must be set when configuring the authority."], new[] { nameof(settings.Audience) })); } if (!string.IsNullOrEmpty(settings.Audience) && settings.Audience.StartsWith(OpenIdConstants.Prefixes.Tenant, StringComparison.OrdinalIgnoreCase)) { results.Add(new ValidationResult(T["The audience cannot start with the special 'oct:' prefix."], new[] { nameof(settings.Audience) })); } // If a tenant was specified, ensure it is valid, that the OpenID server feature // was enabled and that at least a scope linked with the current tenant exists. if (!string.IsNullOrEmpty(settings.Tenant) && !string.Equals(settings.Tenant, _shellSettings.Name, StringComparison.Ordinal)) { if (!_shellSettingsManager.TryGetSettings(settings.Tenant, out var tenant)) { results.Add(new ValidationResult(T["The specified tenant is not valid."])); } else { var context = _shellHost.GetOrCreateShellContext(tenant); using (var scope = context.EnterServiceScope()) { var manager = scope.ServiceProvider.GetService <IOpenIdScopeManager>(); if (manager == null) { results.Add(new ValidationResult(T["The specified tenant is not valid."], new[] { nameof(settings.Tenant) })); } else { var resource = OpenIdConstants.Prefixes.Tenant + _shellSettings.Name; var scopes = await manager.FindByResourceAsync(resource); if (scopes.IsDefaultOrEmpty) { results.Add(new ValidationResult(T["No appropriate scope was found."], new[] { nameof(settings.Tenant) })); } } } } } return(results.ToImmutable()); }
public async Task Invoke(HttpContext httpContext) { // Ensure all ShellContext are loaded and available. _orchardHost.Initialize(); var shellSettings = _runningShellTable.Match(httpContext); // We only serve the next request if the tenant has been resolved. if (shellSettings != null) { var shellContext = _orchardHost.GetOrCreateShellContext(shellSettings); var hasPendingTasks = false; using (var scope = shellContext.EnterServiceScope()) { // Register the shell context as a custom feature. httpContext.Features.Set(shellContext); if (!shellContext.IsActivated) { var semaphore = _semaphores.GetOrAdd(shellSettings.Name, (name) => new SemaphoreSlim(1)); await semaphore.WaitAsync(); try { // The tenant gets activated here if (!shellContext.IsActivated) { using (var activatingScope = shellContext.EnterServiceScope()) { var tenantEvents = activatingScope.ServiceProvider.GetServices <IModularTenantEvents>(); foreach (var tenantEvent in tenantEvents) { await tenantEvent.ActivatingAsync(); } httpContext.Items["BuildPipeline"] = true; foreach (var tenantEvent in tenantEvents.Reverse()) { await tenantEvent.ActivatedAsync(); } } shellContext.IsActivated = true; } } finally { semaphore.Release(); _semaphores.TryRemove(shellSettings.Name, out semaphore); } } await _next.Invoke(httpContext); var deferredTaskEngine = scope.ServiceProvider.GetService <IDeferredTaskEngine>(); hasPendingTasks = deferredTaskEngine?.HasPendingTasks ?? false; } // Create a new scope only if there are pending tasks if (hasPendingTasks) { shellContext = _orchardHost.GetOrCreateShellContext(shellSettings); using (var scope = shellContext.EnterServiceScope()) { var deferredTaskEngine = scope.ServiceProvider.GetService <IDeferredTaskEngine>(); var context = new DeferredTaskContext(scope.ServiceProvider); await deferredTaskEngine.ExecuteTasksAsync(context); } } } }
public async Task ProcessContentItemsAsync() { // TODO: Lock over the filesystem in case two instances get a command to rebuild the index concurrently. var allIndices = new Dictionary <string, int>(); // Find the lowest task id to process int lastTaskId = int.MaxValue; foreach (var indexName in _indexManager.List()) { var taskId = _indexingState.GetLastTaskId(indexName); lastTaskId = Math.Min(lastTaskId, taskId); allIndices.Add(indexName, taskId); } if (!allIndices.Any()) { return; } IndexingTask[] batch; var shellContext = _shellHost.GetOrCreateShellContext(_shellSettings); do { // Create a scope for the content manager using (var scope = shellContext.EnterServiceScope()) { // Load the next batch of tasks batch = (await _indexingTaskManager.GetIndexingTasksAsync(lastTaskId, BatchSize)).ToArray(); if (!batch.Any()) { break; } foreach (var task in batch) { var contentManager = scope.ServiceProvider.GetRequiredService <IContentManager>(); var indexHandlers = scope.ServiceProvider.GetServices <IContentItemIndexHandler>(); foreach (var index in allIndices) { // TODO: ignore if this index is not configured for the content type if (index.Value < task.Id) { _indexManager.DeleteDocuments(index.Key, new string[] { task.ContentItemId }); } } if (task.Type == IndexingTaskTypes.Update) { var contentItem = await contentManager.GetAsync(task.ContentItemId); var context = new BuildIndexContext(new DocumentIndex(task.ContentItemId), contentItem, contentItem.ContentType); // Update the document from the index if its lastIndexId is smaller than the current task id. await indexHandlers.InvokeAsync(x => x.BuildIndexAsync(context), Logger); foreach (var index in allIndices) { if (index.Value < task.Id) { _indexManager.StoreDocuments(index.Key, new DocumentIndex[] { context.DocumentIndex }); } } } } // Update task ids lastTaskId = batch.Last().Id; foreach (var index in allIndices) { if (index.Value < lastTaskId) { _indexingState.SetLastTaskId(index.Key, lastTaskId); } } _indexingState.Update(); } } while (batch.Length == BatchSize); }