141 lines
5.6 KiB
C#
141 lines
5.6 KiB
C#
using System.Net.Http.Headers;
|
|
using System.Text;
|
|
using ApiDenuncias.Configuration;
|
|
using ApiDenuncias.Services;
|
|
using ApiDenuncias.Configuration;
|
|
using GestionaDenuncias.Shared.Models;
|
|
using ApiDenuncias.Services;
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.AspNetCore.DataProtection;
|
|
using Microsoft.AspNetCore.Diagnostics;
|
|
using Microsoft.Extensions.Options;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using GestionaDenuncias.Shared.Models;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
builder.Services.Configure<JwtOptions>(builder.Configuration.GetSection(JwtOptions.SectionName));
|
|
builder.Services.Configure<KeyVaultOptions>(builder.Configuration.GetSection(KeyVaultOptions.SectionName));
|
|
builder.Services.Configure<GestionaOptions>(builder.Configuration.GetSection("Gestiona"));
|
|
builder.Services.Configure<GlobalLeaksOptions>(builder.Configuration.GetSection(GlobalLeaksOptions.SectionName));
|
|
builder.Services.Configure<ComplaintStorageOptions>(builder.Configuration.GetSection(ComplaintStorageOptions.SectionName));
|
|
|
|
builder.Services.AddControllers();
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
builder.Services.AddSwaggerGen();
|
|
builder.Services.AddHttpContextAccessor();
|
|
builder.Services.AddDataProtection()
|
|
.SetApplicationName("ApiDenuncias");
|
|
|
|
builder.Services.AddSingleton<LoginRateLimiter>();
|
|
builder.Services.AddSingleton<GlobalLeaksSessionStore>();
|
|
builder.Services.AddScoped<GlobalLeaksClient>();
|
|
builder.Services.AddSingleton<MySqlConnectionStringProvider>();
|
|
builder.Services.AddScoped<MySqlDenunciaStore>();
|
|
builder.Services.AddSingleton<IEncryptionKeyProvider, KeyVaultEncryptionKeyProvider>();
|
|
builder.Services.AddScoped<IDenunciaStore, EncryptedDenunciaStore>();
|
|
builder.Services.AddScoped<IInboxTrackingService, InboxTrackingService>();
|
|
builder.Services.AddScoped<DenunciaInboxService>();
|
|
builder.Services.AddScoped<GestionaDocumentWorkflowService>();
|
|
builder.Services.AddScoped<UserComplaintAccessService>();
|
|
|
|
builder.Services.AddHttpClient<IGestionaService, GestionaService>((sp, client) =>
|
|
{
|
|
var opts = sp.GetRequiredService<IOptions<GestionaOptions>>().Value;
|
|
client.BaseAddress = new Uri(opts.ApiBase);
|
|
client.DefaultRequestHeaders.Add("X-Gestiona-Access-Token", opts.AccessToken);
|
|
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
|
});
|
|
|
|
var jwt = builder.Configuration.GetSection(JwtOptions.SectionName).Get<JwtOptions>() ?? new JwtOptions();
|
|
var signingKey = Encoding.UTF8.GetBytes(jwt.SigningKey);
|
|
|
|
builder.Services
|
|
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
.AddJwtBearer(options =>
|
|
{
|
|
options.RequireHttpsMetadata = jwt.RequireHttpsMetadata;
|
|
options.TokenValidationParameters = new TokenValidationParameters
|
|
{
|
|
ValidateIssuer = true,
|
|
ValidateAudience = true,
|
|
ValidateLifetime = true,
|
|
ValidateIssuerSigningKey = true,
|
|
ValidIssuer = jwt.Issuer,
|
|
ValidAudience = jwt.Audience,
|
|
IssuerSigningKey = new SymmetricSecurityKey(signingKey),
|
|
ClockSkew = TimeSpan.FromMinutes(1)
|
|
};
|
|
options.Events = new JwtBearerEvents
|
|
{
|
|
OnAuthenticationFailed = context =>
|
|
{
|
|
var logger = context.HttpContext.RequestServices
|
|
.GetRequiredService<ILoggerFactory>()
|
|
.CreateLogger("ApiDenuncias.Jwt");
|
|
logger.LogWarning(context.Exception, "JWT no valido en {Path}", context.HttpContext.Request.Path);
|
|
return Task.CompletedTask;
|
|
},
|
|
OnChallenge = context =>
|
|
{
|
|
var logger = context.HttpContext.RequestServices
|
|
.GetRequiredService<ILoggerFactory>()
|
|
.CreateLogger("ApiDenuncias.Jwt");
|
|
logger.LogWarning(
|
|
"JWT rechazado en {Path}. Error={Error}. Description={Description}. AuthorizationHeader={HasAuthorizationHeader}",
|
|
context.HttpContext.Request.Path,
|
|
context.Error,
|
|
context.ErrorDescription,
|
|
context.Request.Headers.ContainsKey("Authorization"));
|
|
return Task.CompletedTask;
|
|
}
|
|
};
|
|
});
|
|
|
|
builder.Services.AddAuthorization();
|
|
|
|
var app = builder.Build();
|
|
|
|
app.UseExceptionHandler(errorApp =>
|
|
{
|
|
errorApp.Run(async context =>
|
|
{
|
|
var feature = context.Features.Get<IExceptionHandlerFeature>();
|
|
var logger = context.RequestServices
|
|
.GetRequiredService<ILoggerFactory>()
|
|
.CreateLogger("ApiDenuncias.UnhandledException");
|
|
|
|
if (feature?.Error is not null)
|
|
{
|
|
logger.LogError(feature.Error, "Error no controlado en {Path}", context.Request.Path);
|
|
}
|
|
|
|
var detailedErrors = context.RequestServices
|
|
.GetRequiredService<IConfiguration>()
|
|
.GetValue("DetailedApiErrors", false);
|
|
var message = detailedErrors && feature?.Error is not null
|
|
? $"La API de denuncias no ha podido completar la operacion: {feature.Error.GetType().Name}: {feature.Error.Message}"
|
|
: "La API de denuncias no ha podido completar la operacion.";
|
|
|
|
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
|
|
await context.Response.WriteAsJsonAsync(new ApiError(message));
|
|
});
|
|
});
|
|
|
|
if (app.Environment.IsDevelopment())
|
|
{
|
|
app.UseSwagger();
|
|
app.UseSwaggerUI();
|
|
}
|
|
|
|
if (builder.Configuration.GetValue("ForceHttpsRedirection", false))
|
|
{
|
|
app.UseHttpsRedirection();
|
|
}
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
app.MapGet("/health", () => Results.Ok(new { status = "ok" })).AllowAnonymous();
|
|
app.MapControllers();
|
|
|
|
app.Run();
|