2026-08-21 19:27:59 +00:00
|
|
|
|
using System;
|
2026-06-19 08:49:49 +00:00
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using Microsoft.AspNetCore.Http;
|
2026-08-21 19:27:59 +00:00
|
|
|
|
using Microsoft.Data.SqlClient;
|
2026-08-09 08:52:28 +00:00
|
|
|
|
using Microsoft.Extensions.Configuration;
|
|
|
|
|
|
using Sozsoft.Platform.DbStartup;
|
2026-06-19 08:49:49 +00:00
|
|
|
|
using Volo.Abp.DependencyInjection;
|
|
|
|
|
|
using Volo.Abp.Identity;
|
|
|
|
|
|
using Volo.Abp.MultiTenancy;
|
|
|
|
|
|
using Volo.Abp.Security.Claims;
|
|
|
|
|
|
using Volo.Abp.Uow;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Sozsoft.Platform.Identity;
|
|
|
|
|
|
|
|
|
|
|
|
public class PlatformSessionValidationMiddleware : IMiddleware, ITransientDependency
|
|
|
|
|
|
{
|
2026-08-09 08:52:28 +00:00
|
|
|
|
private const string SetupPathPrefix = "/api/setup";
|
|
|
|
|
|
private const string SetupStatusPath = "/api/setup/application-status";
|
|
|
|
|
|
|
2026-06-19 08:49:49 +00:00
|
|
|
|
private readonly IIdentitySessionRepository sessionRepository;
|
|
|
|
|
|
private readonly ICurrentTenant currentTenant;
|
|
|
|
|
|
private readonly IUnitOfWorkManager unitOfWorkManager;
|
2026-08-09 08:52:28 +00:00
|
|
|
|
private readonly IConfiguration configuration;
|
2026-06-19 08:49:49 +00:00
|
|
|
|
|
|
|
|
|
|
public PlatformSessionValidationMiddleware(
|
|
|
|
|
|
IIdentitySessionRepository sessionRepository,
|
|
|
|
|
|
ICurrentTenant currentTenant,
|
2026-08-09 08:52:28 +00:00
|
|
|
|
IUnitOfWorkManager unitOfWorkManager,
|
|
|
|
|
|
IConfiguration configuration)
|
2026-06-19 08:49:49 +00:00
|
|
|
|
{
|
|
|
|
|
|
this.sessionRepository = sessionRepository;
|
|
|
|
|
|
this.currentTenant = currentTenant;
|
|
|
|
|
|
this.unitOfWorkManager = unitOfWorkManager;
|
2026-08-09 08:52:28 +00:00
|
|
|
|
this.configuration = configuration;
|
2026-06-19 08:49:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
|
|
|
|
|
|
{
|
2026-08-09 08:52:28 +00:00
|
|
|
|
if (ShouldSkipSessionValidation(context))
|
2026-06-29 09:37:52 +00:00
|
|
|
|
{
|
|
|
|
|
|
await next(context);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-19 08:49:49 +00:00
|
|
|
|
if (context.User.Identity?.IsAuthenticated != true)
|
|
|
|
|
|
{
|
|
|
|
|
|
await next(context);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var userIdValue = context.User.FindFirst(AbpClaimTypes.UserId)?.Value;
|
|
|
|
|
|
var sessionId = context.User.FindFirst(PlatformClaimTypes.SessionId.Key)?.Value;
|
|
|
|
|
|
|
|
|
|
|
|
if (!Guid.TryParse(userIdValue, out var userId) || sessionId.IsNullOrWhiteSpace())
|
|
|
|
|
|
{
|
|
|
|
|
|
await next(context);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var tenantId = ParseTenantId(context.User.FindFirst(AbpClaimTypes.TenantId)?.Value);
|
|
|
|
|
|
using (currentTenant.Change(tenantId))
|
|
|
|
|
|
{
|
2026-06-29 09:37:52 +00:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
using var uow = unitOfWorkManager.Begin(requiresNew: true, isTransactional: false);
|
|
|
|
|
|
var sessions = await sessionRepository.GetListAsync(userId: userId);
|
|
|
|
|
|
var isCurrentSession = sessions.Any(session => session.SessionId == sessionId);
|
|
|
|
|
|
await uow.CompleteAsync();
|
2026-06-19 08:49:49 +00:00
|
|
|
|
|
2026-06-29 09:37:52 +00:00
|
|
|
|
if (!isCurrentSession)
|
|
|
|
|
|
{
|
|
|
|
|
|
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex) when (IsDatabaseUnavailableException(ex))
|
2026-06-19 08:49:49 +00:00
|
|
|
|
{
|
2026-06-29 09:37:52 +00:00
|
|
|
|
context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
|
2026-06-19 08:49:49 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await next(context);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-09 08:52:28 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Kurulum endpointleri için oturum doğrulaması ne zaman atlanır.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - Durum sorgusu: anonim erişime açık, oturum bilgisine hiç ihtiyaç duymaz.
|
|
|
|
|
|
/// - Diğer /api/setup istekleri: yalnızca veritabanı erişilemezken atlanır. O durumda
|
|
|
|
|
|
/// oturum zaten doğrulanamaz (kayıtlar veritabanında) ve doğrulamaya çalışmak, tam da
|
|
|
|
|
|
/// veritabanını onaracak endpoint'i 503 ile bloke eder. Güvenlik kaybı yoktur: veritabanı
|
|
|
|
|
|
/// düşükken migration yalnızca kurulum parolasıyla çalışır, oturum yetki vermez
|
|
|
|
|
|
/// (bkz. SetupController.AuthorizeMigrateRequestAsync).
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Veritabanı ayaktayken kurulum endpointleri de normal oturum doğrulamasından geçer;
|
|
|
|
|
|
/// böylece iptal edilmiş bir oturum migration tetikleyemez.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private bool ShouldSkipSessionValidation(HttpContext context)
|
|
|
|
|
|
{
|
|
|
|
|
|
var path = context.Request.Path;
|
|
|
|
|
|
|
|
|
|
|
|
if (!path.StartsWithSegments(SetupPathPrefix))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return path.StartsWithSegments(SetupStatusPath) ||
|
|
|
|
|
|
!DatabaseReadinessChecker.GetStatus(configuration).IsReady;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-19 08:49:49 +00:00
|
|
|
|
private static Guid? ParseTenantId(string value)
|
|
|
|
|
|
{
|
|
|
|
|
|
return !string.IsNullOrWhiteSpace(value) && Guid.TryParse(value, out var id) ? id : null;
|
|
|
|
|
|
}
|
2026-06-29 09:37:52 +00:00
|
|
|
|
|
|
|
|
|
|
private static bool IsDatabaseUnavailableException(Exception exception)
|
|
|
|
|
|
{
|
|
|
|
|
|
return exception is SqlException ||
|
|
|
|
|
|
exception is TimeoutException ||
|
|
|
|
|
|
exception is TaskCanceledException ||
|
|
|
|
|
|
exception is InvalidOperationException && exception.InnerException != null &&
|
|
|
|
|
|
IsDatabaseUnavailableException(exception.InnerException);
|
|
|
|
|
|
}
|
2026-06-19 08:49:49 +00:00
|
|
|
|
}
|