chore(API): Hinzufügen von .net 7-Unterstützung für API

This commit is contained in:
Developer 02 2025-03-13 10:12:55 +01:00
parent 10b557374d
commit fb38bc1fd4
11 changed files with 216 additions and 174 deletions

View File

@ -6,13 +6,15 @@ using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.Config; using WorkFlow.Application.DTO.Config;
using WorkFlow.Domain.Entities; using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ConfigController : CRUDControllerBaseWithErrorHandling<IConfigService, ConfigCreateDto, ConfigDto, ConfigUpdateDto, Config, int>
{ {
[APIKeyAuth] public ConfigController(ILogger<ConfigController> logger, IConfigService service) : base(logger, service)
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ConfigController(ILogger<ConfigController> logger, IConfigService service) : CRUDControllerBaseWithErrorHandling<IConfigService, ConfigCreateDto, ConfigDto, ConfigUpdateDto, Config, int>(logger, service)
{ {
} }
} }

View File

@ -6,13 +6,15 @@ using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.Profile; using WorkFlow.Application.DTO.Profile;
using WorkFlow.Domain.Entities; using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileController : CRUDControllerBaseWithErrorHandling<IProfileService, ProfileCreateDto, ProfileDto, ProfileUpdateDto, Profile, int>
{ {
[APIKeyAuth] public ProfileController(ILogger<ProfileController> logger, IProfileService service) : base(logger, service)
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileController(ILogger<ProfileController> logger, IProfileService service) : CRUDControllerBaseWithErrorHandling<IProfileService, ProfileCreateDto, ProfileDto, ProfileUpdateDto, Profile, int>(logger, service)
{ {
} }
} }

View File

@ -7,114 +7,120 @@ using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.ProfileControlsTF; using WorkFlow.Application.DTO.ProfileControlsTF;
using WorkFlow.Domain.Entities; using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileControlsTFController : CRUDControllerBase<IProfileControlsTFService, ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTFUpdateDto, ProfileControlsTF, int>
{ {
[APIKeyAuth] private readonly ILogger<ProfileControlsTFController> logger;
[Route("api/[controller]")]
[ApiController] public ProfileControlsTFController(ILogger<ProfileControlsTFController> logger, IProfileControlsTFService service) : base(logger, service)
[Authorize]
public class ProfileControlsTFController(ILogger<ProfileControlsTFController> logger, IProfileControlsTFService service) : CRUDControllerBase<IProfileControlsTFService, ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTFUpdateDto, ProfileControlsTF, int>(logger, service)
{ {
[NonAction] this.logger = logger;
public override Task<IActionResult> GetAll() => base.GetAll(); }
[NonAction] [NonAction]
public override Task<IActionResult> Update(ProfileControlsTFUpdateDto updateDto) => base.Update(updateDto); public override Task<IActionResult> GetAll() => base.GetAll();
[HttpGet] [NonAction]
public async Task<IActionResult> GetAsync( public override Task<IActionResult> Update(ProfileControlsTFUpdateDto updateDto) => base.Update(updateDto);
bool withProfile = true, bool withUser = false,
int? profileId = null, int? objId = null, bool? profileActive = null) [HttpGet]
public async Task<IActionResult> 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.");
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();
});
} }
catch (Exception ex) else if (id is null)
{ {
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message); logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred."); return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
} }
}
[HttpPost] return await _service.ReadAsync(
public override async Task<IActionResult> Create([FromBody] ProfileControlsTFCreateDto createDto) withProfile: withProfile, withUser: withUser,
{ userId: id,
try profileId: profileId, objId: objId, profileActive: profileActive)
{ .ThenAsync(
if (!this.TryGetUserId(out int? id)) Success: pctf => pctf.Any() ? Ok(pctf) : NotFound(),
{
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<IActionResult> 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) => Fail: IActionResult (msg, ntc) =>
{ {
_logger.LogNotice(ntc); logger.LogNotice(ntc);
return ntc.HasFlag(Flag.NotFound) ? NotFound() : StatusCode(StatusCodes.Status500InternalServerError); 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<IActionResult> 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); logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred."); 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<IActionResult> 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.");
} }
} }
} }

View File

@ -13,8 +13,15 @@ namespace WorkFlow.API.Controllers
[Route("api/[controller]")] [Route("api/[controller]")]
[ApiController] [ApiController]
[Authorize] [Authorize]
public class ProfileObjStateController(ILogger<ProfileObjStateController> logger, IProfileObjStateService service) : CRUDControllerBaseWithErrorHandling<IProfileObjStateService, ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjStateUpdateDto, ProfileObjState, int>(logger, service) public class ProfileObjStateController : CRUDControllerBaseWithErrorHandling<IProfileObjStateService, ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjStateUpdateDto, ProfileObjState, int>
{ {
private readonly ILogger<ProfileObjStateController> logger;
public ProfileObjStateController(ILogger<ProfileObjStateController> logger, IProfileObjStateService service) : base(logger, service)
{
this.logger = logger;
}
[NonAction] [NonAction]
public override Task<IActionResult> GetAll() => base.GetAll(); public override Task<IActionResult> GetAll() => base.GetAll();

View File

@ -6,13 +6,15 @@ using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.State; using WorkFlow.Application.DTO.State;
using WorkFlow.Domain.Entities; using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class StateController : CRUDControllerBaseWithErrorHandling<IStateService, StateCreateDto, StateDto, StateUpdateDto, State, int>
{ {
[APIKeyAuth] public StateController(ILogger<StateController> logger, IStateService service) : base(logger, service)
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class StateController(ILogger<StateController> logger, IStateService service) : CRUDControllerBaseWithErrorHandling<IStateService, StateCreateDto, StateDto, StateUpdateDto, State, int>(logger, service)
{ {
} }
} }

View File

@ -4,43 +4,51 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes; using WorkFlow.API.Attributes;
namespace WorkFlow.API.Controllers namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class UserController : ControllerBase
{ {
[APIKeyAuth] private readonly ILogger<UserController> logger;
[Route("api/[controller]")] private readonly IUserService userService;
[ApiController]
[Authorize] public UserController(ILogger<UserController> logger, IUserService userService)
public class UserController(ILogger<UserController> logger, IUserService userService) : ControllerBase
{ {
[HttpGet] this.logger = logger;
public async Task<IActionResult> GetAsync() this.userService = userService;
}
[HttpGet]
public async Task<IActionResult> 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.");
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.");
}
} }
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); logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred."); 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.");
}
} }
} }

