// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // Add Singletons services.AddSingleton <IEnvironmentInformationProvider>(new EnvironmentInformationProvider(RuntimeEnvironment)); // Ensure nothing non-secure passes to any controller if (!RuntimeEnvironment.IsDevelopment() && !RuntimeEnvironment.EnvironmentName.Equals("Local", StringComparison.InvariantCultureIgnoreCase) && UseHsts()) { services.Configure <MvcOptions>(o => o.Filters.Add(new RequireHttpsAttribute())); // TODO: We need to register codeswifterstarter.com to the list of hsts preloaded domains https://hstspreload.org/ services.AddHsts(o => { o.MaxAge = TimeSpan.FromDays(365); o.Preload = true; o.IncludeSubDomains = true; }); } if (!RuntimeEnvironment.IsDevelopment() && !RuntimeEnvironment.EnvironmentName.Equals("Local", StringComparison.InvariantCultureIgnoreCase) && UseSSL()) { services.AddHttpsRedirection(options => { options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect; }); } services.AddHttpContextAccessor(); services.AddScoped <HttpContextAccessor>(); _serverConfiguration = ConfigurationHelper <ServerConfiguration> .GetConfigurationFromJson(RuntimeEnvironment); services.AddSingleton(_serverConfiguration); // Add AutoMapper services.AddSingleton <IMapper>(new Mapper(new MapperConfiguration(cfg => { cfg.AddProfile <AutoMapperProfile>(); }))); // Add framework services. services.AddTransient <INotificationService, NotificationService>(); services.AddTransient <IFileStorageService, FileStorageService>(); services.AddTransient <ICrudWarningService, CrudWarningService>(); services.AddTransient <IDateTime, MachineDateTime>(); // Register AuthenticatedUserService service services.AddScoped <IAuthenticatedUserService, AuthenticatedUserService>(); services.AddScoped <IAuthManagementService, AuthManagementService>(); // Register all query managers services.AddScopedForClassesEndingWith(typeof(AutoMapperProfile).GetTypeInfo().Assembly, "QueryManager"); // Add MediatR services.AddTransient(typeof(IPipelineBehavior <,>), typeof(RequestPreProcessorBehavior <,>)); services.AddTransient(typeof(IPipelineBehavior <,>), typeof(RequestPerformanceBehaviour <,>)); services.AddTransient(typeof(IPipelineBehavior <,>), typeof(RequestValidationBehavior <,>)); services.AddMediatR(typeof(AutoMapperProfile).GetTypeInfo().Assembly); // Add database context services.AddDbContext <CodeSwifterStarterDbContext>(_serverConfiguration.ConnectionStrings .CodeSwifterStarterDatabase); services.AddScoped(service => (ICodeSwifterStarterDbContext)service.GetService(typeof(CodeSwifterStarterDbContext))); // Add seeder services.AddScoped <CodeSwifterStarterDbSeeder>(); // Add support for proper validation return messages services .AddMvc(options => options.Filters.Add(typeof(CustomExceptionFilterAttribute))) .AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore) .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining <AutoMapperProfile>()); services.AddDataProtection(o => { o.ApplicationDiscriminator = "CodeSwifterStarter"; }); services.Configure <BrotliCompressionProviderOptions> (options => options.Level = CompressionLevel.Fastest); services.Configure <GzipCompressionProviderOptions> (options => options.Level = CompressionLevel.Fastest); services.AddResponseCompression(options => { options.EnableForHttps = true; options.Providers.Add <BrotliCompressionProvider>(); options.Providers.Add <GzipCompressionProvider>(); options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "image/svg+xml", "image/jpeg", "image/png", "text/html", "application/javascript", "application/json", "text/json", "text/css" }); }); services.AddControllers(); if (!RuntimeEnvironment.IsDevelopment() && !RuntimeEnvironment.EnvironmentName.Equals("Local", StringComparison.InvariantCultureIgnoreCase)) { services.Configure <ForwardedHeadersOptions>(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.ForwardLimit = 2; }); } services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/dist"; }); services .AddMvc(options => { options.Filters.Add(typeof(CustomExceptionFilterAttribute)); }) .AddNewtonsoftJson() .SetCompatibilityVersion(CompatibilityVersion.Version_3_0); // Add authentication services services.Configure <CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => RuntimeEnvironment.IsProduction() || RuntimeEnvironment.IsStaging(); options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.Authority = _serverConfiguration.SecurityProvider.Authority; options.Audience = _serverConfiguration.SecurityProvider.SingleSignOn.Api.Audience; }); var authDomain = _serverConfiguration.SecurityProvider.Authority; services.AddAuthorization(options => { foreach (var securityPolicy in SecurityPoliciesFactory.Policies) { options.AddPolicy(securityPolicy.Name, policy => { foreach (var permission in securityPolicy.Permissions) { policy.Requirements.Add(new PermissionRequirement(permission.Name, authDomain)); } }); } }); // register the scope authorization handler services.AddScoped <IAuthorizationHandler, PermissionHandler>(); // Consider making this publicly available if (RuntimeEnvironment.IsDevelopment() || RuntimeEnvironment.EnvironmentName.Equals("Local", StringComparison.InvariantCultureIgnoreCase)) { services.AddSwaggerDocument(settings => { settings.Title = "CodeSwifterStarter API"; }); } // Customise default API behavour services.Configure <ApiBehaviorOptions>(options => { if (RuntimeEnvironment.IsProduction() || RuntimeEnvironment.IsStaging()) { options.SuppressModelStateInvalidFilter = true; } }); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app) { if (RuntimeEnvironment.IsDevelopment() || RuntimeEnvironment.EnvironmentName.Equals("Local", StringComparison.InvariantCultureIgnoreCase)) { app.UseDeveloperExceptionPage(); } else { app.UseForwardedHeaders(); app.UseExceptionHandler("/Error"); if (UseHsts()) { Console.WriteLine("Using HSTS"); HstsBuilderExtensions.UseHsts(app); } if (UseSSL()) { Console.WriteLine("Using SSL"); app.UseHttpsRedirection(); } } // Add request logging to be able to identify attacks app.Use(async(ctx, next) => { if (ctx?.Connection?.RemoteIpAddress != null) { using (LogContext.PushProperty("IPAddress", ctx.Connection.RemoteIpAddress)) { await next(); } } }); app.UseSerilogRequestLogging(); // Hacker prevention app.UseCsp(csp => { if (RuntimeEnvironment.IsDevelopment() || RuntimeEnvironment.EnvironmentName.Equals("Local", StringComparison.InvariantCultureIgnoreCase)) { csp.AllowScripts .FromSelf() .From("http://localhost:4200") .From("https://localhost:6220") .From("http://localhost:6221") .From("https://www.googletagmanager.com") .AllowUnsafeInline() .AllowUnsafeEval(); csp.AllowStyles .FromSelf() .From("http://localhost:4200") .From("https://localhost:6220") .From("http://localhost:6221") .From("https://fonts.googleapis.com") .AllowUnsafeInline(); csp.AllowImages .FromSelf() .From("data:") .From("http://localhost:4200") .From("https://localhost:6220") .From("http://localhost:6221"); csp.AllowFonts.FromAnywhere(); } else { csp.AllowScripts .FromSelf() .From("https://www.googletagmanager.com") .AllowUnsafeInline() .AllowUnsafeEval(); csp.AllowStyles .FromSelf() .From("https://fonts.googleapis.com") .AllowUnsafeInline(); csp.AllowImages .FromSelf() .From("data:"); csp.AllowFonts .FromAnywhere(); } }) .UseXFrameOptions(new XFrameOptionsOptions(XFrameOptionsOptions.XFrameOptionsValues.Deny)) .UseReferrerPolicy(new ReferrerPolicyOptions(ReferrerPolicyOptions.ReferrerPolicyValue.NoReferrer)) .UseXXssProtection(new XXssProtectionOptions(true, true)) .UseXContentTypeOptions(new XContentTypeOptionsOptions(false)); app.UseResponseCompression(); app.UseStaticFiles(); if (!RuntimeEnvironment.IsDevelopment() && !RuntimeEnvironment.EnvironmentName.Equals("Local", StringComparison.InvariantCultureIgnoreCase)) { app.UseSpaStaticFiles(); } else { app.UseCors(c => c.WithOrigins("http://localhost:4200", "https://localhost:6220", "http://localhost:6221")); } // Consider making this publicly available if (RuntimeEnvironment.IsDevelopment() || RuntimeEnvironment.EnvironmentName.Equals("Local", StringComparison.InvariantCultureIgnoreCase)) { app.UseOpenApi(); // Access Swagger UI using https://localhost:6220/swagger/v1/swagger.json app.UseSwaggerUi3(s => { s.DocumentTitle = "CodeSwifterStarter API"; }); } if (RuntimeEnvironment.IsDevelopment()) { Console.WriteLine("Environment: " + RuntimeEnvironment.EnvironmentName); } app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(config => { config.MapControllerRoute( "api", "api/[controller]/{action}/{id?}"); config.MapControllerRoute( "auth", "auth/[controller]/{action}/{id?}"); }); if (!RuntimeEnvironment.IsDevelopment() && !RuntimeEnvironment.EnvironmentName.Equals("Local", StringComparison.InvariantCultureIgnoreCase)) { app.UseRootRewrite(); app.UseSpa(config => { }); } }