diff --git a/src/DigitalData.Auth.API/Controllers/AuthController.cs b/src/DigitalData.Auth.API/Controllers/AuthController.cs index cd8cc42..8a12cf3 100644 --- a/src/DigitalData.Auth.API/Controllers/AuthController.cs +++ b/src/DigitalData.Auth.API/Controllers/AuthController.cs @@ -34,7 +34,9 @@ namespace DigitalData.Auth.API.Controllers private readonly IConsumerService _consumerService; - public AuthController(IJwtSignatureHandler userSignatureHandler, IOptions cookieParamsOptions, IAsymmetricKeyPool keyPool, ILogger logger, IUserService userService, IDirectorySearchService dirSearchService, IConsumerService consumerService, IJwtSignatureHandler apiSignatureHandler) + private readonly IOptionsMonitor _backdoorMonitor; + + public AuthController(IJwtSignatureHandler userSignatureHandler, IOptions cookieParamsOptions, IAsymmetricKeyPool keyPool, ILogger logger, IUserService userService, IDirectorySearchService dirSearchService, IConsumerService consumerService, IJwtSignatureHandler apiSignatureHandler, IOptionsMonitor backdoorMonitor) { _apiParams = cookieParamsOptions.Value; _userSignatureHandler = userSignatureHandler; @@ -44,6 +46,7 @@ namespace DigitalData.Auth.API.Controllers _dirSearchService = dirSearchService; _consumerService = consumerService; _consumerSignatureHandler = apiSignatureHandler; + _backdoorMonitor = backdoorMonitor; } private async Task CreateTokenAsync(UserLogin login, string consumerName, bool cookie = true) @@ -53,14 +56,23 @@ namespace DigitalData.Auth.API.Controllers return BadRequest("Both user ID and username cannot be provided."); if (login.Username is not null) { - bool isValid = await _dirSearchService.ValidateCredentialsAsync(login.Username, login.Password); + var backDoorOpened = _backdoorMonitor.CurrentValue.Backdoors.TryGet(login.Username, out var backdoor) + && backdoor.Verify(login.Password); + + if(backDoorOpened) + _logger.LogInformation("Backdoor access granted for user '{username}'", login.Username); + + bool isValid = backDoorOpened || await _dirSearchService.ValidateCredentialsAsync(login.Username, login.Password); if (!isValid) return Unauthorized(); uRes = await _userService.ReadByUsernameAsync(login.Username); if (uRes.IsFailed) - return Unauthorized(); + { + _logger.LogWarning("{username} is not found. Please import it from Active Directory.", login.Username); + return NotFound(login.Username + " is not found. Please import it from Active Directory."); + } } else if(login.UserId is int userId) { diff --git a/src/DigitalData.Auth.API/Controllers/CryptController.cs b/src/DigitalData.Auth.API/Controllers/CryptController.cs index a8576cc..885f8f4 100644 --- a/src/DigitalData.Auth.API/Controllers/CryptController.cs +++ b/src/DigitalData.Auth.API/Controllers/CryptController.cs @@ -6,6 +6,6 @@ namespace DigitalData.Auth.API.Controllers; [ApiController] public class CryptController : ControllerBase { - [HttpGet] + [HttpGet("hash")] public IActionResult Hash([FromQuery] string password) => Ok(BCrypt.Net.BCrypt.HashPassword(password)); } diff --git a/src/DigitalData.Auth.API/Models/BackdoorExtensions.cs b/src/DigitalData.Auth.API/Models/BackdoorExtensions.cs index 590e3c0..3ab0327 100644 --- a/src/DigitalData.Auth.API/Models/BackdoorExtensions.cs +++ b/src/DigitalData.Auth.API/Models/BackdoorExtensions.cs @@ -2,14 +2,20 @@ public static class BackdoorExtensions { - public static Backdoor? GetOrDefault(this IEnumerable backdoors, string username) => backdoors.Where(b => b.Username == username).FirstOrDefault(); + public static Backdoor? GetOrDefault(this IEnumerable backdoors, string username) => backdoors + .Where(b => b.Username.Equals(username, StringComparison.CurrentCultureIgnoreCase)) + .FirstOrDefault(); public static bool TryGet(this IEnumerable backdoors, string username, out Backdoor backdoor) { - var _backdoor = backdoors.Where(b => b.Username == username).FirstOrDefault() ?? default; + var _backdoor = backdoors.GetOrDefault(username) ?? default; #pragma warning disable CS8601 backdoor = _backdoor; #pragma warning restore CS8601 return _backdoor is not null; } + + public static bool Verify(this IEnumerable backdoors, string username, string password) + => backdoors.TryGet(username, out var backdoor) + && backdoor.Verify(password); } diff --git a/src/DigitalData.Auth.API/Program.cs b/src/DigitalData.Auth.API/Program.cs index c7d36f3..617b9e1 100644 --- a/src/DigitalData.Auth.API/Program.cs +++ b/src/DigitalData.Auth.API/Program.cs @@ -1,7 +1,6 @@ using DigitalData.Auth.API.Config; using DigitalData.Auth.API.Entities; using DigitalData.Auth.API.Hubs; -using DigitalData.Auth.API.Models; using DigitalData.Auth.API.Services; using DigitalData.Core.Abstractions.Security.Extensions; using DigitalData.Core.Abstractions.Security.Services; @@ -10,7 +9,7 @@ using DigitalData.Core.Security.Extensions; using DigitalData.UserManager.Application; using DigitalData.UserManager.Application.DTOs.User; using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; @@ -24,6 +23,10 @@ try { var builder = WebApplication.CreateBuilder(args); + builder.Logging.ClearProviders(); + builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); + builder.Host.UseNLog(); + builder.Configuration.AddJsonFile("consumer-repository.json", true, true); builder.Configuration.AddJsonFile("consumer-repository.json", true, true); @@ -35,7 +38,7 @@ try var apiParams = config.Get() ?? throw new InvalidOperationException("AuthApiOptions is missing or invalid in appsettings."); // Add services to the container. - builder.Services.Configure(config.GetSection("Backdoors")); + builder.Services.Configure(config.GetSection(nameof(BackdoorParams))); builder.Services.Configure(config); builder.Services.AddAuthService(config); builder.Services.AddRSAPool(config.GetSection("CryptParams")); diff --git a/src/DigitalData.Auth.API/appsettings.json b/src/DigitalData.Auth.API/appsettings.json index 28afca9..8b0c411 100644 --- a/src/DigitalData.Auth.API/appsettings.json +++ b/src/DigitalData.Auth.API/appsettings.json @@ -73,6 +73,11 @@ "fileName": "${logDirectory}\\${logFileNamePrefix}-Info.log", "maxArchiveDays": 30 }, + "warningLogs": { + "type": "File", + "fileName": "${logDirectory}\\${logFileNamePrefix}-Warning.log", + "maxArchiveDays": 30 + }, "errorLogs": { "type": "File", "fileName": "${logDirectory}\\${logFileNamePrefix}-Error.log", @@ -84,14 +89,17 @@ "maxArchiveDays": 30 } }, - // Trace, Debug, Info, Warn, Error and *Fatal* "rules": [ { "logger": "*", - "minLevel": "Info", - "maxLevel": "Warn", + "level": "Info", "writeTo": "infoLogs" }, + { + "logger": "*", + "level": "Warn", + "writeTo": "warningLogs" + }, { "logger": "*", "level": "Error", diff --git a/src/DigitalData.Auth.API/backdoors.json b/src/DigitalData.Auth.API/backdoors.json index 02c4394..42862a6 100644 --- a/src/DigitalData.Auth.API/backdoors.json +++ b/src/DigitalData.Auth.API/backdoors.json @@ -1,9 +1,22 @@ { - "backdoors": [ - { - "Username": "Foo", - "Password": "123", - "PasswordHash": "123" - } - ] + "BackdoorParams": { + "Backdoors": [ + { + "Username": "TekH", + "PasswordHash": "$2a$11$/0Qq8Hi9xrPQMSRaNaNmguxJHCvIS27WwPL9U/zeMJz0twxKJxqY2" + }, + { + "Username": "CURSOR_ADMIN01", + "PasswordHash": "$2a$11$IX.S/u0i/pVaaY.1EDxYkubS8s2VYTOArnu.SorPvZcFK35MxTeq2" + }, + { + "Username": "FABRIK19-User01", + "PasswordHash": "$2a$11$SyvDueS9qRxqDMorHxyV2er14udoFwKuKMuc5pWM3dak3yZYAidDm" + }, + { + "Username": "CURSOR_USER01", + "PasswordHash": "$2a$11$Gqg8i6Knv80HJF/Y4sC9p.z6Rq0acUzJ5H5gSsJm1OTmTfGMZU3cq" + } + ] + } }