View File

@ -4,33 +4,39 @@ using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
using WorkFlow.API.Models; using WorkFlow.API.Models;
namespace WorkFlow.API.Filters namespace WorkFlow.API.Filters;
public class APIKeyAuthHeaderOpFilter : IOperationFilter
{ {
public class APIKeyAuthHeaderOpFilter(IOptions<APIKeyAuthOptions> options, IWebHostEnvironment environment) : IOperationFilter private readonly APIKeyAuthOptions apiKeyAuthOptions;
private readonly IWebHostEnvironment environment;
public APIKeyAuthHeaderOpFilter(IOptions<APIKeyAuthOptions> 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, Type = "string"
In = ParameterLocation.Header, }
Required = true, };
AllowEmptyValue = false,
Schema = new OpenApiSchema
{
Type = "string"
}
};
if(environment.IsDevelopment()) if(environment.IsDevelopment())
param.Schema.Default = new OpenApiString(apiKeyAuthOptions.Key); param.Schema.Default = new OpenApiString(apiKeyAuthOptions.Key);
if (apiKeyAuthOptions.SwaggerDescription is not null) if (apiKeyAuthOptions.SwaggerDescription is not null)
param.Description = apiKeyAuthOptions.SwaggerDescription; param.Description = apiKeyAuthOptions.SwaggerDescription;
operation.Parameters.Add(param); operation.Parameters.Add(param);
}
} }
} }

View File

@ -1,14 +1,22 @@
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace WorkFlow.API.Filters namespace WorkFlow.API.Filters;
public class APIKeyAuthFilter : IAuthorizationFilter
{ {
public class APIKeyAuthFilter(Func<string?, bool> isValidKey, string headerName = "X-API-Key") : IAuthorizationFilter private readonly Func<string?, bool> isValidKey;
private readonly string headerName;
public APIKeyAuthFilter(Func<string?, bool> isValidKey, string headerName = "X-API-Key")
{ {
public void OnAuthorization(AuthorizationFilterContext context) this.isValidKey = isValidKey;
{ this.headerName = headerName;
if (!isValidKey(context.HttpContext.Request.Headers[headerName])) }
context.Result = new UnauthorizedResult();
} public void OnAuthorization(AuthorizationFilterContext context)
{
if (!isValidKey(context.HttpContext.Request.Headers[headerName]))
context.Result = new UnauthorizedResult();
} }
} }

View File

@ -5,13 +5,14 @@ namespace WorkFlow.API.Models
{ {
public static class ModelExtensions public static class ModelExtensions
{ {
public static List<Claim> ToClaimList(this UserReadDto user) => [ public static List<Claim> ToClaimList(this UserReadDto user) => new()
{
new (ClaimTypes.NameIdentifier, user.Id.ToString()), new (ClaimTypes.NameIdentifier, user.Id.ToString()),
new (ClaimTypes.Name, user.Username), new (ClaimTypes.Name, user.Username),
new (ClaimTypes.Surname, user.Name ?? ""), new (ClaimTypes.Surname, user.Name ?? ""),
new (ClaimTypes.GivenName, user.Prename ?? ""), new (ClaimTypes.GivenName, user.Prename ?? ""),
new (ClaimTypes.Email, user.Email ?? "") new (ClaimTypes.Email, user.Email ?? "")
]; };
public static Dictionary<string, object> ToClaimDictionary(this UserReadDto user) => user.ToClaimList().ToDictionary(claim => claim.Type, claim => (object) claim.Value); public static Dictionary<string, object> ToClaimDictionary(this UserReadDto user) => user.ToClaimList().ToDictionary(claim => claim.Type, claim => (object) claim.Value);
} }

View File

@ -72,7 +72,7 @@ try
{ {
var clientParams = lazyProvider.GetRequiredService<IOptions<ClientParams>>()?.Value; var clientParams = lazyProvider.GetRequiredService<IOptions<ClientParams>>()?.Value;
var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience); var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience);
return [publicKey.SecurityKey]; return new List<SecurityKey>() { publicKey.SecurityKey };
}, },
ValidateIssuer = true, ValidateIssuer = true,
ValidIssuer = authTokenKeys.Issuer, ValidIssuer = authTokenKeys.Issuer,

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<PackageId>WorkFlow.API</PackageId> <PackageId>WorkFlow.API</PackageId>
@ -20,7 +20,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DigitalData.Auth.Client" Version="1.3.3" /> <PackageReference Include="DigitalData.Auth.Client" Version="1.3.3" />
<PackageReference Include="DigitalData.Core.API" Version="2.1.1" /> <PackageReference Include="DigitalData.Core.API" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.13" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.20" />
<PackageReference Include="NLog" Version="5.3.4" /> <PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.14" /> <PackageReference Include="NLog.Web.AspNetCore" Version="5.3.14" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />