using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.ResponseCompression; // needed for WASM using Microsoft.Extensions.DependencyInjection; using System.Linq; using Microsoft.Extensions.Configuration; using Microsoft.EntityFrameworkCore; using Microsoft.AspNetCore.Http; using System; using System.Reflection; using Microsoft.Extensions.Hosting; using Oqtane.Modules; using Oqtane.Repository; using System.IO; using System.Runtime.Loader; using Oqtane.Services; using System.Net.Http; using Microsoft.AspNetCore.Components; using Oqtane.Shared; using Microsoft.AspNetCore.Identity; using System.Threading.Tasks; using System.Collections.Generic; using Microsoft.OpenApi.Models; using Oqtane.Security; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication; using System.Net; using Microsoft.AspNetCore.Authorization; namespace Oqtane.Server { public class Startup { public IConfigurationRoot Configuration { get; } public Startup(IWebHostEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); Configuration = builder.Build(); AppDomain.CurrentDomain.SetData("DataDirectory", Path.Combine(env.ContentRootPath, "Data")); } #if DEBUG || RELEASE // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddServerSideBlazor(); // 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.ToAbsoluteUri(NavigationManager.Uri).AbsoluteUri); return client; }); } // register authorization services services.AddAuthorizationCore(options => { options.AddPolicy("ViewPage", policy => policy.Requirements.Add(new PermissionRequirement("Page", "View"))); options.AddPolicy("EditPage", policy => policy.Requirements.Add(new PermissionRequirement("Page", "Edit"))); options.AddPolicy("ViewModule", policy => policy.Requirements.Add(new PermissionRequirement("Module", "View"))); options.AddPolicy("EditModule", policy => policy.Requirements.Add(new PermissionRequirement("Module", "Edit"))); }); services.AddScoped(); // 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.AddSingleton(); services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection") .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString()) )); services.AddDbContext(options => { }); 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.AddAuthentication(IdentityConstants.ApplicationScheme) .AddCookie(IdentityConstants.ApplicationScheme); services.ConfigureApplicationCookie(options => { options.Cookie.HttpOnly = false; options.Events.OnRedirectToLogin = context => { context.Response.StatusCode = 401; return Task.CompletedTask; }; }); // register custom claims principal factory for role claims services.AddTransient, ClaimsPrincipalFactory>(); // get list of loaded assemblies Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); // iterate through Oqtane module assemblies in /bin ( filter is narrow to optimize loading process ) string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); DirectoryInfo folder = new DirectoryInfo(path); List moduleassemblies = new List(); foreach (FileInfo file in folder.EnumerateFiles("*.Module.*.dll")) { // check if assembly is already loaded Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault(); if (assembly == null) { // load assembly ( as long as dependencies are in /bin they will load as well ) assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName); moduleassemblies.Add(assembly); } } services.AddMvc().AddModuleAssemblies(moduleassemblies).AddNewtonsoftJson(); // register singleton scoped core services services.AddSingleton(Configuration); services.AddSingleton(); services.AddSingleton(); // 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(); // dynamically register module services, contexts, and repository classes assemblies = AppDomain.CurrentDomain.GetAssemblies() .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray(); foreach (Assembly assembly in assemblies) { Type[] implementationtypes = assembly.GetTypes() .Where(item => item.GetInterfaces().Contains(typeof(IService))) .ToArray(); foreach (Type implementationtype in implementationtypes) { Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name)); if (servicetype != null) { services.AddScoped(servicetype, implementationtype); // traditional service interface } else { services.AddScoped(implementationtype, implementationtype); // no interface defined for service } } } services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Oqtane", Version = "v1" }); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Oqtane V1"); }); app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); endpoints.MapControllers(); endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host"); }); } #endif #if WASM // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddSingleton(); services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection") .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString()) )); services.AddDbContext(options => { }); 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; }); // register authorization services services.AddAuthorizationCore(options => { options.AddPolicy("ViewPage", policy => policy.Requirements.Add(new PermissionRequirement("Page", "View"))); options.AddPolicy("EditPage", policy => policy.Requirements.Add(new PermissionRequirement("Page", "Edit"))); options.AddPolicy("ViewModule", policy => policy.Requirements.Add(new PermissionRequirement("Module", "View"))); options.AddPolicy("EditModule", policy => policy.Requirements.Add(new PermissionRequirement("Module", "Edit"))); }); services.AddScoped(); services.ConfigureApplicationCookie(options => { options.Cookie.HttpOnly = false; options.Events.OnRedirectToLogin = context => { context.Response.StatusCode = 401; return Task.CompletedTask; }; }); // register custom claims principal factory for role claims services.AddTransient, ClaimsPrincipalFactory>(); // get list of loaded assemblies Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); // iterate through Oqtane module assemblies in /bin ( filter is narrow to optimize loading process ) string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); DirectoryInfo folder = new DirectoryInfo(path); List moduleassemblies = new List(); foreach (FileInfo file in folder.EnumerateFiles("*.Module.*.dll")) { // check if assembly is already loaded Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault(); if (assembly == null) { // load assembly ( as long as dependencies are in /bin they will load as well ) assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName); moduleassemblies.Add(assembly); } } services.AddMvc().AddModuleAssemblies(moduleassemblies).AddNewtonsoftJson(); // register singleton scoped core services services.AddSingleton(Configuration); services.AddSingleton(); services.AddSingleton(); // 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(); // dynamically register module services, contexts, and repository classes assemblies = AppDomain.CurrentDomain.GetAssemblies() .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray(); foreach (Assembly assembly in assemblies) { Type[] implementationtypes = assembly.GetTypes() .Where(item => item.GetInterfaces().Contains(typeof(IService))) .ToArray(); foreach (Type implementationtype in implementationtypes) { Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name)); if (servicetype != null) { services.AddScoped(servicetype, implementationtype); // traditional service interface } else { services.AddScoped(implementationtype, implementationtype); // no interface defined for service } } } services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Oqtane", Version = "v1" }); }); services.AddResponseCompression(opts => { opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat( new[] { "application/octet-stream" }); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseResponseCompression(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBlazorDebugging(); } app.UseClientSideBlazorFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Oqtane V1"); }); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); endpoints.MapFallbackToClientSideBlazor("index.html"); }); } #endif } }