using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Options; using PARR.API.Services.Interfaces; using System.Security.Claims; using System.Text.Encodings.Web; namespace PARR.API.Authentication { public class ParrAuthenticationHandler : AuthenticationHandler { private readonly IServiceProvider serviceProvider; public ParrAuthenticationHandler( IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IServiceProvider serviceProvider) : base(options, logger, encoder, clock) { this.serviceProvider = serviceProvider; } protected override async Task HandleAuthenticateAsync() { var ipClient = Request.HttpContext.Connection.RemoteIpAddress?.MapToIPv4().ToString(); if (ipClient == null) return AuthenticateResult.Fail($"IP address not defined. IP: {ipClient}"); // Аутентификация - просто проверка, есть ли у нас такой пользователь using (var scope = serviceProvider.CreateScope()) { var authService = scope.ServiceProvider.GetRequiredService(); var userIsBlocked = await authService.UserIsBlockedAsync(ipClient); if (userIsBlocked) return AuthenticateResult.Fail($"IP address is on the blocking list. IP: {ipClient}"); // если зашли с IP 127.0.0.1, то это скорей всего опрос состояния контейнера // если этот пользователь не заведен в БД, то разрешаем ему вход, но роли не даем (он получит доступ как аноним) // если он есть в БД, то применятся правила из БД var user = await authService.GetUserAsync(ipClient); if (user == null && ipClient == "127.0.0.1") { // это опрос состояния контейнера, возвращаем что ок, без ролей return CreateLocalAuth(ipClient); } if (user == null) return AuthenticateResult.Fail($"IP address not defined. IP: {ipClient}"); // пользователь найден, аутентификация пройдена var claims = new List { new Claim(ClaimTypes.Name, user.UserIp) }; // добавляем роли user.Roles.ForEach(r => claims.Add(new Claim(ClaimTypes.Role, r.Name))); var claimsIdentity = new ClaimsIdentity(claims, Scheme.Name); var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); return AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, Scheme.Name)); } } private AuthenticateResult CreateLocalAuth(string ipClient) { // Когда контейнер запрашивает health статус, var claims = new List { new Claim(ClaimTypes.Name, ipClient) }; var claimsIdentity = new ClaimsIdentity(claims, Scheme.Name); var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); return AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, Scheme.Name)); } } }