Introduced `EnvelopeGeneratorOptions` and `SqlCacheOptions` to enable fine-grained control over optional service registrations in `AddEnvelopeGenerator`. Updated `AddEnvelopeGenerator` to conditionally register services like `HttpContextAccessor`, `DistributedSqlServerCache`, `Dispatcher`, `MemoryCache`, and `UserManager` based on these options. Updated `DependencyInjection.csproj` to include necessary framework references and package dependencies. Simplified `Program.cs` by consolidating service registrations into `AddEnvelopeGenerator`, reducing boilerplate and improving maintainability. Improved extensibility by centralizing service registration logic, allowing consuming projects to customize configurations without modifying the core library. Updated documentation and removed unused directives.
243 lines
8.5 KiB
C#
243 lines
8.5 KiB
C#
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 DigitalData.EmailProfilerDispatcher;
|
|
using EnvelopeGenerator.Infrastructure;
|
|
using EnvelopeGenerator.Web.Sanitizers;
|
|
using EnvelopeGenerator.Web.Models.Annotation;
|
|
using EnvelopeGenerator.Web.Middleware;
|
|
using EnvelopeGenerator.DependencyInjection;
|
|
using EnvelopeGenerator.Web;
|
|
|
|
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;
|
|
|
|
Directory
|
|
.GetFiles(builder.Environment.ContentRootPath, "appsettings.*.json", SearchOption.TopDirectoryOnly)
|
|
.Where(file => Path.GetFileName(file) != $"appsettings.Development.json")
|
|
.Where(file => Path.GetFileName(file) != $"appsettings.migration.json")
|
|
.ToList()
|
|
.ForEach(file => config.AddJsonFile(file, true, true));
|
|
|
|
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();
|
|
});
|
|
});
|
|
|
|
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 useDbMigration = Environment.GetEnvironmentVariable("MIGRATION_TEST_MODE") == true.ToString() || config.GetValue<bool>("UseDbMigration");
|
|
var cnnStrName = useDbMigration ? "DbMigrationTest" : "Default";
|
|
var connStr = config.GetConnectionString(cnnStrName)
|
|
?? throw new InvalidOperationException($"Connection string '{cnnStrName}' is missing in the application configuration.");
|
|
|
|
// Add envelope generator services
|
|
builder.Services.AddEnvelopeGenerator(config,
|
|
infrastructureOptions: opt =>
|
|
{
|
|
opt.AddDbTriggerParams(config);
|
|
opt.AddDbContext((provider, options) =>
|
|
{
|
|
var logger = provider.GetRequiredService<ILogger<EGDbContext>>();
|
|
options.UseSqlServer(connStr)
|
|
.LogTo(log => logger.LogInformation("{log}", log), Microsoft.Extensions.Logging.LogLevel.Trace)
|
|
.EnableSensitiveDataLogging()
|
|
.EnableDetailedErrors();
|
|
});
|
|
},
|
|
options: opt =>
|
|
{
|
|
opt.SqlCacheOptions = new()
|
|
{
|
|
ConnectionString = connStr,
|
|
SchemaName = "dbo",
|
|
TableName = "TBDD_CACHE"
|
|
};
|
|
});
|
|
|
|
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";
|
|
});
|
|
|
|
var authCookieName = "env_auth";
|
|
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
|
.AddCookie(options =>
|
|
{
|
|
options.Cookie.Name = authCookieName;
|
|
options.CookieManager = new EnvelopeCookieManager(authCookieName);
|
|
options.Cookie.HttpOnly = true;
|
|
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
|
|
options.Cookie.SameSite = SameSiteMode.Strict;
|
|
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
|
|
});
|
|
|
|
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<MultiCulture>(config.GetSection("Cultures"));
|
|
builder.Services.AddSingleton(sp => sp.GetRequiredService<IOptions<MultiCulture>>().Value);
|
|
|
|
// Register mail services
|
|
builder.Services.AddEnvelopeMailService();
|
|
|
|
builder.ConfigureBySection<CustomImages>();
|
|
|
|
builder.ConfigureBySection<AnnotationParams>();
|
|
|
|
var app = builder.Build();
|
|
|
|
app.UseMiddleware<ExceptionHandlingMiddleware>();
|
|
app.UseMiddleware<CultureMiddleware>();
|
|
|
|
// 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<MultiCulture>();
|
|
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;
|
|
} |