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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|