sozsoft-platform/api/src/Sozsoft.Platform.HttpApi.Host/Identity/PlatformSignInManager.cs

254 lines
8.2 KiB
C#
Raw Normal View History

2026-02-24 20:44:16 +00:00
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
using Sozsoft.Platform.Entities;
using Sozsoft.Platform.Extensions;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using Volo.Abp.Identity.AspNetCore;
using Volo.Abp.Settings;
using Volo.Abp.TenantManagement;
using Volo.Abp.Timing;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
using SignInResult = Microsoft.AspNetCore.Identity.SignInResult;
namespace Sozsoft.Platform.Identity;
public interface IPlatformSignInManager
{
Task<bool> PreSignInCheckAsync(IdentityUser user);
}
public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
{
private readonly IClock clock;
private readonly IRepository<IpRestriction, Guid> repositoryIp;
2026-03-11 13:23:49 +00:00
private readonly IRepository<WorkHour, Guid> repositoryWorkHour;
2026-02-24 20:44:16 +00:00
private readonly ITenantRepository tenantRepository;
private readonly IdentityUserManager userManager;
public PlatformSignInManager(
IdentityUserManager userManager,
IHttpContextAccessor contextAccessor,
IUserClaimsPrincipalFactory<IdentityUser> claimsFactory,
IOptions<IdentityOptions> optionsAccessor,
ILogger<SignInManager<IdentityUser>> logger,
IAuthenticationSchemeProvider schemes,
IUserConfirmation<IdentityUser> confirmation,
IOptions<AbpIdentityOptions> options,
ISettingProvider settingProvider,
IClock clock,
IRepository<IpRestriction, Guid> repositoryIp,
2026-03-11 13:23:49 +00:00
IRepository<WorkHour, Guid> repositoryWorkHour,
2026-02-24 20:44:16 +00:00
ITenantRepository tenantRepository
) : base(
userManager,
contextAccessor,
claimsFactory,
optionsAccessor,
logger,
schemes,
confirmation,
options,
settingProvider)
{
this.clock = clock;
this.repositoryIp = repositoryIp;
2026-03-11 13:23:49 +00:00
this.repositoryWorkHour = repositoryWorkHour;
2026-02-24 20:44:16 +00:00
this.tenantRepository = tenantRepository;
this.userManager = userManager;
}
public async Task<bool> PreSignInCheckAsync(IdentityUser user)
{
var result = await PreSignInCheck(user);
return result?.Succeeded ?? true;
}
protected override async Task<SignInResult> PreSignInCheck(IdentityUser user)
{
var result = await base.PreSignInCheck(user);
if (result == null)
{
if (!await CanSignInVerifiedAsync(user))
{
return new PlatformSignInResult() { IsNotAllowed_NotVerified = true };
}
if (!CanSignInEndDate(user))
{
return new PlatformSignInResult() { IsNotAllowed_LoginEndDateDue = true };
}
if (!await CanSignInIpAsync(user))
{
return new PlatformSignInResult() { IsNotAllowed_NotAllowedIp = true };
}
2026-03-11 13:23:49 +00:00
if (!await CanSignInTenantActiveAsync(user))
2026-02-24 20:44:16 +00:00
{
return new PlatformSignInResult() { IsNotAllowed_TenantIsPassive = true };
}
2026-03-11 13:23:49 +00:00
if (!await CanSignInWorkHourAsync())
{
return new PlatformSignInResult() { IsNotAllowed_WorkHour = true };
}
2026-02-24 20:44:16 +00:00
}
else
{
// Bu kontrol modules\identity\src\Volo.Abp.Identity.AspNetCore\Volo\Abp\Identity\AspNetCore\AbpSignInManager.cs
// içinde zaten yapılıyor fakat her senaryoya NotAllowed diyor, bizim ayrıt etmemiz gerektiği için tekrar yapıyoruz.
if (user.ShouldChangePasswordOnNextLogin)
{
return new PlatformSignInResult() { ShouldChangePasswordOnNextLogin = true };
}
if (await userManager.ShouldPeriodicallyChangePasswordAsync(user))
{
return new PlatformSignInResult() { ShouldChangePasswordPeriodic = true };
}
}
return result;
}
2026-03-11 13:23:49 +00:00
/// <summary>
/// Used to prevent login outside of defined work hours.
/// </summary>
private async Task<bool> CanSignInWorkHourAsync()
{
var workHours = await repositoryWorkHour.GetListAsync();
if (workHours.IsNullOrEmpty())
{
return true;
}
var now = clock.Now;
var currentTime = now.TimeOfDay;
var dayOfWeek = now.DayOfWeek;
var isAllowed = workHours.Any(wh =>
{
var dayMatches = dayOfWeek switch
{
DayOfWeek.Monday => wh.Monday == true,
DayOfWeek.Tuesday => wh.Tuesday == true,
DayOfWeek.Wednesday => wh.Wednesday == true,
DayOfWeek.Thursday => wh.Thursday == true,
DayOfWeek.Friday => wh.Friday == true,
DayOfWeek.Saturday => wh.Saturday == true,
DayOfWeek.Sunday => wh.Sunday == true,
_ => false
};
if (!dayMatches) return false;
return currentTime >= wh.StartTime.TimeOfDay && currentTime <= wh.EndTime.TimeOfDay;
});
if (!isAllowed)
{
Logger.LogWarning(PlatformEventIds.UserCannotSignInWorkHour, "User cannot sign in outside work hours.");
return false;
}
return true;
}
2026-02-24 20:44:16 +00:00
/// <summary>
/// Tenant IsActive
/// </summary>
2026-03-11 13:23:49 +00:00
private async Task<bool> CanSignInTenantActiveAsync(IdentityUser user)
2026-02-24 20:44:16 +00:00
{
if (!user.TenantId.HasValue)
{
return true;
}
var tenant = await tenantRepository.FindAsync(user.TenantId.Value);
if (tenant == null)
{
return false;
}
if (!tenant.GetIsActive())
{
Logger.LogWarning(PlatformEventIds.UserCannotSignInTenantIsPassive, "Tenant is inactive.");
return false;
}
return true;
}
/// <summary>
/// Used to prevent login for additional scenarios;
/// Account Not Verified
/// </summary>
private async Task<bool> CanSignInVerifiedAsync(IdentityUser user)
{
var rva = await SettingProvider.GetAsync<bool>(PlatformConsts.AbpIdentity.Profile.General.RequireVerifiedAccount);
if (rva && !user.GetIsVerified())
{
Logger.LogWarning(PlatformEventIds.UserCannotSignInWithoutVerifiedAccount, "User cannot sign in without a verified account.");
return false;
}
return true;
}
/// <summary>
/// Used to prevent login if end date is due
/// </summary>
private bool CanSignInEndDate(IdentityUser user)
{
var endDate = user.GetLoginEndDate();
if (endDate != null && clock.Now > endDate)
{
//izin yok
Logger.LogWarning(PlatformEventIds.UserCannotSignInLoginEndDateDue, "User cannot sign in, login end date is due.");
return false;
}
//izin ver
return true;
}
/// <summary>
/// Used to prevent login for allowed IPs;
/// IP Not Allowed
/// </summary>
private async Task<bool> CanSignInIpAsync(IdentityUser user)
{
// User, Role, Global Restrictions
var roles = await UserManager.GetRolesAsync(user);
//var roleIds = roleManager.Roles.Where(a => roles.Contains(a.Name)).Select(a => a.Id).ToArray();
var restrictions = await repositoryIp.GetListAsync(a =>
(a.ResourceType == "User" && a.ResourceId == user.UserName) ||
(a.ResourceType == "Role" && roles.Contains(a.ResourceId)) ||
(a.ResourceType == "Global"));
if (!restrictions.IsNullOrEmpty())
{
var ip = Context.GetRemoteIPAddress();
var ipString = ip?.ToString();
if (ip == null || (ip != null && !restrictions.Select(a => a.IP).Any(a => a == ipString)))
{
//izin yok
Logger.LogWarning(PlatformEventIds.UserCannotSignInWithoutAllowedIp, "User cannot sign in without an allowed IP.");
return false;
}
}
//izin ver
return true;
}
}