diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/AuthScheme.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/AuthScheme.cs
new file mode 100644
index 00000000..84efbb5d
--- /dev/null
+++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/AuthScheme.cs
@@ -0,0 +1,17 @@
+namespace EnvelopeGenerator.WebUI;
+
+///
+/// Authentication scheme names for envelope generator.
+///
+public static class AuthScheme
+{
+ ///
+ /// Scheme name used for per-envelope receiver JWT authentication.
+ ///
+ public const string Receiver = "EnvelopeGenerator.WebUI.ReceiverJWT";
+
+ ///
+ /// Scheme name used for per-envelope sender JWT authentication.
+ ///
+ public const string Sender = "EnvelopeGenerator.WebUI.SenderJWT";
+}
diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI.csproj b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI.csproj
index 7674d95b..b05029cf 100644
--- a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI.csproj
+++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI.csproj
@@ -8,11 +8,40 @@
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -22,9 +51,6 @@
Always
-
- PreserveNewest
-
diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Middleware/ExceptionHandlingMiddleware.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Middleware/ExceptionHandlingMiddleware.cs
new file mode 100644
index 00000000..807d240e
--- /dev/null
+++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Middleware/ExceptionHandlingMiddleware.cs
@@ -0,0 +1,84 @@
+namespace EnvelopeGenerator.WebUI.Middleware;
+
+using DigitalData.Core.Exceptions;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using System.Net;
+using System.Text.Json;
+
+///
+/// Middleware for handling exceptions globally in the application.
+/// Captures exceptions thrown during the request pipeline execution,
+/// logs them, and returns an appropriate HTTP response with a JSON error message.
+///
+public class ExceptionHandlingMiddleware
+{
+ private readonly RequestDelegate _next;
+ private readonly ILogger _logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The next middleware in the request pipeline.
+ /// The logger instance for logging exceptions.
+ public ExceptionHandlingMiddleware(RequestDelegate next, ILogger logger)
+ {
+ _next = next;
+ _logger = logger;
+ }
+
+ ///
+ /// Invokes the middleware to handle the HTTP request.
+ ///
+ /// The HTTP context of the current request.
+ /// A task that represents the asynchronous operation.
+ public async Task InvokeAsync(HttpContext context)
+ {
+ try
+ {
+ await _next(context); // Continue down the pipeline
+ }
+ catch (Exception ex)
+ {
+ await HandleExceptionAsync(context, ex, _logger);
+ }
+ }
+
+ ///
+ /// Handles exceptions by logging them and writing an appropriate JSON response.
+ ///
+ /// The HTTP context of the current request.
+ /// The exception that occurred.
+ /// The logger instance for logging the exception.
+ /// A task that represents the asynchronous operation.
+ private static async Task HandleExceptionAsync(HttpContext context, Exception exception, ILogger logger)
+ {
+ context.Response.ContentType = "application/json";
+
+ string message;
+
+ switch (exception)
+ {
+ case BadRequestException badRequestEx:
+ context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
+ message = badRequestEx.Message;
+ break;
+
+ case NotFoundException notFoundEx:
+ context.Response.StatusCode = (int)HttpStatusCode.NotFound;
+ message = notFoundEx.Message;
+ break;
+
+ default:
+ logger.LogError(exception, "Unhandled exception occurred.");
+ context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
+ message = "An unexpected error occurred.";
+ break;
+ }
+
+ await context.Response.WriteAsync(JsonSerializer.Serialize(new
+ {
+ message
+ }));
+ }
+}
diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/AuthTokenKeys.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/AuthTokenKeys.cs
new file mode 100644
index 00000000..0d296df7
--- /dev/null
+++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/AuthTokenKeys.cs
@@ -0,0 +1,28 @@
+namespace EnvelopeGenerator.WebUI.Models;
+
+///
+/// Represents the keys and default values used for authentication token handling
+/// within the Envelope Generator WebUI.
+///
+public class AuthTokenKeys
+{
+ ///
+ /// Gets the name of the cookie used to store the authentication token.
+ ///
+ public string Cookie { get; init; } = "AuthToken";
+
+ ///
+ /// Gets the name of the query string parameter used to pass the authentication token.
+ ///
+ public string QueryString { get; init; } = "AuthToken";
+
+ ///
+ /// Gets the expected issuer value for the authentication token.
+ ///
+ public string Issuer { get; init; } = "auth.digitaldata.works";
+
+ ///
+ /// Gets the expected audience value for the authentication token.
+ ///
+ public string Audience { get; init; } = "sign-flow.digitaldata.works";
+}
diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/ConnectionString.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/ConnectionString.cs
new file mode 100644
index 00000000..553360fd
--- /dev/null
+++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Models/ConnectionString.cs
@@ -0,0 +1,12 @@
+namespace EnvelopeGenerator.WebUI.Models;
+
+///
+/// Represents the database connection string for dependency injection.
+///
+public class ConnectionString
+{
+ ///
+ /// The database connection string value.
+ ///
+ public string Value { get; set; } = string.Empty;
+}
diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Options/CacheOptions.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Options/CacheOptions.cs
new file mode 100644
index 00000000..a5ebb277
--- /dev/null
+++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Options/CacheOptions.cs
@@ -0,0 +1,18 @@
+namespace EnvelopeGenerator.WebUI.Options;
+
+///
+/// Configuration options for distributed caching.
+///
+public sealed class CacheOptions
+{
+ ///
+ /// Configuration section name in appsettings.json.
+ ///
+ public const string SectionName = "Cache";
+
+ ///
+ /// Signature cache expiration time.
+ /// If null, signatures will not expire automatically.
+ ///
+ public TimeSpan? SignatureCacheExpiration { get; set; }
+}
diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Program.cs b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Program.cs
index 0c0153d6..331ee357 100644
--- a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Program.cs
+++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/Program.cs
@@ -1,83 +1,386 @@
using EnvelopeGenerator.WebUI.Components;
+using EnvelopeGenerator.WebUI.Models;
+using EnvelopeGenerator.WebUI.Options;
using DevExpress.Blazor;
using EnvelopeGenerator.WebUI.Client.Services;
+using DigitalData.Core.API;
+using DigitalData.Core.Application;
+using EnvelopeGenerator.Infrastructure;
+using EnvelopeGenerator.Domain.Constants;
+using Microsoft.AspNetCore.Authentication.Cookies;
+using Microsoft.AspNetCore.Localization;
+using Microsoft.EntityFrameworkCore;
+using System.Globalization;
+using Scalar.AspNetCore;
+using Microsoft.OpenApi.Models;
+using DigitalData.UserManager.DependencyInjection;
+using EnvelopeGenerator.Application;
+using DigitalData.Auth.Client;
+using DigitalData.Core.Abstractions;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.IdentityModel.Tokens;
+using DigitalData.Core.Abstractions.Security.Extensions;
+using NLog.Web;
+using NLog;
+using DigitalData.Auth.Claims;
+using EnvelopeGenerator.WebUI;
-var builder = WebApplication.CreateBuilder(args);
+var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
+logger.Info("EnvelopeGenerator.WebUI logging initialized!");
-// Load YARP configuration
-builder.Configuration.AddJsonFile("yarp.json", optional: false, reloadOnChange: true);
+try
+{
+ var builder = WebApplication.CreateBuilder(args);
-// Add services to the container.
-builder.Services.AddRazorComponents()
- .AddInteractiveServerComponents()
- .AddInteractiveWebAssemblyComponents();
+ builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
-// HttpClient for server-side components (e.g., MainLayout with FontLoader)
-builder.Services.AddHttpContextAccessor();
-builder.Services.AddScoped(sp => {
- var httpContextAccessor = sp.GetRequiredService();
- var request = httpContextAccessor.HttpContext?.Request;
-
- var httpClient = sp.GetRequiredService().CreateClient();
-
- if (request != null) {
- // Set base address to current host (e.g., https://localhost:5131)
- httpClient.BaseAddress = new Uri($"{request.Scheme}://{request.Host}");
+ if (!builder.Environment.IsDevelopment())
+ {
+ builder.Logging.ClearProviders();
+ builder.Host.UseNLog();
}
-
- return httpClient;
-});
-builder.Services.AddHttpClient();
-// Business Services
-builder.Services.AddScoped();
-builder.Services.AddScoped();
-builder.Services.AddScoped();
-builder.Services.AddScoped();
-builder.Services.AddScoped();
-builder.Services.AddScoped();
-builder.Services.AddSingleton();
+ var config = builder.Configuration;
-// YARP Reverse Proxy
-builder.Services.AddReverseProxy()
- .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
+ var deferredProvider = new DeferredServiceProvider();
-// DevExpress Server-Side Services (CRITICAL for DxPdfViewer)
-builder.Services.AddDevExpressBlazor();
-builder.Services.AddDevExpressServerSideBlazorPdfViewer();
+ // Add Blazor services
+ builder.Services.AddRazorComponents()
+ .AddInteractiveServerComponents()
+ .AddInteractiveWebAssemblyComponents();
-// Configuration Options
-builder.Services.Configure(
- builder.Configuration.GetSection("ApiOptions"));
-builder.Services.Configure(
- builder.Configuration.GetSection("PdfViewerOptions"));
+ // Add API Controllers
+ builder.Services.AddControllers();
+ builder.Services.AddHttpClient();
-var app = builder.Build();
+ // CORS Policy
+ var allowedOrigins = config.GetSection("AllowedOrigins").Get() ??
+ throw new InvalidOperationException("AllowedOrigins section is missing in the configuration.");
+ builder.Services.AddCors(options =>
+ {
+ options.AddPolicy("AllowSpecificOriginsPolicy", builder =>
+ {
+ builder.WithOrigins(allowedOrigins)
+ .SetIsOriginAllowedToAllowWildcardSubdomains()
+ .AllowAnyMethod()
+ .AllowAnyHeader()
+ .AllowCredentials();
+ });
+ });
-// Configure the HTTP request pipeline.
-if (app.Environment.IsDevelopment())
-{
- app.UseWebAssemblyDebugging();
+ // Swagger/OpenAPI
+ builder.Services.AddEndpointsApiExplorer();
+ builder.Services.AddSwaggerGen(options =>
+ {
+ options.SwaggerDoc("v1", new OpenApiInfo
+ {
+ Version = "v1",
+ Title = "signFLOW Absender-API",
+ Description = "Eine API zur Verwaltung der Erstellung, des Versands und der Nachverfolgung von Umschlägen in der signFLOW-Anwendung.",
+ Contact = new OpenApiContact
+ {
+ Name = "Digital Data GmbH",
+ Url = new Uri("https://digitaldata.works/digitale-signatur#kontakt"),
+ Email = "info-flow@digitaldata.works"
+ },
+ });
+
+ options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
+ {
+ Name = "Authorization",
+ Type = SecuritySchemeType.Http,
+ Scheme = "bearer",
+ BearerFormat = "JWT",
+ In = ParameterLocation.Header,
+ Description = "JWT-Autorisierungs-Header unter Verwendung des Bearer-Schemas.",
+ });
+
+ options.AddSecurityRequirement(new OpenApiSecurityRequirement
+ {
+ {
+ new OpenApiSecurityScheme
+ {
+ Reference = new OpenApiReference
+ {
+ Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme,
+ Id = "Bearer"
+ }
+ },
+ Array.Empty()
+ }
+ });
+
+ var xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.xml");
+ foreach (var xmlFile in xmlFiles)
+ {
+ options.IncludeXmlComments(xmlFile);
+ }
+ });
+
+ // Database Context
+ var useDbMigration = Environment.GetEnvironmentVariable("MIGRATION_TEST_MODE") == true.ToString() || config.GetValue("UseDbMigration");
+ var cnnStrName = useDbMigration ? "DbMigrationTest" : "Default";
+ var connStr = config.GetConnectionString(cnnStrName)
+ ?? throw new InvalidOperationException($"Connection string '{cnnStrName}' is missing in the application configuration.");
+
+ builder.Services.Configure(cs => cs.Value = connStr);
+
+ builder.Services.AddDbContext(options => options.UseSqlServer(connStr));
+
+ // Authentication - AuthHub
+ builder.Services.AddAuthHubClient(config.GetSection("AuthClientParams"));
+
+ var authTokenKeys = config.GetOrDefault();
+
+ builder.Services.AddAuthentication(options =>
+ {
+ options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+ options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+ })
+ .AddJwtBearer(AuthScheme.Sender, opt =>
+ {
+ opt.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidateIssuerSigningKey = true,
+ IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) =>
+ {
+ var clientParams = deferredProvider.GetOptions();
+ var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience);
+ return [publicKey.SecurityKey];
+ },
+ ValidateIssuer = true,
+ ValidIssuer = authTokenKeys.Issuer,
+ ValidateAudience = true,
+ ValidAudience = authTokenKeys.Audience,
+ };
+
+ opt.Events = new JwtBearerEvents
+ {
+ OnMessageReceived = context =>
+ {
+ if (context.Token is null)
+ {
+ if (context.Request.Cookies.TryGetValue(authTokenKeys.Cookie, out var cookieToken) && cookieToken is not null)
+ context.Token = cookieToken;
+ else if (context.Request.Query.TryGetValue(authTokenKeys.QueryString, out var queryStrToken))
+ context.Token = queryStrToken;
+ }
+ return Task.CompletedTask;
+ }
+ };
+ })
+ .AddJwtBearer(AuthScheme.Receiver, opt =>
+ {
+ opt.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidateIssuerSigningKey = true,
+ IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) =>
+ {
+ var clientParams = deferredProvider.GetOptions();
+ var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience);
+ return [publicKey.SecurityKey];
+ },
+ ValidateIssuer = true,
+ ValidIssuer = authTokenKeys.Issuer,
+ ValidateAudience = true,
+ ValidAudience = authTokenKeys.Audience,
+ };
+
+ opt.Events = new JwtBearerEvents
+ {
+ OnMessageReceived = context =>
+ {
+ var paths = context.Request.Path.Value?.Split('/', StringSplitOptions.RemoveEmptyEntries);
+ var envelopeKey = paths?.LastOrDefault();
+
+ if (envelopeKey is not null)
+ {
+ var cookieName = CookieNames.GetEnvelopeReceiverCookieName(authTokenKeys.Cookie, envelopeKey);
+ if (context.Request.Cookies.TryGetValue(cookieName, out var cookieToken) && cookieToken is not null)
+ context.Token = cookieToken;
+ }
+
+ return Task.CompletedTask;
+ },
+ OnTokenValidated = context =>
+ {
+ var paths = context.Request.Path.Value?.Split('/', StringSplitOptions.RemoveEmptyEntries);
+ var envelopeKey = paths?.LastOrDefault();
+
+ var sub = context.Principal?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value
+ ?? context.Principal?.FindFirst("sub")?.Value;
+
+ if (envelopeKey is null || sub != envelopeKey)
+ context.Fail("Envelope key in the path does not match the token subject.");
+
+ return Task.CompletedTask;
+ }
+ };
+ });
+
+ // Cookie Authentication
+ builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
+ .AddCookie(options =>
+ {
+ options.Cookie.HttpOnly = true;
+ options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
+ options.Cookie.SameSite = SameSiteMode.Strict;
+ options.LoginPath = "/api/auth/login";
+ options.LogoutPath = "/api/auth/logout";
+ options.SlidingExpiration = true;
+ });
+
+ // Authorization Policies
+ builder.Services.AddAuthorizationBuilder()
+ .AddPolicy(AuthPolicy.SenderOrReceiver, policy => policy.RequireRole(Role.Sender, Role.Receiver.Full))
+ .AddPolicy(AuthPolicy.Sender, policy => policy
+ .RequireRole(Role.Sender)
+ .AddAuthenticationSchemes(AuthScheme.Sender))
+ .AddPolicy(AuthPolicy.Receiver, policy => policy
+ .AddAuthenticationSchemes(AuthScheme.Receiver)
+ .RequireAuthenticatedUser()
+ .RequireRole(Role.Receiver.Full, "receiver"))
+ .AddPolicy(AuthPolicy.ReceiverTFA, policy => policy.RequireRole(Role.Receiver.TFA));
+
+ // User Manager
+#pragma warning disable CS0618
+ builder.Services.AddUserManager();
+#pragma warning restore CS0618
+
+ // LDAP Directory Search
+ builder.ConfigureBySection();
+ builder.Services.AddDirectorySearchService(config.GetSection("DirectorySearchOptions"));
+
+ // Localization
+ builder.Services.AddCookieBasedLocalizer();
+
+ // Cache options
+ builder.Services.Configure(config.GetSection(CacheOptions.SectionName));
+
+ // Distributed Cache - SQL Server
+ builder.Services.AddDistributedSqlServerCache(options =>
+ {
+ config.GetSection("Cache:SqlServer").Bind(options);
+
+ if (string.IsNullOrWhiteSpace(options.ConnectionString))
+ {
+ options.ConnectionString = connStr;
+ }
+ });
+
+ // Envelope Generator Infrastructure & Application Services
+#pragma warning disable CS0618
+ builder.Services
+ .AddEnvelopeGeneratorInfrastructureServices(opt =>
+ {
+ opt.AddDbTriggerParams(config);
+ opt.AddDbContext((provider, options) =>
+ {
+ var logger = provider.GetRequiredService>();
+ options.UseSqlServer(connStr)
+ .LogTo(log => logger.LogInformation("{log}", log), Microsoft.Extensions.Logging.LogLevel.Trace)
+ .EnableSensitiveDataLogging()
+ .EnableDetailedErrors();
+ });
+ opt.AddSQLExecutor(executor => executor.ConnectionString = connStr);
+ })
+ .AddEnvelopeGeneratorServices(config);
+#pragma warning restore CS0618
+
+ // HttpClient for server-side components (e.g., MainLayout with FontLoader)
+ builder.Services.AddHttpContextAccessor();
+ builder.Services.AddScoped(sp =>
+ {
+ var httpContextAccessor = sp.GetRequiredService();
+ var request = httpContextAccessor.HttpContext?.Request;
+
+ var httpClient = sp.GetRequiredService().CreateClient();
+
+ if (request != null)
+ {
+ httpClient.BaseAddress = new Uri($"{request.Scheme}://{request.Host}");
+ }
+
+ return httpClient;
+ });
+
+ // Business Services (WebUI specific)
+ builder.Services.AddScoped();
+ builder.Services.AddScoped();
+ builder.Services.AddScoped();
+ builder.Services.AddScoped();
+ builder.Services.AddScoped();
+ builder.Services.AddScoped();
+ builder.Services.AddSingleton();
+
+ // DevExpress Server-Side Services (CRITICAL for DxPdfViewer)
+ builder.Services.AddDevExpressBlazor();
+ builder.Services.AddDevExpressServerSideBlazorPdfViewer();
+
+ // Configuration Options
+ builder.Services.Configure(
+ builder.Configuration.GetSection("ApiOptions"));
+ builder.Services.Configure(
+ builder.Configuration.GetSection("PdfViewerOptions"));
+
+ var app = builder.Build();
+
+ deferredProvider.Factory = () => app.Services;
+
+ // Exception handling middleware for API controllers
+ app.UseMiddleware();
+
+ // Configure the HTTP request pipeline.
+ if (app.Environment.IsDevelopment())
+ {
+ app.UseWebAssemblyDebugging();
+ app.UseSwagger();
+ app.UseSwaggerUI();
+ app.MapScalarApiReference();
+ }
+ else
+ {
+ app.UseExceptionHandler("/Error", createScopeForErrors: true);
+ app.UseHsts();
+ }
+
+ // Set CORS policy
+ app.UseCors("AllowSpecificOriginsPolicy");
+
+ // Localization
+ string[] supportedCultureNames = ["de-DE", "en-US"];
+ IList list = [.. supportedCultureNames.Select(cn => new CultureInfo(cn))];
+ var cultureInfo = list.FirstOrDefault() ?? throw new InvalidOperationException("There is no supported culture.");
+ var requestLocalizationOptions = new RequestLocalizationOptions
+ {
+ SupportedCultures = list,
+ SupportedUICultures = list
+ };
+ requestLocalizationOptions.RequestCultureProviders.Add(new QueryStringRequestCultureProvider());
+ app.UseRequestLocalization(requestLocalizationOptions);
+
+ app.UseHttpsRedirection();
+
+ app.UseDefaultFiles();
+ app.UseStaticFiles();
+ app.UseAntiforgery();
+
+ app.UseAuthentication();
+ app.UseAuthorization();
+
+ // API Controllers (map before Blazor routing)
+ app.MapControllers();
+
+ // Blazor routing
+ app.MapRazorComponents()
+ .AddInteractiveServerRenderMode()
+ .AddInteractiveWebAssemblyRenderMode()
+ .AddAdditionalAssemblies(typeof(EnvelopeGenerator.WebUI.Client._Imports).Assembly);
+
+ app.Run();
}
-else
+catch (Exception ex)
{
- app.UseExceptionHandler("/Error", createScopeForErrors: true);
- // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
- app.UseHsts();
+ logger.Error(ex, "Stopped program because of exception");
+ throw;
}
-
-app.UseHttpsRedirection();
-
-app.UseStaticFiles();
-app.UseAntiforgery();
-
-// Blazor routing (BEFORE YARP - important order!)
-app.MapRazorComponents()
- .AddInteractiveServerRenderMode()
- .AddInteractiveWebAssemblyRenderMode()
- .AddAdditionalAssemblies(typeof(EnvelopeGenerator.WebUI.Client._Imports).Assembly);
-
-// YARP proxy (AFTER Blazor - catch-all for /api/*)
-app.MapReverseProxy();
-
-app.Run();
diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/appsettings.Development.json b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/appsettings.Development.json
index 0c208ae9..f3350f57 100644
--- a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/appsettings.Development.json
+++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/appsettings.Development.json
@@ -4,5 +4,15 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
+ },
+ "AuthClientParams": {
+ "Url": "http://172.24.12.39:9090/auth-hub",
+ "PublicKeys": [
+ {
+ "Issuer": "auth.digitaldata.works",
+ "Audience": "sign-flow.digitaldata.works"
+ }
+ ],
+ "RetryDelay": "00:00:05"
}
}
diff --git a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/appsettings.json b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/appsettings.json
index 46430479..561aa0d4 100644
--- a/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/appsettings.json
+++ b/EnvelopeGenerator.WebUI/EnvelopeGenerator.WebUI/appsettings.json
@@ -1,4 +1,7 @@
{
+ "UseSwagger": true,
+ "UseDbMigration": false,
+ "DiPMode": true,
"Logging": {
"LogLevel": {
"Default": "Information",
@@ -6,6 +9,42 @@
}
},
"AllowedHosts": "*",
+ "AllowedOrigins": [
+ "http://localhost:4200",
+ "http://172.24.12.39:9090",
+ "https://localhost:8088",
+ "http://localhost:5131",
+ "http://localhost:7192"
+ ],
+ "ConnectionStrings": {
+ "Default": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;",
+ "DbMigrationTest": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM_DATA_MIGR_TEST;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
+ },
+ "DirectorySearchOptions": {
+ "ServerName": "DD-VMP01-DC01",
+ "Root": "DC=dd-gan,DC=local,DC=digitaldata,DC=works",
+ "UserCacheExpirationDays": 1,
+ "CustomSearchFilters": {
+ "User": "(&(objectClass=user)(sAMAccountName=*))",
+ "Group": "(&(objectClass=group)(samAccountName=*))"
+ }
+ },
+ "AuthClientParams": {
+ "Url": "http://172.24.12.39:9090/auth-hub",
+ "PublicKeys": [
+ {
+ "Issuer": "auth.digitaldata.works",
+ "Audience": "sign-flow.digitaldata.works"
+ }
+ ],
+ "RetryDelay": "00:00:05"
+ },
+ "AuthTokenKeys": {
+ "Cookie": "AuthToken",
+ "QueryString": "AuthToken",
+ "Issuer": "auth.digitaldata.works",
+ "Audience": "sign-flow.digitaldata.works"
+ },
"ApiOptions": {
"BaseUrl": ""
},
@@ -14,5 +53,215 @@
"ThumbnailEnableHiDPI": true,
"MainCanvasEnableHiDPI": true,
"ZoomStepPercentage": 5
+ },
+ "PSPDFKitLicenseKey": "SXCtGGY9XA-31OGUXQK-r7c6AkdLGPm2ljuyDr1qu0kkhLvydg-Do-fxpNUF4Rq3fS_xAnZRNFRHbXpE6sQ2BMcCSVTcXVJO6tPviexjpiT-HnrDEySlUERJnnvh-tmeOWprxS6BySPnSILkmaVQtUfOIUS-cUbvvEYHTvQBKbSF8di4XHQFyfv49ihr51axm3NVV3AXwh2EiKL5C5XdqBZ4sQ4O7vXBjM2zvxdPxlxdcNYmiU83uAzw7B83O_jubPzya4CdUHh_YH7Nlp2gP56MeG1Sw2JhMtfG3Rj14Sg4ctaeL9p6AEWca5dDjJ2li5tFIV2fQSsw6A_cowLu0gtMm5i8IfJXeIcQbMC2-0wGv1oe9hZYJvFMdzhTM_FiejM0agemxt3lJyzuyP8zbBSOgp7Si6A85krLWPZptyZBTG7pp7IHboUHfPMxCXqi-zMsqewOJtQBE2mjntU-lPryKnssOpMPfswwQX7QSkJYV5EMqNmEhQX6mEkp2wcqFzMC7bJQew1aO4pOpvChUaMvb1vgRek0HxLag0nwQYX2YrYGh7F_xXJs-8HNwJe8H0-eW4x4faayCgM5rB5772CCCsD9ThZcvXFrjNHHLGJ8WuBUFm6LArvSfFQdii_7j-_sqHMpeKZt26NFgivj1A==",
+ "Content-Security-Policy": [
+ "default-src 'self'",
+ "script-src 'self' 'nonce-{0}' 'unsafe-eval'",
+ "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com:*",
+ "img-src 'self' data: https: blob:",
+ "font-src 'self' https://fonts.gstatic.com:*",
+ "connect-src 'self' https://nominatim.openstreetmap.org:* http://localhost:* https://localhost:* ws://localhost:* wss://localhost:* blob:",
+ "frame-src 'self'",
+ "media-src 'self'",
+ "object-src 'self'"
+ ],
+ "NLog": {
+ "throwConfigExceptions": true,
+ "variables": {
+ "logDirectory": "E:\\LogFiles\\Digital Data\\signFlow",
+ "logFileNamePrefix": "${shortdate}-ECM.EnvelopeGenerator.WebUI"
+ },
+ "targets": {
+ "infoLogs": {
+ "type": "File",
+ "fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log",
+ "maxArchiveDays": 30
+ },
+ "errorLogs": {
+ "type": "File",
+ "fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log",
+ "maxArchiveDays": 30
+ },
+ "criticalLogs": {
+ "type": "File",
+ "fileName": "${logDirectory}\\${logFileNamePrefix}-Critical.log",
+ "maxArchiveDays": 30
+ }
+ },
+ "rules": [
+ {
+ "logger": "*",
+ "minLevel": "Info",
+ "maxLevel": "Warn",
+ "writeTo": "infoLogs"
+ },
+ {
+ "logger": "*",
+ "level": "Error",
+ "writeTo": "errorLogs"
+ },
+ {
+ "logger": "*",
+ "level": "Fatal",
+ "writeTo": "criticalLogs"
+ }
+ ]
+ },
+ "ContactLink": {
+ "Label": "Kontakt",
+ "Href": "https://digitaldata.works/",
+ "HrefLang": "de",
+ "Target": "_blank",
+ "Title": "Digital Data GmbH"
+ },
+ "Cultures": [
+ {
+ "Language": "de-DE",
+ "FIClass": "fi-de"
+ },
+ {
+ "Language": "en-US",
+ "FIClass": "fi-us"
+ }
+ ],
+ "DisableMultiLanguage": false,
+ "Regexes": [
+ {
+ "Pattern": "/^\\p{L}+(?:([\\ \\-\\']|(\\.\\ ))\\p{L}+)*$/u",
+ "Name": "City",
+ "Platforms": [ ".NET" ]
+ },
+ {
+ "Pattern": "/^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$/",
+ "Name": "City",
+ "Platforms": [ "javascript" ]
+ }
+ ],
+ "CustomImages": {
+ "App": {
+ "Src": "/img/DD_signFLOW_LOGO.png",
+ "Classes": {
+ "Main": "signFlow-logo"
+ }
+ },
+ "Company": {
+ "Src": "/img/digital_data.svg",
+ "Classes": {
+ "Show": "dd-show-logo",
+ "Locked": "dd-locked-logo"
+ }
+ }
+ },
+ "DispatcherParams": {
+ "SendingProfile": 1,
+ "AddedWho": "DDEnvelopGenerator",
+ "ReminderTypeId": 202377,
+ "EmailAttmt1": ""
+ },
+ "MailParams": {
+ "Placeholders": {
+ "[NAME_PORTAL]": "signFlow",
+ "[SIGNATURE_TYPE]": "signieren",
+ "[REASON]": ""
+ }
+ },
+ "GtxMessagingParams": {
+ "Uri": "https://rest.gtx-messaging.net",
+ "Path": "smsc/sendsms/f566f7e5-bdf2-4a9a-bf52-ed88215a432e/json",
+ "Headers": {},
+ "QueryParams": {
+ "from": "signFlow"
+ }
+ },
+ "TFARegParams": {
+ "TimeLimit": "00:30:00"
+ },
+ "DbTriggerParams": {
+ "Envelope": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ],
+ "EnvelopeHistory": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ],
+ "EmailOut": [ "TBEMLP_EMAIL_OUT_AFT_INS", "TBEMLP_EMAIL_OUT_AFT_UPD" ],
+ "EnvelopeReceiverReadOnly": [ "TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD" ],
+ "Receiver": [],
+ "EmailTemplate": [ "TBSIG_EMAIL_TEMPLATE_AFT_UPD" ]
+ },
+ "Cache": {
+ "SignatureCacheExpiration": null,
+ "SqlServer": {
+ "ConnectionString": null,
+ "SchemaName": "dbo",
+ "TableName": "TBDD_CACHE"
+ }
+ },
+ "MainPageTitle": null,
+ "AnnotationParams": {
+ "Background": {
+ "Margin": 0.20,
+ "BackgroundColor": {
+ "R": 222,
+ "G": 220,
+ "B": 215
+ },
+ "BorderColor": {
+ "R": 204,
+ "G": 202,
+ "B": 198
+ },
+ "BorderStyle": "underline",
+ "BorderWidth": 4
+ },
+ "DefaultAnnotation": {
+ "Width": 1,
+ "Height": 0.5,
+ "MarginTop": 1
+ },
+ "Annotations": [
+ {
+ "Name": "Signature",
+ "MarginTop": 0
+ },
+ {
+ "Name": "PositionLabel",
+ "VerBoundAnnotName": "Signature",
+ "WidthRatio": 1.2,
+ "HeightRatio": 0.5,
+ "MarginTopRatio": 0.22
+ },
+ {
+ "Name": "Position",
+ "VerBoundAnnotName": "PositionLabel",
+ "WidthRatio": 1.2,
+ "HeightRatio": 0.5,
+ "MarginTopRatio": -0.05
+ },
+ {
+ "Name": "CityLabel",
+ "VerBoundAnnotName": "Position",
+ "WidthRatio": 1.2,
+ "HeightRatio": 0.5,
+ "MarginTopRatio": 0.05
+ },
+ {
+ "Name": "City",
+ "VerBoundAnnotName": "CityLabel",
+ "WidthRatio": 1.2,
+ "HeightRatio": 0.5,
+ "MarginTopRatio": -0.05
+ },
+ {
+ "Name": "DateLabel",
+ "VerBoundAnnotName": "City",
+ "WidthRatio": 1.55,
+ "HeightRatio": 0.5,
+ "MarginTopRatio": 0.05
+ },
+ {
+ "Name": "Date",
+ "VerBoundAnnotName": "DateLabel",
+ "WidthRatio": 1.55,
+ "HeightRatio": 0.5,
+ "MarginTopRatio": -0.1
+ }
+ ]
}
}