diff --git a/WorkFlow.API/Controllers/ConfigController.cs b/WorkFlow.API/Controllers/ConfigController.cs index 4ae5d47..bd4a6ed 100644 --- a/WorkFlow.API/Controllers/ConfigController.cs +++ b/WorkFlow.API/Controllers/ConfigController.cs @@ -6,13 +6,15 @@ using WorkFlow.Application.Contracts; using WorkFlow.Application.DTO.Config; using WorkFlow.Domain.Entities; -namespace WorkFlow.API.Controllers +namespace WorkFlow.API.Controllers; + +[APIKeyAuth] +[Route("api/[controller]")] +[ApiController] +[Authorize] +public class ConfigController : CRUDControllerBaseWithErrorHandling { - [APIKeyAuth] - [Route("api/[controller]")] - [ApiController] - [Authorize] - public class ConfigController(ILogger logger, IConfigService service) : CRUDControllerBaseWithErrorHandling(logger, service) + public ConfigController(ILogger logger, IConfigService service) : base(logger, service) { } } \ No newline at end of file diff --git a/WorkFlow.API/Controllers/ProfileController.cs b/WorkFlow.API/Controllers/ProfileController.cs index 7d8aae6..a888fa2 100644 --- a/WorkFlow.API/Controllers/ProfileController.cs +++ b/WorkFlow.API/Controllers/ProfileController.cs @@ -6,13 +6,15 @@ using WorkFlow.Application.Contracts; using WorkFlow.Application.DTO.Profile; using WorkFlow.Domain.Entities; -namespace WorkFlow.API.Controllers +namespace WorkFlow.API.Controllers; + +[APIKeyAuth] +[Route("api/[controller]")] +[ApiController] +[Authorize] +public class ProfileController : CRUDControllerBaseWithErrorHandling { - [APIKeyAuth] - [Route("api/[controller]")] - [ApiController] - [Authorize] - public class ProfileController(ILogger logger, IProfileService service) : CRUDControllerBaseWithErrorHandling(logger, service) + public ProfileController(ILogger logger, IProfileService service) : base(logger, service) { } } \ No newline at end of file diff --git a/WorkFlow.API/Controllers/ProfileControlsTFController.cs b/WorkFlow.API/Controllers/ProfileControlsTFController.cs index b4db649..0196e36 100644 --- a/WorkFlow.API/Controllers/ProfileControlsTFController.cs +++ b/WorkFlow.API/Controllers/ProfileControlsTFController.cs @@ -7,114 +7,120 @@ using WorkFlow.Application.Contracts; using WorkFlow.Application.DTO.ProfileControlsTF; using WorkFlow.Domain.Entities; -namespace WorkFlow.API.Controllers +namespace WorkFlow.API.Controllers; + +[APIKeyAuth] +[Route("api/[controller]")] +[ApiController] +[Authorize] +public class ProfileControlsTFController : CRUDControllerBase { - [APIKeyAuth] - [Route("api/[controller]")] - [ApiController] - [Authorize] - public class ProfileControlsTFController(ILogger logger, IProfileControlsTFService service) : CRUDControllerBase(logger, service) + private readonly ILogger logger; + + public ProfileControlsTFController(ILogger logger, IProfileControlsTFService service) : base(logger, service) { - [NonAction] - public override Task GetAll() => base.GetAll(); + this.logger = logger; + } - [NonAction] - public override Task Update(ProfileControlsTFUpdateDto updateDto) => base.Update(updateDto); + [NonAction] + public override Task GetAll() => base.GetAll(); - [HttpGet] - public async Task GetAsync( - bool withProfile = true, bool withUser = false, - int? profileId = null, int? objId = null, bool? profileActive = null) + [NonAction] + public override Task Update(ProfileControlsTFUpdateDto updateDto) => base.Update(updateDto); + + [HttpGet] + public async Task GetAsync( + bool withProfile = true, bool withUser = false, + int? profileId = null, int? objId = null, bool? profileActive = null) + { + try { - try + if (!this.TryGetUserId(out int? id)) { - if (!this.TryGetUserId(out int? id)) - { - logger.LogError("Authorization failed: User ID claim not found."); - return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity."); - } - else if (id is null) - { - logger.LogError("Invalid user ID: Retrieved ID is null or not an integer."); - return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID."); - } - - return await _service.ReadAsync( - withProfile: withProfile, withUser: withUser, - userId: id, - profileId: profileId, objId: objId, profileActive: profileActive) - .ThenAsync( - Success: pctf => pctf.Any() ? Ok(pctf) : NotFound(), - Fail: IActionResult (msg, ntc) => - { - logger.LogNotice(ntc); - return NotFound(); - }); + logger.LogError("Authorization failed: User ID claim not found."); + return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity."); } - catch (Exception ex) + else if (id is null) { - logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message); - return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred."); + logger.LogError("Invalid user ID: Retrieved ID is null or not an integer."); + return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID."); } - } - [HttpPost] - public override async Task Create([FromBody] ProfileControlsTFCreateDto createDto) - { - try - { - if (!this.TryGetUserId(out int? id)) - { - logger.LogError("Authorization failed: User ID claim not found."); - return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity."); - } - else if (id is null) - { - logger.LogError("Invalid user ID: Retrieved ID is null or not an integer."); - return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID."); - } - - if (createDto.UserId != id) - return Unauthorized(); - - return await base.Create(createDto); - } - catch (Exception ex) - { - logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message); - return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred."); - } - } - - [HttpDelete] - public override async Task Delete([FromRoute] int id) - { - try - { - if (!this.TryGetUserId(out int? userId)) - { - logger.LogError("Authorization failed: User ID claim not found."); - return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity."); - } - else if (userId is null) - { - logger.LogError("Invalid user ID: Retrieved ID is null or not an integer."); - return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID."); - } - - return await _service.ReadByIdAsync(id).ThenAsync( - SuccessAsync: async pctf => pctf.UserId == userId ? await base.Delete(id) : Unauthorized(), + return await _service.ReadAsync( + withProfile: withProfile, withUser: withUser, + userId: id, + profileId: profileId, objId: objId, profileActive: profileActive) + .ThenAsync( + Success: pctf => pctf.Any() ? Ok(pctf) : NotFound(), Fail: IActionResult (msg, ntc) => { - _logger.LogNotice(ntc); - return ntc.HasFlag(Flag.NotFound) ? NotFound() : StatusCode(StatusCodes.Status500InternalServerError); + logger.LogNotice(ntc); + return NotFound(); }); - } - catch (Exception ex) + } + catch (Exception ex) + { + logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred."); + } + } + + [HttpPost] + public override async Task Create([FromBody] ProfileControlsTFCreateDto createDto) + { + try + { + if (!this.TryGetUserId(out int? id)) { - logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message); - return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred."); + logger.LogError("Authorization failed: User ID claim not found."); + return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity."); } + else if (id is null) + { + logger.LogError("Invalid user ID: Retrieved ID is null or not an integer."); + return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID."); + } + + if (createDto.UserId != id) + return Unauthorized(); + + return await base.Create(createDto); + } + catch (Exception ex) + { + logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred."); + } + } + + [HttpDelete] + public override async Task Delete([FromRoute] int id) + { + try + { + if (!this.TryGetUserId(out int? userId)) + { + logger.LogError("Authorization failed: User ID claim not found."); + return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity."); + } + else if (userId is null) + { + logger.LogError("Invalid user ID: Retrieved ID is null or not an integer."); + return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID."); + } + + return await _service.ReadByIdAsync(id).ThenAsync( + SuccessAsync: async pctf => pctf.UserId == userId ? await base.Delete(id) : Unauthorized(), + Fail: IActionResult (msg, ntc) => + { + _logger.LogNotice(ntc); + return ntc.HasFlag(Flag.NotFound) ? NotFound() : StatusCode(StatusCodes.Status500InternalServerError); + }); + } + catch (Exception ex) + { + logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred."); } } } \ No newline at end of file diff --git a/WorkFlow.API/Controllers/ProfileObjStateController.cs b/WorkFlow.API/Controllers/ProfileObjStateController.cs index c5c63fd..f75e92e 100644 --- a/WorkFlow.API/Controllers/ProfileObjStateController.cs +++ b/WorkFlow.API/Controllers/ProfileObjStateController.cs @@ -13,8 +13,15 @@ namespace WorkFlow.API.Controllers [Route("api/[controller]")] [ApiController] [Authorize] - public class ProfileObjStateController(ILogger logger, IProfileObjStateService service) : CRUDControllerBaseWithErrorHandling(logger, service) + public class ProfileObjStateController : CRUDControllerBaseWithErrorHandling { + private readonly ILogger logger; + + public ProfileObjStateController(ILogger logger, IProfileObjStateService service) : base(logger, service) + { + this.logger = logger; + } + [NonAction] public override Task GetAll() => base.GetAll(); diff --git a/WorkFlow.API/Controllers/StateController.cs b/WorkFlow.API/Controllers/StateController.cs index a63199d..8d38353 100644 --- a/WorkFlow.API/Controllers/StateController.cs +++ b/WorkFlow.API/Controllers/StateController.cs @@ -6,13 +6,15 @@ using WorkFlow.Application.Contracts; using WorkFlow.Application.DTO.State; using WorkFlow.Domain.Entities; -namespace WorkFlow.API.Controllers +namespace WorkFlow.API.Controllers; + +[APIKeyAuth] +[Route("api/[controller]")] +[ApiController] +[Authorize] +public class StateController : CRUDControllerBaseWithErrorHandling { - [APIKeyAuth] - [Route("api/[controller]")] - [ApiController] - [Authorize] - public class StateController(ILogger logger, IStateService service) : CRUDControllerBaseWithErrorHandling(logger, service) + public StateController(ILogger logger, IStateService service) : base(logger, service) { } } \ No newline at end of file diff --git a/WorkFlow.API/Controllers/UserController.cs b/WorkFlow.API/Controllers/UserController.cs index 9944aaa..bc76eeb 100644 --- a/WorkFlow.API/Controllers/UserController.cs +++ b/WorkFlow.API/Controllers/UserController.cs @@ -4,43 +4,51 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using WorkFlow.API.Attributes; -namespace WorkFlow.API.Controllers +namespace WorkFlow.API.Controllers; + +[APIKeyAuth] +[Route("api/[controller]")] +[ApiController] +[Authorize] +public class UserController : ControllerBase { - [APIKeyAuth] - [Route("api/[controller]")] - [ApiController] - [Authorize] - public class UserController(ILogger logger, IUserService userService) : ControllerBase + private readonly ILogger logger; + private readonly IUserService userService; + + public UserController(ILogger logger, IUserService userService) { - [HttpGet] - public async Task GetAsync() + this.logger = logger; + this.userService = userService; + } + + [HttpGet] + public async Task GetAsync() + { + try { - try + if (!this.TryGetUserId(out int? id)) { - if (!this.TryGetUserId(out int? id)) - { - logger.LogError("Authorization failed: User ID claim not found."); - return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity."); - } - else if(id is int id_int) - return await userService.ReadByIdAsync(id_int).ThenAsync( - Success: Ok, - Fail: IActionResult (msg, ntc) => - { - logger.LogNotice(ntc); - return NotFound(); - }); - else - { - logger.LogError("Invalid user ID: Retrieved ID is null or not an integer."); - return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID."); - } + logger.LogError("Authorization failed: User ID claim not found."); + return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity."); } - catch (Exception ex) + else if(id is int id_int) + return await userService.ReadByIdAsync(id_int).ThenAsync( + Success: Ok, + Fail: IActionResult (msg, ntc) => + { + logger.LogNotice(ntc); + return NotFound(); + }); + else { - logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message); - return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred."); + logger.LogError("Invalid user ID: Retrieved ID is null or not an integer."); + return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID."); } } + catch (Exception ex) + { + logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred."); + } } } \ No newline at end of file diff --git a/WorkFlow.API/Filters/APIKeyAuthHeaderOpFilter.cs b/WorkFlow.API/Filters/APIKeyAuthHeaderOpFilter.cs index bb022a5..7a7a7b4 100644 --- a/WorkFlow.API/Filters/APIKeyAuthHeaderOpFilter.cs +++ b/WorkFlow.API/Filters/APIKeyAuthHeaderOpFilter.cs @@ -4,33 +4,39 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using WorkFlow.API.Models; -namespace WorkFlow.API.Filters +namespace WorkFlow.API.Filters; + +public class APIKeyAuthHeaderOpFilter : IOperationFilter { - public class APIKeyAuthHeaderOpFilter(IOptions options, IWebHostEnvironment environment) : IOperationFilter + private readonly APIKeyAuthOptions apiKeyAuthOptions; + private readonly IWebHostEnvironment environment; + + public APIKeyAuthHeaderOpFilter(IOptions options, IWebHostEnvironment environment) { - private readonly APIKeyAuthOptions apiKeyAuthOptions = options.Value; + this.environment = environment; + apiKeyAuthOptions = options.Value; + } - public void Apply(OpenApiOperation operation, OperationFilterContext context) + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + var param = new OpenApiParameter { - var param = new OpenApiParameter + Name = apiKeyAuthOptions.HeaderName, + In = ParameterLocation.Header, + Required = true, + AllowEmptyValue = false, + Schema = new OpenApiSchema { - Name = apiKeyAuthOptions.HeaderName, - In = ParameterLocation.Header, - Required = true, - AllowEmptyValue = false, - Schema = new OpenApiSchema - { - Type = "string" - } - }; + Type = "string" + } + }; - if(environment.IsDevelopment()) - param.Schema.Default = new OpenApiString(apiKeyAuthOptions.Key); + if(environment.IsDevelopment()) + param.Schema.Default = new OpenApiString(apiKeyAuthOptions.Key); - if (apiKeyAuthOptions.SwaggerDescription is not null) - param.Description = apiKeyAuthOptions.SwaggerDescription; + if (apiKeyAuthOptions.SwaggerDescription is not null) + param.Description = apiKeyAuthOptions.SwaggerDescription; - operation.Parameters.Add(param); - } + operation.Parameters.Add(param); } } \ No newline at end of file diff --git a/WorkFlow.API/Filters/ApiKeyAuthFilter.cs b/WorkFlow.API/Filters/ApiKeyAuthFilter.cs index b69a6c2..f9f73af 100644 --- a/WorkFlow.API/Filters/ApiKeyAuthFilter.cs +++ b/WorkFlow.API/Filters/ApiKeyAuthFilter.cs @@ -1,14 +1,22 @@ using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc; -namespace WorkFlow.API.Filters +namespace WorkFlow.API.Filters; + +public class APIKeyAuthFilter : IAuthorizationFilter { - public class APIKeyAuthFilter(Func isValidKey, string headerName = "X-API-Key") : IAuthorizationFilter + private readonly Func isValidKey; + private readonly string headerName; + + public APIKeyAuthFilter(Func isValidKey, string headerName = "X-API-Key") { - public void OnAuthorization(AuthorizationFilterContext context) - { - if (!isValidKey(context.HttpContext.Request.Headers[headerName])) - context.Result = new UnauthorizedResult(); - } + this.isValidKey = isValidKey; + this.headerName = headerName; + } + + public void OnAuthorization(AuthorizationFilterContext context) + { + if (!isValidKey(context.HttpContext.Request.Headers[headerName])) + context.Result = new UnauthorizedResult(); } } \ No newline at end of file diff --git a/WorkFlow.API/Models/ModelExtensions.cs b/WorkFlow.API/Models/ModelExtensions.cs index c7bc6e8..6abccf1 100644 --- a/WorkFlow.API/Models/ModelExtensions.cs +++ b/WorkFlow.API/Models/ModelExtensions.cs @@ -5,13 +5,14 @@ namespace WorkFlow.API.Models { public static class ModelExtensions { - public static List ToClaimList(this UserReadDto user) => [ + public static List ToClaimList(this UserReadDto user) => new() + { new (ClaimTypes.NameIdentifier, user.Id.ToString()), new (ClaimTypes.Name, user.Username), new (ClaimTypes.Surname, user.Name ?? ""), new (ClaimTypes.GivenName, user.Prename ?? ""), new (ClaimTypes.Email, user.Email ?? "") - ]; + }; public static Dictionary ToClaimDictionary(this UserReadDto user) => user.ToClaimList().ToDictionary(claim => claim.Type, claim => (object) claim.Value); } diff --git a/WorkFlow.API/Program.cs b/WorkFlow.API/Program.cs index 5b57f3f..00708f0 100644 --- a/WorkFlow.API/Program.cs +++ b/WorkFlow.API/Program.cs @@ -72,7 +72,7 @@ try { var clientParams = lazyProvider.GetRequiredService>()?.Value; var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience); - return [publicKey.SecurityKey]; + return new List() { publicKey.SecurityKey }; }, ValidateIssuer = true, ValidIssuer = authTokenKeys.Issuer, diff --git a/WorkFlow.API/WorkFlow.API.csproj b/WorkFlow.API/WorkFlow.API.csproj index 577e3de..c0730ea 100644 --- a/WorkFlow.API/WorkFlow.API.csproj +++ b/WorkFlow.API/WorkFlow.API.csproj @@ -1,7 +1,7 @@  - net8.0 + net7.0;net8.0 enable enable WorkFlow.API @@ -20,7 +20,7 @@ - +