From 73b13d7a5455e081acfc8e75f97ffcef73f5d136 Mon Sep 17 00:00:00 2001 From: hishamco Date: Sun, 18 Apr 2021 02:25:40 +0300 Subject: [PATCH 1/5] Add Oqtane extension methods for clean startup --- .../OqtaneServiceCollectionExtensions.cs | 194 ++++++++++++++++++ Oqtane.Server/Startup.cs | 164 ++------------- 2 files changed, 214 insertions(+), 144 deletions(-) diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index 36c41601..c429b282 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -1,11 +1,21 @@ using System; using System.IO; using System.Linq; +using System.Net.Http; using System.Reflection; using System.Runtime.Loader; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; using Oqtane.Infrastructure; using Oqtane.Modules; +using Oqtane.Repository; +using Oqtane.Security; using Oqtane.Services; using Oqtane.Shared; @@ -23,6 +33,190 @@ namespace Microsoft.Extensions.DependencyInjection return services; } + public static IServiceCollection AddOqtaneDbContext(this IServiceCollection services) + { + services.AddDbContext(options => { }); + services.AddDbContext(options => { }); + + services.AddIdentityCore(options => { }) + .AddEntityFrameworkStores() + .AddSignInManager() + .AddDefaultTokenProviders(); + + return services; + } + + public static IServiceCollection AddOqtaneAuthorizationPolicies(this IServiceCollection services) + { + services.AddAuthorizationCore(options => + { + options.AddPolicy(PolicyNames.ViewPage, policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Page, PermissionNames.View))); + options.AddPolicy(PolicyNames.EditPage, policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Page, PermissionNames.Edit))); + options.AddPolicy(PolicyNames.ViewModule, policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Module, PermissionNames.View))); + options.AddPolicy(PolicyNames.EditModule, policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Module, PermissionNames.Edit))); + options.AddPolicy(PolicyNames.ViewFolder, policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Folder, PermissionNames.View))); + options.AddPolicy(PolicyNames.EditFolder, policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Folder, PermissionNames.Edit))); + options.AddPolicy(PolicyNames.ListFolder, policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Folder, PermissionNames.Browse))); + }); + + return services; + } + + internal static IServiceCollection AddOqtaneScopedServices(this IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + return services; + } + + internal static IServiceCollection AddOqtaneSingletonServices(this IServiceCollection services, IConfigurationRoot configurationRoot) + { + services.AddSingleton(configurationRoot); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + return services; + } + + internal static IServiceCollection AddOqtaneTransientServices(this IServiceCollection services) + { + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + return services; + } + + public static IServiceCollection ConfigureOqtaneCookieOptions(this IServiceCollection services) + { + services.ConfigureApplicationCookie(options => + { + options.Cookie.HttpOnly = false; + options.Events.OnRedirectToLogin = context => + { + context.Response.StatusCode = 401; + + return Task.CompletedTask; + }; + }); + + return services; + } + + public static IServiceCollection ConfigureOqtaneIdentityOptions(this IServiceCollection services) + { + services.Configure(options => + { + // Password settings + options.Password.RequireDigit = false; + options.Password.RequiredLength = 6; + options.Password.RequireNonAlphanumeric = false; + options.Password.RequireUppercase = false; + options.Password.RequireLowercase = false; + + // Lockout settings + options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30); + options.Lockout.MaxFailedAccessAttempts = 10; + options.Lockout.AllowedForNewUsers = true; + + // User settings + options.User.RequireUniqueEmail = false; + }); + + return services; + } + + internal static IServiceCollection TryAddHttpClientWithAuthenticationCookie(this IServiceCollection services) + { + if (!services.Any(x => x.ServiceType == typeof(HttpClient))) + { + services.AddScoped(s => + { + // creating the URI helper needs to wait until the JS Runtime is initialized, so defer it. + var navigationManager = s.GetRequiredService(); + var httpContextAccessor = s.GetRequiredService(); + var authToken = httpContextAccessor.HttpContext.Request.Cookies[".AspNetCore.Identity.Application"]; + var client = new HttpClient(new HttpClientHandler { UseCookies = false }); + if (authToken != null) + { + client.DefaultRequestHeaders.Add("Cookie", ".AspNetCore.Identity.Application=" + authToken); + } + + client.BaseAddress = new Uri(navigationManager.Uri); + + return client; + }); + } + + return services; + } + + internal static IServiceCollection TryAddSwagger(this IServiceCollection services, bool useSwagger) + { + if (useSwagger) + { + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "Oqtane", Version = "v1" }); + }); + } + + return services; + } + private static IServiceCollection AddOqtaneServices(this IServiceCollection services, Runtime runtime) { if (services is null) diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 1e744c01..f2c44365 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -1,35 +1,25 @@ using System; -using System.Collections; using System.IO; -using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.OpenApi.Models; using Oqtane.Extensions; using Oqtane.Infrastructure; -using Oqtane.Repository; using Oqtane.Security; -using Oqtane.Services; using Oqtane.Shared; namespace Oqtane { public class Startup { - private Runtime _runtime; - private bool _useSwagger; - private IWebHostEnvironment _env; - private string[] _supportedCultures; + private readonly Runtime _runtime; + private readonly bool _useSwagger; + private readonly IWebHostEnvironment _env; + private readonly string[] _supportedCultures; public IConfigurationRoot Configuration { get; } @@ -59,157 +49,46 @@ namespace Oqtane // Register localization services services.AddLocalization(options => options.ResourcesPath = "Resources"); - services.AddServerSideBlazor().AddCircuitOptions(options => - { - if (_env.IsDevelopment()) + services.AddServerSideBlazor() + .AddCircuitOptions(options => { - options.DetailedErrors = true; - } - }); + if (_env.IsDevelopment()) + { + options.DetailedErrors = true; + } + }); // setup HttpClient for server side in a client side compatible fashion ( with auth cookie ) - if (!services.Any(x => x.ServiceType == typeof(HttpClient))) - { - services.AddScoped(s => - { - // creating the URI helper needs to wait until the JS Runtime is initialized, so defer it. - var navigationManager = s.GetRequiredService(); - var httpContextAccessor = s.GetRequiredService(); - var authToken = httpContextAccessor.HttpContext.Request.Cookies[".AspNetCore.Identity.Application"]; - var client = new HttpClient(new HttpClientHandler {UseCookies = false}); - if (authToken != null) - { - client.DefaultRequestHeaders.Add("Cookie", ".AspNetCore.Identity.Application=" + authToken); - } - client.BaseAddress = new Uri(navigationManager.Uri); - return client; - }); - } + services.TryAddHttpClientWithAuthenticationCookie(); // register custom authorization policies - services.AddAuthorizationCore(options => - { - options.AddPolicy(PolicyNames.ViewPage, policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Page, PermissionNames.View))); - options.AddPolicy(PolicyNames.EditPage, policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Page, PermissionNames.Edit))); - options.AddPolicy(PolicyNames.ViewModule, policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Module, PermissionNames.View))); - options.AddPolicy(PolicyNames.EditModule, policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Module, PermissionNames.Edit))); - options.AddPolicy(PolicyNames.ViewFolder, policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Folder, PermissionNames.View))); - options.AddPolicy(PolicyNames.EditFolder, policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Folder, PermissionNames.Edit))); - options.AddPolicy(PolicyNames.ListFolder, policy => policy.Requirements.Add(new PermissionRequirement(EntityNames.Folder, PermissionNames.Browse))); - }); + services.AddOqtaneAuthorizationPolicies(); // register scoped core services - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddOqtaneScopedServices(); services.AddSingleton(); - services.AddDbContext(options => { }); - services.AddDbContext(options => { }); + services.AddOqtaneDbContext(); - services.AddIdentityCore(options => { }) - .AddEntityFrameworkStores() - .AddSignInManager() - .AddDefaultTokenProviders(); - - services.Configure(options => - { - // Password settings - options.Password.RequireDigit = false; - options.Password.RequiredLength = 6; - options.Password.RequireNonAlphanumeric = false; - options.Password.RequireUppercase = false; - options.Password.RequireLowercase = false; - - // Lockout settings - options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30); - options.Lockout.MaxFailedAccessAttempts = 10; - options.Lockout.AllowedForNewUsers = true; - - // User settings - options.User.RequireUniqueEmail = false; - }); + services.ConfigureOqtaneIdentityOptions(); services.AddAuthentication(IdentityConstants.ApplicationScheme) .AddCookie(IdentityConstants.ApplicationScheme); - services.ConfigureApplicationCookie(options => - { - options.Cookie.HttpOnly = false; - options.Events.OnRedirectToLogin = context => - { - context.Response.StatusCode = 401; - return Task.CompletedTask; - }; - }); + services.ConfigureOqtaneCookieOptions(); // register custom claims principal factory for role claims services.AddTransient, ClaimsPrincipalFactory>(); // register singleton scoped core services - services.AddSingleton(Configuration); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddOqtaneSingletonServices(Configuration); // install any modules or themes ( this needs to occur BEFORE the assemblies are loaded into the app domain ) InstallationManager.InstallPackages("Modules,Themes", _env.WebRootPath, _env.ContentRootPath); // register transient scoped core services - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + services.AddOqtaneTransientServices(); // load the external assemblies into the app domain, install services services.AddOqtane(_runtime, _supportedCultures); @@ -219,10 +98,7 @@ namespace Oqtane .AddOqtaneApplicationParts() // register any Controllers from custom modules .ConfigureOqtaneMvc(); // any additional configuration from IStart classes. - if (_useSwagger) - { - services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo {Title = "Oqtane", Version = "v1"}); }); - } + services.TryAddSwagger(_useSwagger); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. From a018e853a8173327fcc7b07538c548f8fbbcceff Mon Sep 17 00:00:00 2001 From: hishamco Date: Sun, 18 Apr 2021 02:27:31 +0300 Subject: [PATCH 2/5] Register configuration in startup --- Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs | 4 +--- Oqtane.Server/Startup.cs | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index c429b282..bfa20cd5 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; using Oqtane.Infrastructure; @@ -96,9 +95,8 @@ namespace Microsoft.Extensions.DependencyInjection return services; } - internal static IServiceCollection AddOqtaneSingletonServices(this IServiceCollection services, IConfigurationRoot configurationRoot) + internal static IServiceCollection AddOqtaneSingletonServices(this IServiceCollection services) { - services.AddSingleton(configurationRoot); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index f2c44365..1a5de4df 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -82,7 +82,8 @@ namespace Oqtane services.AddTransient, ClaimsPrincipalFactory>(); // register singleton scoped core services - services.AddOqtaneSingletonServices(Configuration); + services.AddSingleton(Configuration) + .AddOqtaneSingletonServices(); // install any modules or themes ( this needs to occur BEFORE the assemblies are loaded into the app domain ) InstallationManager.InstallPackages("Modules,Themes", _env.WebRootPath, _env.ContentRootPath); From f7d8888232ea875fdbb6a2c4698ca44a83137228 Mon Sep 17 00:00:00 2001 From: hishamco Date: Tue, 20 Apr 2021 19:01:56 +0300 Subject: [PATCH 3/5] Refactor Program.cs --- .../OqtaneServiceCollectionExtensions.cs | 52 +++++++ Oqtane.Client/Program.cs | 128 ++++++++---------- .../OqtaneServiceCollectionExtensions.cs | 1 - 3 files changed, 112 insertions(+), 69 deletions(-) create mode 100644 Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs diff --git a/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs new file mode 100644 index 00000000..e06b991e --- /dev/null +++ b/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs @@ -0,0 +1,52 @@ +using Microsoft.AspNetCore.Components.Authorization; +using Oqtane.Providers; +using Oqtane.Services; +using Oqtane.Shared; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class OqtaneServiceCollectionExtensions + { + public static IServiceCollection AddOqtaneAuthorization(this IServiceCollection services) + { + services.AddAuthorizationCore(); + services.AddScoped(); + services.AddScoped(s => s.GetRequiredService()); + + return services; + } + + internal static IServiceCollection AddOqtaneScopedServices(this IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + return services; + } + } +} diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs index fdc5145f..184ff75d 100644 --- a/Oqtane.Client/Program.cs +++ b/Oqtane.Client/Program.cs @@ -8,14 +8,12 @@ using System.Net.Http; using System.Reflection; using System.Runtime.Loader; using System.Threading.Tasks; -using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Localization; using Microsoft.Extensions.DependencyInjection; using Microsoft.JSInterop; using Oqtane.Interfaces; using Oqtane.Modules; -using Oqtane.Providers; using Oqtane.Services; using Oqtane.Shared; using Oqtane.UI; @@ -28,7 +26,8 @@ namespace Oqtane.Client { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("app"); - HttpClient httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)}; + + var httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)}; builder.Services.AddSingleton(httpClient); builder.Services.AddOptions(); @@ -37,38 +36,10 @@ namespace Oqtane.Client builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); // register auth services - builder.Services.AddAuthorizationCore(); - builder.Services.AddScoped(); - builder.Services.AddScoped(s => s.GetRequiredService()); + builder.Services.AddOqtaneAuthorization(); // register scoped core services - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); + builder.Services.AddOqtaneScopedServices(); await LoadClientAssemblies(httpClient); @@ -76,49 +47,18 @@ namespace Oqtane.Client foreach (var assembly in assemblies) { // dynamically register module services - var implementationTypes = assembly.GetInterfaces(); - foreach (var implementationType in implementationTypes) - { - if (implementationType.AssemblyQualifiedName != null) - { - var serviceType = Type.GetType(implementationType.AssemblyQualifiedName.Replace(implementationType.Name, $"I{implementationType.Name}")); - builder.Services.AddScoped(serviceType ?? implementationType, implementationType); - } - } + RegisterModuleServices(assembly, builder.Services); // dynamically register database providers - var databaseTypes = assembly.GetInterfaces(); - foreach (var databaseType in databaseTypes) - { - if (databaseType.AssemblyQualifiedName != null) - { - var serviceType = Type.GetType("Oqtane.Interfaces.IDatabase, Oqtane.Shared"); - builder.Services.AddScoped(serviceType ?? databaseType, databaseType); - } - } + RegisterDatabaseProviders(assembly, builder.Services); // register client startup services - var startUps = assembly.GetInstances(); - foreach (var startup in startUps) - { - startup.ConfigureServices(builder.Services); - } + RegisterClientStartups(assembly, builder.Services); } var host = builder.Build(); - var jsRuntime = host.Services.GetRequiredService(); - var interop = new Interop(jsRuntime); - var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName); - var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie).UICultures[0].Value; - var localizationService = host.Services.GetRequiredService(); - var cultures = await localizationService.GetCulturesAsync(); - if (culture == null || !cultures.Any(c => c.Name.Equals(culture, StringComparison.OrdinalIgnoreCase))) - { - culture = cultures.Single(c => c.IsDefault).Name; - } - - SetCulture(culture); + await SetCultureFromLocalizationCookie(host.Services); ServiceActivator.Configure(host.Services); @@ -174,6 +114,58 @@ namespace Oqtane.Client } } + private static void RegisterModuleServices(Assembly assembly, IServiceCollection services) + { + var implementationTypes = assembly.GetInterfaces(); + foreach (var implementationType in implementationTypes) + { + if (implementationType.AssemblyQualifiedName != null) + { + var serviceType = Type.GetType(implementationType.AssemblyQualifiedName.Replace(implementationType.Name, $"I{implementationType.Name}")); + services.AddScoped(serviceType ?? implementationType, implementationType); + } + } + } + + private static void RegisterDatabaseProviders(Assembly assembly, IServiceCollection services) + { + var databaseTypes = assembly.GetInterfaces(); + foreach (var databaseType in databaseTypes) + { + if (databaseType.AssemblyQualifiedName != null) + { + var serviceType = Type.GetType("Oqtane.Interfaces.IDatabase, Oqtane.Shared"); + services.AddScoped(serviceType ?? databaseType, databaseType); + } + } + } + + private static void RegisterClientStartups(Assembly assembly, IServiceCollection services) + { + var startUps = assembly.GetInstances(); + foreach (var startup in startUps) + { + startup.ConfigureServices(services); + } + } + + private static async Task SetCultureFromLocalizationCookie(IServiceProvider serviceProvider) + { + var jsRuntime = serviceProvider.GetRequiredService(); + var interop = new Interop(jsRuntime); + var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName); + var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie).UICultures[0].Value; + var localizationService = serviceProvider.GetRequiredService(); + var cultures = await localizationService.GetCulturesAsync(); + + if (culture == null || !cultures.Any(c => c.Name.Equals(culture, StringComparison.OrdinalIgnoreCase))) + { + culture = cultures.Single(c => c.IsDefault).Name; + } + + SetCulture(culture); + } + private static void SetCulture(string culture) { var cultureInfo = CultureInfo.GetCultureInfo(culture); diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index 432d01c3..6dd5186e 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -19,7 +19,6 @@ using Oqtane.Security; using Oqtane.Services; using Oqtane.Shared; -// ReSharper disable once CheckNamespace namespace Microsoft.Extensions.DependencyInjection { public static class OqtaneServiceCollectionExtensions From 97fb6ede7ea4abc587d340722094e9f236e5697b Mon Sep 17 00:00:00 2001 From: hishamco Date: Tue, 20 Apr 2021 19:10:06 +0300 Subject: [PATCH 4/5] Reuse AddOqtaneScopedServices() --- Oqtane.Client/AssemblyInfo.cs | 4 ++- .../OqtaneServiceCollectionExtensions.cs | 34 ------------------- Oqtane.Server/Startup.cs | 4 ++- 3 files changed, 6 insertions(+), 36 deletions(-) diff --git a/Oqtane.Client/AssemblyInfo.cs b/Oqtane.Client/AssemblyInfo.cs index d598bfb9..159ad127 100644 --- a/Oqtane.Client/AssemblyInfo.cs +++ b/Oqtane.Client/AssemblyInfo.cs @@ -1,3 +1,5 @@ -using Microsoft.Extensions.Localization; +using System.Runtime.CompilerServices; +using Microsoft.Extensions.Localization; [assembly: RootNamespace("Oqtane")] +[assembly: InternalsVisibleTo("Oqtane.Server")] diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index 6dd5186e..fa438356 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -57,40 +57,6 @@ namespace Microsoft.Extensions.DependencyInjection return services; } - internal static IServiceCollection AddOqtaneScopedServices(this IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - return services; - } - internal static IServiceCollection AddOqtaneSingletonServices(this IServiceCollection services) { services.AddSingleton(); diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 9793510c..6f86bcc4 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -65,7 +66,8 @@ namespace Oqtane services.AddOqtaneAuthorizationPolicies(); // register scoped core services - services.AddOqtaneScopedServices(); + services.AddScoped() + .AddOqtaneScopedServices(); services.AddSingleton(); From 7c181b65cd32b7a7003206a93267a8b22bc1342b Mon Sep 17 00:00:00 2001 From: hishamco Date: Sat, 12 Jun 2021 00:18:57 +0300 Subject: [PATCH 5/5] Fix merge conflict --- .../OqtaneServiceCollectionExtensions.cs | 2 ++ Oqtane.Client/Program.cs | 16 ---------------- .../OqtaneServiceCollectionExtensions.cs | 16 +++++++++++++--- Oqtane.Server/Startup.cs | 6 ++---- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs index e06b991e..40db5da5 100644 --- a/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Client/Extensions/OqtaneServiceCollectionExtensions.cs @@ -45,6 +45,8 @@ namespace Microsoft.Extensions.DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); return services; } diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs index cbb1e60c..ff27f5c0 100644 --- a/Oqtane.Client/Program.cs +++ b/Oqtane.Client/Program.cs @@ -48,9 +48,6 @@ namespace Oqtane.Client // dynamically register module services RegisterModuleServices(assembly, builder.Services); - // dynamically register database providers - RegisterDatabaseProviders(assembly, builder.Services); - // register client startup services RegisterClientStartups(assembly, builder.Services); } @@ -126,19 +123,6 @@ namespace Oqtane.Client } } - private static void RegisterDatabaseProviders(Assembly assembly, IServiceCollection services) - { - var databaseTypes = assembly.GetInterfaces(); - foreach (var databaseType in databaseTypes) - { - if (databaseType.AssemblyQualifiedName != null) - { - var serviceType = Type.GetType("Oqtane.Interfaces.IDatabase, Oqtane.Shared"); - services.AddScoped(serviceType ?? databaseType, databaseType); - } - } - } - private static void RegisterClientStartups(Assembly assembly, IServiceCollection services) { var startUps = assembly.GetInstances(); diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index 92bcb688..d939110b 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using System.Net; using System.Net.Http; using System.Reflection; using System.Runtime.Loader; @@ -34,7 +35,6 @@ namespace Microsoft.Extensions.DependencyInjection public static IServiceCollection AddOqtaneDbContext(this IServiceCollection services) { - services.AddScoped(); services.AddDbContext(options => { }); services.AddDbContext(options => { }); @@ -62,6 +62,7 @@ namespace Microsoft.Extensions.DependencyInjection services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); return services; } @@ -96,6 +97,8 @@ namespace Microsoft.Extensions.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + // obsolete - replaced by ITenantManager + services.AddTransient(); return services; } @@ -105,12 +108,19 @@ namespace Microsoft.Extensions.DependencyInjection services.ConfigureApplicationCookie(options => { options.Cookie.HttpOnly = false; + options.Cookie.SameSite = SameSiteMode.Strict; + options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; options.Events.OnRedirectToLogin = context => { - context.Response.StatusCode = 401; - + context.Response.StatusCode = (int)HttpStatusCode.Forbidden; return Task.CompletedTask; }; + options.Events.OnRedirectToAccessDenied = context => + { + context.Response.StatusCode = (int)HttpStatusCode.Forbidden; + return Task.CompletedTask; + }; + options.Events.OnValidatePrincipal = PrincipalValidator.ValidateAsync; }); return services; diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index fec5fa6e..d1fa176f 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -1,10 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -56,6 +52,8 @@ namespace Oqtane // Register localization services services.AddLocalization(options => options.ResourcesPath = "Resources"); + services.AddOptions>().Bind(Configuration.GetSection(SettingKeys.AvailableDatabasesSection)); + services.AddServerSideBlazor() .AddCircuitOptions(options => {