Developer 02 83d29bf78d Add EmailProfilerDispatcher and enhance logging
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().
2025-05-13 13:44:13 +02:00

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;
}