diff --git a/APIFicheros/APIFicheros.csproj b/APIFicheros/APIFicheros.csproj index 505c4bc..db4869c 100644 --- a/APIFicheros/APIFicheros.csproj +++ b/APIFicheros/APIFicheros.csproj @@ -7,6 +7,9 @@ + + + diff --git a/APIFicheros/Controllers/AuthController.cs b/APIFicheros/Controllers/AuthController.cs new file mode 100644 index 0000000..29890e7 --- /dev/null +++ b/APIFicheros/Controllers/AuthController.cs @@ -0,0 +1,58 @@ +using APIFicheros.DTOs; +using bdAsegasa; +using bdAsegasa.db; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; +using tsUtilidades; + +namespace APIFicheros.Controllers +{ + [ApiController] + [Route("[controller]")] + public class AuthController : Controller + { + + private readonly IConfiguration _configuration; + + public AuthController(IConfiguration configuration) + { + _configuration = configuration; + } + + [AllowAnonymous] + [HttpPost("login")] + public IActionResult Login([FromBody] DatosAuth loginDto) + { + + string token = ""; + string datosLoginUser = _configuration["DatosLogin:Usuario"]; + string datosLoginPassword = _configuration["DatosLogin:Password"]; + + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + if (loginDto.usuario == datosLoginUser && loginDto.password == datosLoginPassword) + { + token = Utilidades.AuthenticateUser(loginDto, _configuration); + } + else + { + return Unauthorized("Nombre de usuario o contraseña incorrectos."); + } + + return Ok(new + { + Token = token + }); + } + + + } +} diff --git a/APIFicheros/Controllers/ObtenerFicherosController.cs b/APIFicheros/Controllers/ManipularFicherosController.cs similarity index 54% rename from APIFicheros/Controllers/ObtenerFicherosController.cs rename to APIFicheros/Controllers/ManipularFicherosController.cs index a483c83..7407189 100644 --- a/APIFicheros/Controllers/ObtenerFicherosController.cs +++ b/APIFicheros/Controllers/ManipularFicherosController.cs @@ -1,6 +1,8 @@ using APIFicheros.DTOs; using bdAsegasa; using bdAsegasa.db; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; @@ -10,11 +12,11 @@ namespace APIFicheros.Controllers { [ApiController] [Route("[controller]")] - public class ObtenerFicherosController : Controller + public class ManipularFicherosController : Controller { private readonly IConfiguration _configuration; private bdAsegasa.tscgestionasegasa bd; - public ObtenerFicherosController(IConfiguration configuration) + public ManipularFicherosController(IConfiguration configuration) { _configuration = configuration; @@ -25,14 +27,12 @@ namespace APIFicheros.Controllers } - [HttpPost("ObtenerFicherosFaltantes")] - public ActionResult PostNombreBD([FromBody] string numeroPoliza) + [Authorize] + public ActionResult PostComprobarFicheros([FromBody] string numeroPoliza) { - try { - int idPoliza = bd.polizassg.First(x => x.NumeroPoliza!.Contains(numeroPoliza)).idPoliza; @@ -54,8 +54,9 @@ namespace APIFicheros.Controllers return Ok(listadoFicherosFaltantes); } - catch (Exception ex) { - + catch (Exception ex) + { + return BadRequest("Ha ocurrido un error. Mensaje de error: " + ex.Message); } @@ -63,5 +64,47 @@ namespace APIFicheros.Controllers } + + + [HttpPost("SubirFicherosFaltantes")] + [Authorize] + public ActionResult PostSubirFicheros([FromBody] DatosFicheros datosFichero) + { + + var transaction = bd.Database.BeginTransaction(); + + try + { + + byte[] contenidoFichero; + var ms = new MemoryStream(); + datosFichero.fichero.CopyTo(ms); + contenidoFichero = ms.ToArray(); + + int idFichero = Utilidades.guardarFichero(bd, contenidoFichero, datosFichero.fichero.FileName, datosFichero.descripcion); + + + documentospolizassg documentoObtenido = bd.documentospolizassg.First( x => x.idDocumento == datosFichero.idDocumento); + + documentoObtenido.idFichero = idFichero; + + bd.Update(documentoObtenido); + + bd.SaveChanges(); + + transaction.Commit(); + return Ok(); + + } + catch (Exception ex) + { + transaction.Rollback(); + return BadRequest("Ha ocurrido un error. Mensaje de error: " + ex.Message); + } + + } + + + } } diff --git a/APIFicheros/Controllers/WeatherForecastController.cs b/APIFicheros/Controllers/WeatherForecastController.cs deleted file mode 100644 index 9216ce9..0000000 --- a/APIFicheros/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace APIFicheros.Controllers; - -[ApiController] -[Route("[controller]")] -public class WeatherForecastController : ControllerBase -{ - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable Get() - { - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)] - }) - .ToArray(); - } -} diff --git a/APIFicheros/DTOs/DatosAuth.cs b/APIFicheros/DTOs/DatosAuth.cs new file mode 100644 index 0000000..2ed71d7 --- /dev/null +++ b/APIFicheros/DTOs/DatosAuth.cs @@ -0,0 +1,8 @@ +namespace APIFicheros.DTOs +{ + public class DatosAuth + { + public string usuario { get; set; } + public string password { get; set; } + } +} diff --git a/APIFicheros/DTOs/DatosFicheros.cs b/APIFicheros/DTOs/DatosFicheros.cs new file mode 100644 index 0000000..c0669a2 --- /dev/null +++ b/APIFicheros/DTOs/DatosFicheros.cs @@ -0,0 +1,9 @@ +namespace APIFicheros.DTOs +{ + public class DatosFicheros + { + public int idDocumento { get; set; } + public IFormFile fichero { get; set; } + public string descripcion { get; set; } + } +} diff --git a/APIFicheros/Program.cs b/APIFicheros/Program.cs index 7bded67..cab2a27 100644 --- a/APIFicheros/Program.cs +++ b/APIFicheros/Program.cs @@ -1,32 +1,130 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using System.Text; +using Microsoft.OpenApi.Models; +using System.Reflection; +using System.Text.Json.Serialization; + var builder = WebApplication.CreateBuilder(args); -// Add services to the container. - -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); - +// 1. Configuración de Servicios +// a. Configurar servicios de controladores builder.Services.AddControllers() - .AddJsonOptions(options => +.ConfigureApiBehaviorOptions(options => +{ + options.SuppressModelStateInvalidFilter = true; // Desactiva la validación automática del estado del modelo +}) +.AddJsonOptions(options => +{ + options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; +}); + +// b. Configuración de JWT +var jwtSettings = builder.Configuration.GetSection("Jwt"); +var keyString = jwtSettings["Key"]; +if (string.IsNullOrEmpty(keyString)) +{ + throw new ArgumentNullException("JWT Key is not configured."); +} +var key = Encoding.UTF8.GetBytes(keyString); + +builder.Services.AddAuthentication(options => +{ + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; +}) +.AddJwtBearer(options => +{ + options.TokenValidationParameters = new TokenValidationParameters { - options.JsonSerializerOptions.ReferenceHandler = - System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles; + ValidateIssuer = true, + ValidIssuer = jwtSettings["Issuer"], + ValidateAudience = true, + ValidAudience = jwtSettings["Audience"], + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["Key"])), + ValidAlgorithms = new[] { SecurityAlgorithms.HmacSha256 } + }; + + options.Events = new JwtBearerEvents + { + OnAuthenticationFailed = ctx => + { + var logger = ctx.HttpContext.RequestServices.GetRequiredService>(); + logger.LogError("NOMBRE FALLO {0}", ctx.Exception.GetType().Name); + logger.LogError("MENSAJE FALLO {0}", ctx.Exception.Message); + return Task.CompletedTask; + }, + }; +}); + +// d. Configurar Swagger con soporte para JWT +builder.Services.AddSwaggerGen(c => +{ + c.SwaggerDoc("v1", new OpenApiInfo { Title = "SwaggerCamcue API", Version = "v1" }); + + // Definir el esquema de seguridad JWT + var securityScheme = new OpenApiSecurityScheme + { + Name = "Authorization", + Description = "Ingrese 'Bearer' seguido de su token en el campo de texto.\n\nEjemplo: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6...'", + In = ParameterLocation.Header, + Type = SecuritySchemeType.Http, + Scheme = "Bearer", + BearerFormat = "JWT" + }; + + c.AddSecurityDefinition("Bearer", securityScheme); + + + c.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + Array.Empty() + } }); +}); var app = builder.Build(); -// Configure the HTTP request pipeline. +// 2. Configuración del Pipeline HTTP + +// b. Habilitar Swagger solo en Desarrollo if (app.Environment.IsDevelopment()) { app.UseSwagger(); - app.UseSwaggerUI(); + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "SwaggerCamcue API V1"); + c.RoutePrefix = string.Empty; // Swagger en la raíz + }); } -app.UseHttpsRedirection(); +app.UseCors(policy => +{ + policy.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader(); +}); + +//app.UseHttpsRedirection(); +// d. Autenticación y Autorización +app.UseAuthentication(); app.UseAuthorization(); +// e. Mapear Controladores app.MapControllers(); +// f. Ejecutar la Aplicación app.Run(); diff --git a/APIFicheros/Utilidades.cs b/APIFicheros/Utilidades.cs new file mode 100644 index 0000000..0b95065 --- /dev/null +++ b/APIFicheros/Utilidades.cs @@ -0,0 +1,90 @@ +using APIFicheros.DTOs; +using bdAsegasa; +using bdAsegasa.db; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; +using tsUtilidades; +namespace APIFicheros +{ + + + public class Utilidades + { + public static string AuthenticateUser(DatosAuth loginDto, IConfiguration _configuration) + { + try + { + + // Generar token JWT + var jwtSettings = _configuration.GetSection("Jwt"); + var keyBytes = Encoding.UTF8.GetBytes(jwtSettings["Key"]); + var key = new SymmetricSecurityKey(keyBytes); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + + var claims = new List + { + new Claim("usu", "1"), + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + }; + + + var expiration = DateTime.UtcNow.AddMinutes(int.Parse(jwtSettings["ExpireMinutes"])); + + var token = new JwtSecurityToken( + issuer: jwtSettings["Issuer"], + audience: jwtSettings["Audience"], + claims: claims, + expires: expiration, + signingCredentials: creds + ); + + var tokenString = new JwtSecurityTokenHandler().WriteToken(token); + + return tokenString; + } + catch (Exception ex) + { + throw new Exception($"Error en la autenticación: {ex.Message}"); + } + } + + + public static int guardarFichero(bdAsegasa.tscgestionasegasa bd, byte[] fichero,string nombreFichero ,string? descripcion) + { + int idFichero = 0; + + try + { + + ficheros nuevoFichero = new ficheros(); + + + nuevoFichero.Fichero = fichero; + nuevoFichero.NombreFichero = nombreFichero; + nuevoFichero.Descripcion = descripcion; + + + + bd.ficheros.Add(nuevoFichero); + + bd.SaveChanges(); + + + idFichero = nuevoFichero.idFichero; + return idFichero; + + } + catch (Exception ex) { + + throw new Exception("Excepción: " + ex.Message); + } + + } + } +} diff --git a/APIFicheros/WeatherForecast.cs b/APIFicheros/WeatherForecast.cs deleted file mode 100644 index 4d1de37..0000000 --- a/APIFicheros/WeatherForecast.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace APIFicheros; - -public class WeatherForecast -{ - public DateOnly Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string? Summary { get; set; } -} diff --git a/APIFicheros/appsettings.json b/APIFicheros/appsettings.json index 48353fb..a938d99 100644 --- a/APIFicheros/appsettings.json +++ b/APIFicheros/appsettings.json @@ -1,11 +1,20 @@ { + "Jwt": { + "Key": "Asegasa_APIFicheros2025_5849651882", + "Issuer": "MiAPI_AuthServer", + "Audience": "TuAudience", + "ExpireMinutes":1 + }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, - + "DatosLogin": { + "Usuario": "ASEGASA", + "Password": "Asegasa.2025" + }, "Configuracion": { "SegundosMinimosEntreProcesos": "60", "HoraProcesosDiarios": "06:30",