From 54ae16ee71655665fcfef805a83fa5a015ef7946 Mon Sep 17 00:00:00 2001 From: antoineatstariongroup Date: Wed, 22 Jan 2025 16:26:23 +0100 Subject: [PATCH] UserName route logic implemented and added check empty header --- .../BasicAuthenticationHandlerTestFixture.cs | 5 +- ...tBearerAuthenticationHandlerTestFixture.cs | 22 +++- .../Basic/BasicAuthenticationHandler.cs | 10 ++ .../Bearer/JwtBearerAuthenticationHandler.cs | 18 +++ .../Authorization/ICredentialsService.cs | 2 + CometServer/Modules/10-25/ApiBase.cs | 2 +- .../Authentication/AuthenticationModule.cs | 13 ++- .../Modules/Authentication/UsernameModule.cs | 105 +++++++++++++++++- 8 files changed, 165 insertions(+), 12 deletions(-) diff --git a/CometServer.Tests/Authentication/Basic/BasicAuthenticationHandlerTestFixture.cs b/CometServer.Tests/Authentication/Basic/BasicAuthenticationHandlerTestFixture.cs index 65238488..a19e1d19 100644 --- a/CometServer.Tests/Authentication/Basic/BasicAuthenticationHandlerTestFixture.cs +++ b/CometServer.Tests/Authentication/Basic/BasicAuthenticationHandlerTestFixture.cs @@ -34,7 +34,6 @@ namespace CometServer.Tests.Authentication.Basic using CDP4Authentication; using CometServer.Authentication; - using CometServer.Authentication.Anonymous; using CometServer.Authentication.Basic; using CometServer.Configuration; using CometServer.Exceptions; @@ -143,7 +142,7 @@ public async Task VerifyAuthenticationFailsBasedOnConfig() } [Test] - public async Task VerifyAuthenticationNotSucceedNorFailedWithEmptyHeader() + public async Task VerifyAuthenticationFailsWithEmptyHeader() { var context = new DefaultHttpContext { @@ -160,7 +159,7 @@ public async Task VerifyAuthenticationNotSucceedNorFailedWithEmptyHeader() await this.handler.ChallengeAsync(result.Properties); - Assert.That(context.Response.StatusCode, Is.EqualTo(200)); + Assert.That(context.Response.StatusCode, Is.EqualTo(401)); } [Test] diff --git a/CometServer.Tests/Authentication/Bearer/JwtBearerAuthenticationHandlerTestFixture.cs b/CometServer.Tests/Authentication/Bearer/JwtBearerAuthenticationHandlerTestFixture.cs index e9d68d4a..31a0a6f3 100644 --- a/CometServer.Tests/Authentication/Bearer/JwtBearerAuthenticationHandlerTestFixture.cs +++ b/CometServer.Tests/Authentication/Bearer/JwtBearerAuthenticationHandlerTestFixture.cs @@ -35,7 +35,6 @@ namespace CometServer.Tests.Authentication.Bearer using CDP4Authentication; using CometServer.Authentication; - using CometServer.Authentication.Basic; using CometServer.Authentication.Bearer; using CometServer.Configuration; using CometServer.Health; @@ -228,5 +227,26 @@ public async Task VerifyAuthenticationSuccessWithValidToken() Assert.That(result.Succeeded, Is.True); } + + [Test] + public async Task VerifyAuthenticationFailsWithEmptyHeader() + { + var context = new DefaultHttpContext + { + RequestServices = this.requestService.Object + }; + + this.cometHasStartedService.Setup(x => x.GetHasStartedAndIsReady()).Returns(new ServerStatus(true, DateTime.Now)); + await this.handler.InitializeAsync(new AuthenticationScheme(JwtBearerDefaults.LocalAuthenticationScheme, "", typeof(JwtBearerAuthenticationHandler)), context); + + this.appConfigService.Setup(x => x.IsAuthenticationSchemeEnabled(this.handler.Scheme.Name)).Returns(true); + var result = await this.handler.AuthenticateAsync(); + + Assert.That(result.Succeeded, Is.False); + + await this.handler.ChallengeAsync(result.Properties); + + Assert.That(context.Response.StatusCode, Is.EqualTo(401)); + } } } diff --git a/CometServer/Authentication/Basic/BasicAuthenticationHandler.cs b/CometServer/Authentication/Basic/BasicAuthenticationHandler.cs index 3a806be2..64f0f297 100644 --- a/CometServer/Authentication/Basic/BasicAuthenticationHandler.cs +++ b/CometServer/Authentication/Basic/BasicAuthenticationHandler.cs @@ -144,6 +144,16 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop return; } + + var authorizationHeader = this.Request.Headers.Authorization; + + if (string.IsNullOrEmpty(authorizationHeader)) + { + this.Response.ContentType = "application/json"; + this.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + await this.Response.AsJson("Missing Authorization Header"); + return; + } if (!this.Request.DoesAuthorizationHeaderMatches(this.Scheme.Name)) { diff --git a/CometServer/Authentication/Bearer/JwtBearerAuthenticationHandler.cs b/CometServer/Authentication/Bearer/JwtBearerAuthenticationHandler.cs index 9ba5c1c8..8df5bb25 100644 --- a/CometServer/Authentication/Bearer/JwtBearerAuthenticationHandler.cs +++ b/CometServer/Authentication/Bearer/JwtBearerAuthenticationHandler.cs @@ -115,7 +115,17 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop return; } + + var authorizationHeader = this.Request.Headers.Authorization; + if (string.IsNullOrEmpty(authorizationHeader)) + { + this.Response.ContentType = "application/json"; + this.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + await this.Response.AsJson("Missing Authorization Header"); + return; + } + if (!this.Request.DoesAuthorizationHeaderMatches(this.Scheme.Name)) { return; @@ -143,6 +153,14 @@ protected override async Task HandleAuthenticateAsync() { return AuthenticateResult.NoResult(); } + + var authorizationHeader = this.Request.Headers.Authorization; + + if (string.IsNullOrEmpty(authorizationHeader)) + { + this.Logger.LogInformation("no Authorization header provided"); + return AuthenticateResult.NoResult(); + } if (!this.Request.DoesAuthorizationHeaderMatches(this.Scheme.Name)) { diff --git a/CometServer/Authorization/ICredentialsService.cs b/CometServer/Authorization/ICredentialsService.cs index 0fd7b289..0ad09957 100644 --- a/CometServer/Authorization/ICredentialsService.cs +++ b/CometServer/Authorization/ICredentialsService.cs @@ -27,6 +27,8 @@ namespace CometServer.Authorization using System; using System.Threading.Tasks; + using CDP4Common.DTO; + using Npgsql; /// diff --git a/CometServer/Modules/10-25/ApiBase.cs b/CometServer/Modules/10-25/ApiBase.cs index 114749cb..93c9b55a 100644 --- a/CometServer/Modules/10-25/ApiBase.cs +++ b/CometServer/Modules/10-25/ApiBase.cs @@ -1,4 +1,4 @@ -// -------------------------------------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) 2015-2024 Starion Group S.A. // diff --git a/CometServer/Modules/Authentication/AuthenticationModule.cs b/CometServer/Modules/Authentication/AuthenticationModule.cs index 73d0a72d..0dba72ac 100644 --- a/CometServer/Modules/Authentication/AuthenticationModule.cs +++ b/CometServer/Modules/Authentication/AuthenticationModule.cs @@ -1,4 +1,4 @@ -// -------------------------------------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) 2015-2024 Starion Group S.A. // @@ -26,6 +26,7 @@ namespace CometServer.Modules { using System; using System.Linq; + using System.Net; using System.Threading.Tasks; using Carter; @@ -52,8 +53,16 @@ public class AuthenticationModule : CarterModule /// public override void AddRoutes(IEndpointRouteBuilder app) { - app.MapPost("/login", async (LoginUser loginUser, HttpResponse res, IAuthenticationPersonAuthenticator authenticationPersonAuthenticator, IJwtTokenService jwtTokenService) => + app.MapPost("/login", async (LoginUser loginUser, HttpResponse res, IAuthenticationPersonAuthenticator authenticationPersonAuthenticator, IJwtTokenService jwtTokenService + , IAppConfigService appConfigService) => { + if (!appConfigService.IsAuthenticationSchemeEnabled(JwtBearerDefaults.LocalAuthenticationScheme)) + { + res.StatusCode = (int)HttpStatusCode.Forbidden; + await res.WriteAsync("Local JWT Authentication is disable."); + return; + } + var authenticationPerson = await authenticationPersonAuthenticator.Authenticate(loginUser.UserName, loginUser.Password); if (authenticationPerson != null) diff --git a/CometServer/Modules/Authentication/UsernameModule.cs b/CometServer/Modules/Authentication/UsernameModule.cs index 17a6c5ca..53637718 100644 --- a/CometServer/Modules/Authentication/UsernameModule.cs +++ b/CometServer/Modules/Authentication/UsernameModule.cs @@ -24,19 +24,96 @@ namespace CometServer.Modules { + using System; + using System.Linq; + using System.Net; + using System.Security.Claims; + using Carter; + using Carter.Response; + + using CDP4DalCommon.Tasks; - using CometServer.Authentication.Basic; + using CDP4ServicesMessaging.Services.BackgroundMessageProducers; + + using CometServer.Authentication.Bearer; + using CometServer.Authorization; + using CometServer.Configuration; + using CometServer.Enumerations; + using CometServer.Exceptions; + using CometServer.Extensions; + using CometServer.Health; + using CometServer.Services; + using CometServer.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Logging.Abstractions; + + using Npgsql; /// /// handle request on the logged-in users /// - public class UsernameModule : CarterModule + public class UsernameModule : ApiBase { + /// + /// The (injected) instance + /// + private readonly ILogger logger; + + /// + /// Initializes a new instance of the class + /// + /// + /// The (injected) + /// + /// + /// The (injected) that is used to check whether CDP4-COMET is ready to start + /// acceptng requests + /// + /// + /// The (injected) used generate HTTP request tokens + /// + /// + /// The (injected) used to create typed loggers + /// + /// + /// The (injected) used to schedule things messages to be sent + /// + /// + /// The (injected) used to register and access running s + /// + public UsernameModule(IAppConfigService appConfigService, ICometHasStartedService cometHasStartedService, ITokenGeneratorService tokenGeneratorService, ILoggerFactory loggerFactory, IBackgroundThingsMessageProducer thingsMessageProducer, ICometTaskService cometTaskService) : + base(appConfigService, cometHasStartedService, tokenGeneratorService, loggerFactory, thingsMessageProducer, cometTaskService) + { + this.logger = loggerFactory == null ? NullLogger.Instance : loggerFactory.CreateLogger(); + } + + /// + /// Initializes a new instance of the class + /// + /// + /// The (injected) + /// + /// + /// The (injected) that is used to determine whether the servre + /// is ready to accept incoming requests + /// + /// + /// The (injected) used generate HTTP request tokens + /// + /// + /// The (injected) used to create typed loggers + /// + public UsernameModule(IAppConfigService appConfigService, ICometHasStartedService cometHasStartedService, ITokenGeneratorService tokenGeneratorService, ILoggerFactory loggerFactory) : + base(appConfigService, cometHasStartedService, tokenGeneratorService, loggerFactory) + { + this.logger = loggerFactory == null ? NullLogger.Instance : loggerFactory.CreateLogger(); + } + /// /// Add the routes to the /// @@ -45,10 +122,28 @@ public class UsernameModule : CarterModule /// public override void AddRoutes(IEndpointRouteBuilder app) { - app.MapGet("/username", async (HttpRequest req, HttpResponse res) => + app.MapGet("/username", async (HttpRequest req, HttpResponse res, IAppConfigService appConfigService, ICredentialsService credentialsService) => { - await res.WriteAsync(res.HttpContext.User.Identity.Name); - }).RequireAuthorization(new[] { BasicAuthenticationDefaults.AuthenticationScheme }); + if (!await this.IsServerReady(res)) + { + return; + } + + var identity = req.HttpContext.User.Identity!.Name; + + try + { + identity = await this.Authorize(appConfigService, credentialsService, req); + await res.WriteAsync(credentialsService.Credentials.UserName); + } + catch (AuthorizationException) + { + this.logger.LogWarning("The GET UserName was not authorized for {Identity}", identity); + + res.UpdateWithNotAutherizedSettings(); + await res.AsJson("not authorized"); + } + }).RequireAuthorization(ApiBase.AuthenticationSchemes); } } } \ No newline at end of file