Included the DigitalData.EmailProfilerDispatcher namespace in Program.cs for email profiling functionality. Removed Microsoft.Extensions.DependencyInjection namespace to streamline dependency management. Enhanced logging configuration by adding EnableDetailedErrors() for improved error messages during development while retaining EnableSensitiveDataLogging().
259 lines
9.2 KiB
C#
259 lines
9.2 KiB
C#
using EnvelopeGenerator.Application.Services;
|
|
using EnvelopeGenerator.Web.Services;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using NLog;
|
|
using Quartz;
|
|
using NLog.Web;
|
|
using DigitalData.Core.API;
|
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
using EnvelopeGenerator.Web.Models;
|
|
using System.Text.Encodings.Web;
|
|
using Ganss.Xss;
|
|
using Microsoft.Extensions.Options;
|
|
using EnvelopeGenerator.Application;
|
|
using DigitalData.EmailProfilerDispatcher;
|
|
using EnvelopeGenerator.Infrastructure;
|
|
using EnvelopeGenerator.Web.Sanitizers;
|
|
using EnvelopeGenerator.Application.Contracts.Services;
|
|
using EnvelopeGenerator.Web.Models.Annotation;
|
|
using DigitalData.UserManager.DependencyInjection;
|
|
|
|
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
|
logger.Info("Logging initialized!");
|
|
try
|
|
{
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
|
|
|
|
if (!builder.Environment.IsDevelopment())
|
|
{
|
|
builder.Logging.ClearProviders();
|
|
builder.Host.UseNLog();
|
|
}
|
|
|
|
var config = builder.Configuration;
|
|
|
|
var allowedOrigins = config.GetSection("AllowedOrigins").Get<string[]>() ??
|
|
throw new InvalidOperationException("AllowedOrigins section is missing in the configuration.");
|
|
|
|
builder.Services.AddCors(options =>
|
|
{
|
|
options.AddPolicy("SameOriginPolicy", builder =>
|
|
{
|
|
builder.WithOrigins(allowedOrigins)
|
|
.SetIsOriginAllowedToAllowWildcardSubdomains()
|
|
.AllowCredentials()
|
|
.AllowAnyMethod()
|
|
.AllowAnyHeader();
|
|
});
|
|
});
|
|
|
|
// Add base services
|
|
builder.Services.AddScoped<DatabaseService>();
|
|
|
|
// Add higher order services
|
|
builder.Services.AddScoped<EnvelopeOldService>();
|
|
|
|
builder.Services.AddHttpContextAccessor();
|
|
|
|
builder.ConfigureBySection<TFARegParams>();
|
|
|
|
// Add controllers and razor views
|
|
builder.Services.AddControllersWithViews(options =>
|
|
{
|
|
//remove option for Test*Controller
|
|
options.Conventions.Add(new RemoveIfControllerConvention()
|
|
.AndIf(c => c.ControllerName.StartsWith("Test"))
|
|
.AndIf(_ => !builder.IsDevOrDiP() || !config.GetValue<bool>("EnableTestControllers")));
|
|
}).AddJsonOptions(q =>
|
|
{
|
|
// Prevents serialization error when serializing SvgBitmap in EnvelopeReceiver
|
|
q.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles;
|
|
});
|
|
|
|
builder.Services.Configure<CookiePolicyOptions>(options =>
|
|
{
|
|
// This lambda determines whether user consent for non-essential
|
|
// cookies is needed for a given request.
|
|
options.CheckConsentNeeded = context => true;
|
|
|
|
options.MinimumSameSitePolicy = SameSiteMode.None;
|
|
});
|
|
|
|
if (config.GetValue<bool>("EnableSwagger") && builder.IsDevOrDiP())
|
|
{
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
builder.Services.AddSwaggerGen();
|
|
}
|
|
|
|
//AddEF Core dbcontext
|
|
var connStr = config.GetConnectionString(Key.Default) ?? throw new InvalidOperationException("There is no default connection string in appsettings.json.");
|
|
|
|
builder.Services.AddDistributedSqlServerCache(options =>
|
|
{
|
|
options.ConnectionString = connStr;
|
|
options.SchemaName = "dbo";
|
|
options.TableName = "TBDD_CACHE";
|
|
});
|
|
|
|
// Add envelope generator services
|
|
builder.Services.AddEnvelopeGeneratorInfrastructureServices((provider, options) =>
|
|
{
|
|
var logger = provider.GetRequiredService<ILogger<EGDbContext>>();
|
|
options.UseSqlServer(connStr)
|
|
.LogTo(log => logger.LogInformation("{log}", log), Microsoft.Extensions.Logging.LogLevel.Trace)
|
|
.EnableSensitiveDataLogging()
|
|
.EnableDetailedErrors();
|
|
});
|
|
|
|
builder.Services.AddEnvelopeGeneratorServices(config);
|
|
|
|
builder.Services.Configure<CookiePolicyOptions>(options =>
|
|
{
|
|
options.CheckConsentNeeded = context =>
|
|
{
|
|
var consentCookie = context.Request.Cookies["cookie-consent-settings"];
|
|
return consentCookie != "necessary=false";
|
|
};
|
|
|
|
options.MinimumSameSitePolicy = SameSiteMode.Strict;
|
|
options.ConsentCookie.Name = "cookie-consent-settings";
|
|
});
|
|
|
|
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
|
.AddCookie(options =>
|
|
{
|
|
options.Cookie.HttpOnly = true; // Makes the cookie inaccessible to client-side scripts for security
|
|
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; // Ensures cookies are sent over HTTPS only
|
|
options.Cookie.SameSite = SameSiteMode.Strict; // Protects against CSRF attacks by restricting how cookies are sent with requests from external sites
|
|
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
|
|
|
|
options.Events = new CookieAuthenticationEvents
|
|
{
|
|
OnRedirectToLogin = context =>
|
|
{
|
|
// Dynamically calculate the redirection path, for example:
|
|
var envelopeReceiverId = context.HttpContext.Request.RouteValues["envelopeReceiverId"];
|
|
context.RedirectUri = $"/EnvelopeKey/{envelopeReceiverId}/Locked";
|
|
|
|
context.Response.Redirect(context.RedirectUri);
|
|
return Task.CompletedTask;
|
|
},
|
|
OnRedirectToLogout = context =>
|
|
{
|
|
// Apply a similar redirection logic for logout
|
|
var envelopeReceiverId = context.HttpContext.Request.RouteValues["envelopeReceiverId"];
|
|
context.RedirectUri = $"/EnvelopeKey/{envelopeReceiverId}/Success";
|
|
|
|
context.Response.Redirect(context.RedirectUri);
|
|
return Task.CompletedTask;
|
|
}
|
|
};
|
|
});
|
|
|
|
builder.Services.AddSingleton(config.GetSection("ContactLink").Get<ContactLink>() ?? new());
|
|
|
|
builder.Services.AddCookieBasedLocalizer();
|
|
|
|
builder.Services.AddSingleton(HtmlEncoder.Default);
|
|
builder.Services.AddSingleton(UrlEncoder.Default);
|
|
builder.Services.AddSanitizer<HtmlSanitizer>();
|
|
builder.Services.AddSanitizer<HighlightHtmlSanitizer>(s =>
|
|
{
|
|
s.AllowedTags.Add("a");
|
|
s.AllowedAttributes.Add("href");
|
|
s.AllowedAttributes.Add("class");
|
|
s.AllowedClasses.Add("highlight");
|
|
s.AllowedClasses.Add("highlight-envelope-info-1");
|
|
s.AllowedClasses.Add("highlight-envelope-info-2");
|
|
});
|
|
|
|
// Register the FlagIconCssClass instance as a singleton
|
|
builder.Services.Configure<Cultures>(config.GetSection("Cultures"));
|
|
builder.Services.AddSingleton(sp => sp.GetRequiredService<IOptions<Cultures>>().Value);
|
|
|
|
// Register mail services
|
|
builder.Services.AddScoped<IEnvelopeMailService, EnvelopeMailService>();
|
|
builder.Services.AddDispatcher<EGDbContext>();
|
|
|
|
builder.Services.AddMemoryCache();
|
|
|
|
builder.ConfigureBySection<CustomImages>();
|
|
|
|
builder.ConfigureBySection<AnnotationParams>();
|
|
|
|
builder.Services.AddUserManager<EGDbContext>();
|
|
|
|
var app = builder.Build();
|
|
|
|
// Configure the HTTP request pipeline.
|
|
if (!app.Environment.IsDevelopment())
|
|
{
|
|
app.UseExceptionHandler("/Home/Error");
|
|
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
|
app.UseHsts();
|
|
}
|
|
|
|
//Content-Security-Policy
|
|
if (config.GetValue<bool>("UseCSPInDev") || !app.Environment.IsDevelopment())
|
|
{
|
|
var csp_list = config.GetSection("Content-Security-Policy").Get<string[]>();
|
|
if (csp_list is null)
|
|
logger.Warn("There is no Content-Security-Policy");
|
|
else
|
|
{
|
|
var csp = string.Join("; ", csp_list?.Where(st => st is not null) ?? Array.Empty<string>());
|
|
logger.Info($"Content-Security-Policy {csp}");
|
|
if (csp_list is not null)
|
|
app.UseCSPMiddleware(csp);
|
|
}
|
|
}
|
|
|
|
if (config.GetValue<bool>("EnableSwagger") && builder.IsDevOrDiP())
|
|
{
|
|
app.UseSwagger();
|
|
app.UseSwaggerUI();
|
|
}
|
|
|
|
app.UseHttpsRedirection();
|
|
|
|
app.UseStaticFiles();
|
|
|
|
app.UseCookiePolicy();
|
|
|
|
app.UseRouting();
|
|
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
|
|
var cultures = app.Services.GetRequiredService<Cultures>();
|
|
if(!cultures.Any())
|
|
throw new InvalidOperationException(@"Languages section is missing in the appsettings. Please configure like following.
|
|
Language is both a name of the culture and the name of the resx file such as Resource.de-DE.resx
|
|
FIClass is the css class (in wwwroot/lib/flag-icons-main) for the flag of country.
|
|
""Cultures"": [
|
|
{
|
|
""Language"": ""de-DE"",
|
|
""FIClass"": ""fi-de""
|
|
},
|
|
{
|
|
""Language"": ""en-US"",
|
|
""FIClass"": ""fi-us""
|
|
}
|
|
]");
|
|
|
|
if(!config.GetValue<bool>("DisableMultiLanguage"))
|
|
app.UseCookieBasedLocalizer(cultures.Languages.ToArray());
|
|
|
|
app.UseCors("SameOriginPolicy");
|
|
|
|
app.MapControllers();
|
|
app.MapFallbackToController("Error404", "Home");
|
|
app.Run();
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
logger.Error(ex, "Stopped program because of exception");
|
|
throw;
|
|
} |