From e901aafc5c194adaac619ad6e6c2bccb7eac0d2e Mon Sep 17 00:00:00 2001 From: rafsanulhasan Date: Sat, 1 Sep 2018 02:29:19 +0600 Subject: [PATCH] 1. Configured CookiePolicyOptions to the DI Container (Line 39) and used CookiePolicy MiddleWare (line 76) in order to support GDPR Cookie compliance 2. Set compatibility version to 2.1 according to documentation (line 172) 3. HTTPS Support a. Updated MVC pipeline to support HTTPS and specified port (Line 166-171) b. Configure HTTPS redirection options to the DI (line 51) and used HttpsRedirection MiddleWare (Line 75) c. 4. Used var instead of type (Line 100, 123, 145, 250-252) --- src/Web/WebMVC/Startup.cs | 430 ++++++++++++++++++++------------------ 1 file changed, 225 insertions(+), 205 deletions(-) diff --git a/src/Web/WebMVC/Startup.cs b/src/Web/WebMVC/Startup.cs index 9c1c0a3b8..563431322 100644 --- a/src/Web/WebMVC/Startup.cs +++ b/src/Web/WebMVC/Startup.cs @@ -24,258 +24,278 @@ using WebMVC.Services; namespace Microsoft.eShopOnContainers.WebMVC { - public class Startup - { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the IoC container. - public void ConfigureServices(IServiceCollection services) - { - services.AddAppInsight(Configuration) - .AddHealthChecks(Configuration) - .AddCustomMvc(Configuration) - .AddHttpClientServices(Configuration) - //.AddHttpClientLogging(Configuration) //Opt-in HttpClientLogging config - .AddCustomAuthentication(Configuration); - } + // This method gets called by the runtime. Use this method to add services to the IoC container. + public void ConfigureServices(IServiceCollection services) + { + services.Configure(opts => + { + opts.MinimumSameSitePolicy = SameSiteMode.None; + opts.CheckConsentNeeded = context => true; + }); + services + .AddAppInsight(Configuration) + .AddHealthChecks(Configuration) + .AddCustomMvc(Configuration) + .AddHttpClientServices(Configuration) + //.AddHttpClientLogging(Configuration) //Opt-in HttpClientLogging config + .AddCustomAuthentication(Configuration); + services.AddHttpsRedirection(options => + { + options.HttpsPort = 4100; + }); + } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) - { - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); - loggerFactory.AddAzureWebAppDiagnostics(); - loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + loggerFactory.AddAzureWebAppDiagnostics(); + loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Error"); - } + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseExceptionHandler("/Error"); + app.UseHsts(); + } - var pathBase = Configuration["PATH_BASE"]; - if (!string.IsNullOrEmpty(pathBase)) - { - loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); - app.UsePathBase(pathBase); - } + app.UseHttpsRedirection(); + app.UseCookiePolicy(); + + string pathBase = Configuration["PATH_BASE"]; + if (!string.IsNullOrEmpty(pathBase)) + { + loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); + app.UsePathBase(pathBase); + } #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously - app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200)); + app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200)); #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously - app.UseSession(); - app.UseStaticFiles(); + app.UseSession(); + app.UseStaticFiles(); - if (Configuration.GetValue("UseLoadTest")) - { - app.UseMiddleware(); - } + if (Configuration.GetValue("UseLoadTest")) + { + app.UseMiddleware(); + } - app.UseAuthentication(); + app.UseAuthentication(); - var log = loggerFactory.CreateLogger("identity"); + ILogger log = loggerFactory.CreateLogger("identity"); - WebContextSeed.Seed(app, env, loggerFactory); + WebContextSeed.Seed(app, env, loggerFactory); - app.UseMvc(routes => - { - routes.MapRoute( - name: "default", - template: "{controller=Catalog}/{action=Index}/{id?}"); + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Catalog}/{action=Index}/{id?}"); - routes.MapRoute( - name: "defaultError", - template: "{controller=Error}/{action=Error}"); - }); - } - } + routes.MapRoute( + name: "defaultError", + template: "{controller=Error}/{action=Error}"); + }); + } + } - static class ServiceCollectionExtensions - { + static class ServiceCollectionExtensions + { - public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) - { - services.AddApplicationInsightsTelemetry(configuration); - var orchestratorType = configuration.GetValue("OrchestratorType"); + public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) + { + services.AddApplicationInsightsTelemetry(configuration); + string orchestratorType = configuration.GetValue("OrchestratorType"); - if (orchestratorType?.ToUpper() == "K8S") - { - // Enable K8s telemetry initializer - services.EnableKubernetes(); - } + if (orchestratorType?.ToUpper() == "K8S") + { + // Enable K8s telemetry initializer + services.EnableKubernetes(); + } - if (orchestratorType?.ToUpper() == "SF") - { - // Enable SF telemetry initializer - services.AddSingleton((serviceProvider) => - new FabricTelemetryInitializer()); - } + if (orchestratorType?.ToUpper() == "SF") + { + // Enable SF telemetry initializer + services.AddSingleton((serviceProvider) => + new FabricTelemetryInitializer()); + } - return services; - } + return services; + } - public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) - { - services.AddHealthChecks(checks => - { - var minutes = 1; - if (int.TryParse(configuration["HealthCheck:Timeout"], out var minutesParsed)) - { - minutes = minutesParsed; - } + public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) + { + services.AddHealthChecks(checks => + { + int minutes = 1; + if (int.TryParse(configuration["HealthCheck:Timeout"], out int minutesParsed)) + { + minutes = minutesParsed; + } - checks.AddUrlCheck(configuration["CatalogUrlHC"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheck(configuration["OrderingUrlHC"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheck(configuration["BasketUrlHC"], TimeSpan.Zero); //No cache for this HealthCheck, better just for demos - checks.AddUrlCheck(configuration["IdentityUrlHC"], TimeSpan.FromMinutes(minutes)); - checks.AddUrlCheck(configuration["MarketingUrlHC"], TimeSpan.FromMinutes(minutes)); - }); + checks.AddUrlCheck(configuration["CatalogUrlHC"], TimeSpan.FromMinutes(minutes)); + checks.AddUrlCheck(configuration["OrderingUrlHC"], TimeSpan.FromMinutes(minutes)); + checks.AddUrlCheck(configuration["BasketUrlHC"], TimeSpan.Zero); //No cache for this HealthCheck, better just for demos + checks.AddUrlCheck(configuration["IdentityUrlHC"], TimeSpan.FromMinutes(minutes)); + checks.AddUrlCheck(configuration["MarketingUrlHC"], TimeSpan.FromMinutes(minutes)); + }); - return services; - } + return services; + } - public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration) - { - services.AddOptions(); - services.Configure(configuration); + public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration) + { + services.AddOptions(); + services.Configure(configuration); - services.AddMvc(); + services + .AddMvc(opts => + { + opts.SslPort = 4100; + opts.RequireHttpsPermanent = true; + }) + .SetCompatibilityVersion(AspNetCore.Mvc.CompatibilityVersion.Version_2_1); - services.AddSession(); + services.AddSession(); - if (configuration.GetValue("IsClusterEnv") == bool.TrueString) - { - services.AddDataProtection(opts => - { - opts.ApplicationDiscriminator = "eshop.webmvc"; - }) - .PersistKeysToRedis(ConnectionMultiplexer.Connect(configuration["DPConnectionString"]), "DataProtection-Keys"); - } - return services; - } + if (configuration.GetValue("IsClusterEnv") == bool.TrueString) + { + services.AddDataProtection(opts => + { + opts.ApplicationDiscriminator = "eshop.webmvc"; + }) + .PersistKeysToRedis(ConnectionMultiplexer.Connect(configuration["DPConnectionString"]), "DataProtection-Keys"); + } + return services; + } - // Adds all Http client services (like Service-Agents) using resilient Http requests based on HttpClient factory and Polly's policies - public static IServiceCollection AddHttpClientServices(this IServiceCollection services, IConfiguration configuration) - { - services.AddSingleton(); + // Adds all Http client services (like Service-Agents) using resilient Http requests based on HttpClient factory and Polly's policies + public static IServiceCollection AddHttpClientServices(this IServiceCollection services, IConfiguration configuration) + { + services.AddSingleton(); - //register delegating handlers - services.AddTransient(); - services.AddTransient(); + //register delegating handlers + services.AddTransient(); + services.AddTransient(); - //set 5 min as the lifetime for each HttpMessageHandler int the pool - services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(TimeSpan.FromMinutes(5)); + //set 5 min as the lifetime for each HttpMessageHandler int the pool + services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(TimeSpan.FromMinutes(5)); - //add http client services - services.AddHttpClient() - .SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Sample. Default lifetime is 2 minutes - .AddHttpMessageHandler() - .AddPolicyHandler(GetRetryPolicy()) - .AddPolicyHandler(GetCircuitBreakerPolicy()); + //add http client services + services.AddHttpClient() + .SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Sample. Default lifetime is 2 minutes + .AddHttpMessageHandler() + .AddPolicyHandler(GetRetryPolicy()) + .AddPolicyHandler(GetCircuitBreakerPolicy()); - services.AddHttpClient() - .AddPolicyHandler(GetRetryPolicy()) - .AddPolicyHandler(GetCircuitBreakerPolicy()); + services.AddHttpClient() + .AddPolicyHandler(GetRetryPolicy()) + .AddPolicyHandler(GetCircuitBreakerPolicy()); - services.AddHttpClient() - .AddHttpMessageHandler() - .AddHttpMessageHandler() - .AddPolicyHandler(GetRetryPolicy()) - .AddPolicyHandler(GetCircuitBreakerPolicy()); + services.AddHttpClient() + .AddHttpMessageHandler() + .AddHttpMessageHandler() + .AddPolicyHandler(GetRetryPolicy()) + .AddPolicyHandler(GetCircuitBreakerPolicy()); - services.AddHttpClient() - .AddHttpMessageHandler() - .AddPolicyHandler(GetRetryPolicy()) - .AddPolicyHandler(GetCircuitBreakerPolicy()); + services.AddHttpClient() + .AddHttpMessageHandler() + .AddPolicyHandler(GetRetryPolicy()) + .AddPolicyHandler(GetCircuitBreakerPolicy()); - services.AddHttpClient() - .AddHttpMessageHandler() - .AddPolicyHandler(GetRetryPolicy()) - .AddPolicyHandler(GetCircuitBreakerPolicy()); + services.AddHttpClient() + .AddHttpMessageHandler() + .AddPolicyHandler(GetRetryPolicy()) + .AddPolicyHandler(GetCircuitBreakerPolicy()); - //add custom application services - services.AddTransient, IdentityParser>(); + //add custom application services + services.AddTransient, IdentityParser>(); - return services; - } + return services; + } - public static IServiceCollection AddHttpClientLogging(this IServiceCollection services, IConfiguration configuration) - { - services.AddLogging(b => - { - b.AddFilter((category, level) => true); // Spam the world with logs. + public static IServiceCollection AddHttpClientLogging(this IServiceCollection services, IConfiguration configuration) + { + services.AddLogging(b => + { + b.AddFilter((category, level) => true); // Spam the world with logs. - // Add console logger so we can see all the logging produced by the client by default. - b.AddConsole(c => c.IncludeScopes = true); + // Add console logger so we can see all the logging produced by the client by default. + b.AddConsole(c => c.IncludeScopes = true); - // Add console logger - b.AddDebug(); - }); + // Add console logger + b.AddDebug(); + }); - return services; - } + return services; + } - public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) - { - var useLoadTest = configuration.GetValue("UseLoadTest"); - var identityUrl = configuration.GetValue("IdentityUrl"); - var callBackUrl = configuration.GetValue("CallBackUrl"); + public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) + { + bool useLoadTest = configuration.GetValue("UseLoadTest"); + string identityUrl = configuration.GetValue("IdentityUrl"); + string callBackUrl = configuration.GetValue("CallBackUrl"); - // Add Authentication services + // Add Authentication services - services.AddAuthentication(options => - { - options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; - }) - .AddCookie() - .AddOpenIdConnect(options => - { - options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; - options.Authority = identityUrl.ToString(); - options.SignedOutRedirectUri = callBackUrl.ToString(); - options.ClientId = useLoadTest ? "mvctest" : "mvc"; - options.ClientSecret = "secret"; - options.ResponseType = useLoadTest ? "code id_token token" : "code id_token"; - options.SaveTokens = true; - options.GetClaimsFromUserInfoEndpoint = true; - options.RequireHttpsMetadata = false; - options.Scope.Add("openid"); - options.Scope.Add("profile"); - options.Scope.Add("orders"); - options.Scope.Add("basket"); - options.Scope.Add("marketing"); - options.Scope.Add("locations"); - options.Scope.Add("webshoppingagg"); - options.Scope.Add("orders.signalrhub"); - }); + services.AddAuthentication(options => + { + options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; + }) + .AddCookie() + .AddOpenIdConnect(options => + { + options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.Authority = identityUrl.ToString(); + options.SignedOutRedirectUri = callBackUrl.ToString(); + options.ClientId = useLoadTest ? "mvctest" : "mvc"; + options.ClientSecret = "secret"; + options.ResponseType = useLoadTest ? "code id_token token" : "code id_token"; + options.SaveTokens = true; + options.GetClaimsFromUserInfoEndpoint = true; + options.RequireHttpsMetadata = false; + options.Scope.Add("openid"); + options.Scope.Add("profile"); + options.Scope.Add("orders"); + options.Scope.Add("basket"); + options.Scope.Add("marketing"); + options.Scope.Add("locations"); + options.Scope.Add("webshoppingagg"); + options.Scope.Add("orders.signalrhub"); + }); - return services; - } + return services; + } - static IAsyncPolicy GetRetryPolicy() - { - return HttpPolicyExtensions - .HandleTransientHttpError() - .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound) - .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); + static IAsyncPolicy GetRetryPolicy() + { + return HttpPolicyExtensions + .HandleTransientHttpError() + .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound) + .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); - } - static IAsyncPolicy GetCircuitBreakerPolicy() - { - return HttpPolicyExtensions - .HandleTransientHttpError() - .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)); - } - } + } + static IAsyncPolicy GetCircuitBreakerPolicy() + { + return HttpPolicyExtensions + .HandleTransientHttpError() + .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)); + } + } }