using System.Globalization; using System.Reflection; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.Net.Http.Headers; using Newtonsoft.Json; using Google.Apis.Util.Store; using Microsoft.Extensions.Localization; using Yavsc.Helpers; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using Yavsc.Models; using Yavsc.Services; using Microsoft.IdentityModel.Tokens; using Microsoft.AspNetCore.Identity; using Yavsc.Interface; using Yavsc.Settings; using Microsoft.AspNetCore.DataProtection; namespace Yavsc { public partial class Startup { public static string AvatarsDirName { private set; get; } public static string GitDirName { private set; get; } public static string Authority { get; private set; } public static string Temp { get; set; } public static SiteSettings SiteSetup { get; private set; } public SmtpSettings SmtpSetup { get; private set; } public static GoogleServiceAccount GServiceAccount { get; private set; } public static string HostingFullName { get; set; } public static PayPalSettings PayPalSettings { get; private set; } private static ILogger _logger; public Startup(IWebHostEnvironment env) { AppDomain.CurrentDomain.UnhandledException += OnUnHandledException; string devtag = env.IsDevelopment() ? "D" : ""; string prodtag = env.IsProduction() ? "P" : ""; string stagetag = env.IsStaging() ? "S" : ""; HostingFullName = $"{env.EnvironmentName} [{prodtag}/{devtag}/{stagetag}]"; // Set up configuration sources. IConfigurationBuilder builder = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("appsettings.json") .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); _ = builder.AddEnvironmentVariables(); Configuration = builder.Build(); ConnectionString = Configuration["ConnectionStrings:Default"]; AppDomain.CurrentDomain.SetData(Constants.YavscConnectionStringEnvName, ConnectionString); string? googleClientFile = Configuration["Authentication:Google:GoogleWebClientJson"]; string? googleServiceAccountJsonFile = Configuration["Authentication:Google:GoogleServiceAccountJson"]; if (googleClientFile != null) { GoogleWebClientConfiguration = new ConfigurationBuilder().AddJsonFile(googleClientFile).Build(); } if (googleServiceAccountJsonFile != null) { FileInfo safile = new FileInfo(googleServiceAccountJsonFile); GServiceAccount = JsonConvert.DeserializeObject(safile.OpenText().ReadToEnd()); } } // never hit ... private void OnUnHandledException(object sender, UnhandledExceptionEventArgs e) { _logger.LogError(sender.ToString()); _logger.LogError(JsonConvert.SerializeObject(e.ExceptionObject)); } public static string ConnectionString { get; set; } public static GoogleAuthSettings GoogleSettings { get; set; } public IConfigurationRoot Configuration { get; set; } public static IConfigurationRoot GoogleWebClientConfiguration { get; set; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { IConfigurationSection siteSettings = Configuration.GetSection("Site"); _ = services.Configure(siteSettings); IConfigurationSection smtpSettings = Configuration.GetSection("Smtp"); _ = services.Configure(smtpSettings); IConfigurationSection protectionSettings = Configuration.GetSection("DataProtection"); _ = services.Configure(smtpSettings); IConfigurationSection googleSettings = Configuration.GetSection("Authentication").GetSection("Google"); _ = services.Configure(googleSettings); IConfigurationSection cinfoSettings = Configuration.GetSection("Authentication").GetSection("Societeinfo"); _ = services.Configure(cinfoSettings); IConfigurationSection oauthFacebookSettings = Configuration.GetSection("Authentication").GetSection("Facebook"); _ = services.Configure(oauthFacebookSettings); IConfigurationSection paypalSettings = Configuration.GetSection("Authentication").GetSection("PayPal"); _ = services.Configure(paypalSettings); services.Add(ServiceDescriptor.Singleton(typeof(IOptions), typeof(OptionsManager))); services.Add(ServiceDescriptor.Singleton(typeof(IOptions), typeof(OptionsManager))); services.Add(ServiceDescriptor.Singleton(typeof(IOptions), typeof(OptionsManager))); services.Add(ServiceDescriptor.Singleton(typeof(IOptions), typeof(OptionsManager))); services.Add(ServiceDescriptor.Singleton(typeof(IOptions), typeof(OptionsManager))); services.Add(ServiceDescriptor.Singleton(typeof(IDiskUsageTracker), typeof(DiskUsageTracker))); _ = services.Configure(options => { CultureInfo[] supportedCultures = new[] { new CultureInfo("en"), new CultureInfo("fr"), new CultureInfo("pt") }; CultureInfo[] supportedUICultures = new[] { new CultureInfo("fr"), new CultureInfo("en"), new CultureInfo("pt") }; // You must explicitly state which cultures your application supports. // These are the cultures the app supports for formatting numbers, dates, etc. options.SupportedCultures = supportedCultures; // These are the cultures the app supports for UI strings, i.e. we have localized resources for. options.SupportedUICultures = supportedUICultures; options.RequestCultureProviders = new List { new QueryStringRequestCultureProvider { Options = options }, new CookieRequestCultureProvider { Options = options, CookieName="ASPNET_CULTURE" }, new AcceptLanguageHeaderRequestCultureProvider { Options = options } }; }); services.AddSignalR(); services.AddOptions(); _ = services.AddCors(options => { options.AddPolicy("CorsPolicy", builder => { _ = builder.WithOrigins("*"); }); }); // Add the system clock service _ = services.AddSingleton(); _ = services.AddSingleton(); _ = services.AddSingleton(); _ = services.AddSingleton(); // Add framework services. /*services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton();*/ _ = services.AddDbContext() .AddIdentity() .AddRoles() .AddEntityFrameworkStores() .AddSignInManager() .AddDefaultUI() .AddDefaultTokenProviders(); _ = services.AddMvc(config => { /* var policy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .Build(); config.Filters.Add(new AuthorizeFilter(policy)); */ config.Filters.Add(new ProducesAttribute("application/json")); // config.ModelBinders.Insert(0,new MyDateTimeModelBinder()); // config.ModelBinders.Insert(0,new MyDecimalModelBinder()); config.EnableEndpointRouting = false; }).AddFormatterMappings( config => config.SetMediaTypeMappingForFormat("text/pdf", new MediaTypeHeaderValue("text/pdf")) ).AddFormatterMappings( config => config.SetMediaTypeMappingForFormat("text/x-tex", new MediaTypeHeaderValue("text/x-tex")) ) .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix, options => { options.ResourcesPath = "Resources"; }).AddDataAnnotationsLocalization(); // services.AddScoped(); // Inject ticket formatting // services.AddTransient(typeof(ISecureDataFormat<>), typeof(SecureDataFormat<>)); // services.AddTransient, SecureDataFormat>(); // services.AddTransient, TicketDataFormat>(); // Add application services. _ = services.AddTransient(); _ = services.AddTransient(); _ = services.AddTransient(); _ = services.AddTransient(); _ = services.AddTransient((sp) => new FileDataStore("googledatastore", false)); _ = services.AddTransient(); // TODO for SMS: services.AddTransient(); _ = services.AddLocalization(options => { options.ResourcesPath = "Resources"; }); var datadi = new DirectoryInfo(protectionSettings.GetSection("Keys").GetValue("Dir")); // Add session related services. services.AddSession(); services.AddDataProtection().PersistKeysToFileSystem(datadi); services.AddAuthorization(options => { options.AddPolicy("AdministratorOnly", policy => { _ = policy.RequireClaim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", Constants.AdminGroupName); }); options.AddPolicy("FrontOffice", policy => policy.RequireRole(Constants.FrontOfficeGroupName)); options.AddPolicy("Bearer", new AuthorizationPolicyBuilder() .AddAuthenticationSchemes("Bearer") .RequireAuthenticatedUser().Build()); // options.AddPolicy("EmployeeId", policy => policy.RequireClaim("EmployeeId", "123", "456")); // options.AddPolicy("BuildingEntry", policy => policy.Requirements.Add(new OfficeEntryRequirement())); options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser()); }); services.AddAuthentication("Bearer") .AddJwtBearer("Bearer", options => { options.Authority = siteSettings.GetValue("Authority"); options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false }; }); _ = services.AddControllersWithViews() .AddNewtonsoftJson(); } public static IServiceProvider Services { get; private set; } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure( IApplicationBuilder app, IOptions siteSettings, IOptions smtpSettings, IAuthorizationService authorizationService, IOptions payPalSettings, IOptions googleSettings, IStringLocalizer localizer, ILoggerFactory loggerFactory, IWebHostEnvironment env) { Services = app.ApplicationServices; GoogleSettings = googleSettings.Value; ResourcesHelpers.GlobalLocalizer = localizer; SiteSetup = siteSettings.Value; SmtpSetup = smtpSettings.Value; Authority = siteSettings.Value.Authority; string blogsDir = siteSettings.Value.Blog ?? throw new Exception("blogsDir is not set."); string billsDir = siteSettings.Value.Bills ?? throw new Exception("billsDir is not set."); AbstractFileSystemHelpers.UserFilesDirName = new DirectoryInfo(blogsDir).FullName; AbstractFileSystemHelpers.UserBillsDirName = new DirectoryInfo(billsDir).FullName; Temp = siteSettings.Value.TempDir; PayPalSettings = payPalSettings.Value; AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); // TODO implement an installation & upgrade procedure // Create required directories foreach (string dir in new string[] { AbstractFileSystemHelpers.UserFilesDirName, AbstractFileSystemHelpers.UserBillsDirName, SiteSetup.TempDir }) { if (dir == null) { throw new Exception(nameof(dir)); } DirectoryInfo di = new(dir); if (!di.Exists) { di.Create(); } } _logger = loggerFactory.CreateLogger(); _ = app.UseStatusCodePagesWithReExecute("/Home/Status/{0}"); if (env.IsDevelopment()) { _ = app.UseDeveloperExceptionPage(); _ = app.UseWelcomePage("/welcome"); app.UseMigrationsEndPoint(); } else { // For more details on creating database during deployment see http://go.microsoft.com/fwlink/?LinkID=615859 _ = app.UseExceptionHandler("/Home/Error"); try { using IServiceScope serviceScope = app.ApplicationServices.GetRequiredService() .CreateScope(); serviceScope.ServiceProvider.GetRequiredService() .Database.Migrate(); } catch (TargetInvocationException ex) { if (ex.InnerException is InvalidOperationException) // nothing to do ? { // TODO (or not) Hit the developper } else { throw ex; } } } // before fixing the security protocol, let beleive our lib it's done with it. // _ = PayPal.Manager.ConnectionManager.Instance; // then, fix it. // ServicePointManager.SecurityProtocol = (SecurityProtocolType)0xC00; // Tls12, required by PayPal // _ = app.UseSession(); ConfigureFileServerApp(app, SiteSetup, env, authorizationService); _ = app.UseRequestLocalization(); ConfigureWorkflow(); ConfigureWebSocketsApp(app); _logger.LogInformation("LocalApplicationData: " + Environment.GetFolderPath( Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.DoNotVerify)); CheckApp(env, loggerFactory); app.UseSession(); _ = app.UseStatusCodePages().UseStaticFiles().UseAuthentication(); _ = app.UseMvcWithDefaultRoute(); } } } //