Claude güncellemesi Login.Result düzeltmeleri
This commit is contained in:
parent
b9550acda2
commit
2368ee3825
6 changed files with 65 additions and 27 deletions
|
|
@ -86,9 +86,11 @@ public class PlatformAccountController : AccountController
|
||||||
});
|
});
|
||||||
|
|
||||||
var result = new PlatformLoginResult(signInResult, LP);
|
var result = new PlatformLoginResult(signInResult, LP);
|
||||||
if (result.PResult != PlatformLoginResultType.Succeeded && user.AccessFailedCount >= maxAccessFailedCount)
|
|
||||||
|
// Captcha yalnızca gerçek parola hatasında gösterilir. Lisans limiti, tenant pasif,
|
||||||
|
// çalışma saati gibi PreSignInCheck sonuçları ezilirse kullanıcı asıl sebebi göremez.
|
||||||
|
if (result.PResult == PlatformLoginResultType.Failed && user.AccessFailedCount >= maxAccessFailedCount)
|
||||||
{
|
{
|
||||||
// Display captcha
|
|
||||||
result.PResult = PlatformLoginResultType.ShowCaptcha;
|
result.PResult = PlatformLoginResultType.ShowCaptcha;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ public static class PlatformEventIds
|
||||||
|
|
||||||
public static EventId UserCannotSignInConcurrentUserLimit =
|
public static EventId UserCannotSignInConcurrentUserLimit =
|
||||||
new(19, PlatformConsts.UserCannotSignInErrors.LoginNotAllowed_ConcurrentUserLimit);
|
new(19, PlatformConsts.UserCannotSignInErrors.LoginNotAllowed_ConcurrentUserLimit);
|
||||||
|
|
||||||
|
public static EventId UserCannotSignInBranchLimit =
|
||||||
|
new(20, PlatformConsts.UserCannotSignInErrors.LoginNotAllowed_BranchLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,11 @@ using Sozsoft.Platform.Extensions;
|
||||||
namespace Sozsoft.Platform.Identity;
|
namespace Sozsoft.Platform.Identity;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AbpSessions tablosundaki atıl (idle) oturumları temizleyen Hangfire job'ı.
|
/// AbpSessions tablosundaki atıl (idle) oturumları temizler; böylece kapatılan tarayıcıların
|
||||||
/// Hem Hangfire (DoWorkAsync) hem de PlatformBackgroundWorker altyapısından (ISessionCleanupWorker) çağrılabilir.
|
/// tuttuğu concurrent user lisansları serbest kalır.
|
||||||
/// Atıl sayılma eşiği: OpenIddict refresh token ömrü + 5 dakika.
|
/// WorkerType = SessionCleanupWorker olan kayıt üzerinden PlatformBackgroundWorker tarafından çalıştırılır.
|
||||||
|
/// Atıl sayılma eşiği: OpenIddict refresh token ömrü + 5 dakika. Aktif kullanıcılarda her
|
||||||
|
/// refresh token isteğinde oturum satırı yeniden oluşturulduğu için SignedIn güncel kalır.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PlatformSessionCleanupWorker : ISessionCleanupWorker, ITransientDependency
|
public class PlatformSessionCleanupWorker : ISessionCleanupWorker, ITransientDependency
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
{
|
{
|
||||||
private const string UserLicenseProductName = "Public.products.userLicense";
|
private const string UserLicenseProductName = "Public.products.userLicense";
|
||||||
private const string BranchLicenseProductName = "Public.products.branchHosting";
|
private const string BranchLicenseProductName = "Public.products.branchHosting";
|
||||||
|
private const string ApprovedOrderStatus = "Approved";
|
||||||
|
|
||||||
private readonly IClock clock;
|
private readonly IClock clock;
|
||||||
private readonly IRepository<IpRestriction, Guid> repositoryIp;
|
private readonly IRepository<IpRestriction, Guid> repositoryIp;
|
||||||
|
|
@ -289,16 +290,24 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
/// Tenant'a ait aktif Order'lardaki kullanıcı lisansı limitini aşmamak için login'i engeller.
|
/// Tenant'a ait aktif Order'lardaki kullanıcı lisansı limitini aşmamak için login'i engeller.
|
||||||
/// AbpSessions tablosundaki aktif oturumları sayar (farklı UserId'lere göre distinct).
|
/// AbpSessions tablosundaki aktif oturumları sayar (farklı UserId'lere göre distinct).
|
||||||
/// Kullanıcının kendisinin zaten oturumu varsa (refresh senaryosu) yeni concurrent user sayılmaz.
|
/// Kullanıcının kendisinin zaten oturumu varsa (refresh senaryosu) yeni concurrent user sayılmaz.
|
||||||
|
/// Yalnızca Internal kullanıcılar lisans tüketir.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<bool> CheckConcurrentLimitAsync(IdentityUser user)
|
public async Task<bool> CheckConcurrentLimitAsync(IdentityUser user)
|
||||||
{
|
{
|
||||||
// Eğer tenantId yoksa, kullanıcı tenant'a bağlı değil demektir.
|
// Eğer tenantId yoksa, kullanıcı tenant'a bağlı değil demektir (host).
|
||||||
// Bu durumda concurrent limit kontrolü yapılmaz.
|
// Bu durumda concurrent limit kontrolü yapılmaz.
|
||||||
if (!user.TenantId.HasValue)
|
if (!user.TenantId.HasValue)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// External kullanıcılar sayıma dahil edilmediği için limitle de kısıtlanmazlar.
|
||||||
|
// (Aksi halde lisans dolu iken lisans tüketmeyen kullanıcılar da bloke olurdu.)
|
||||||
|
if (user.GetUserType() != PlatformConsts.UserType.Internal)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
var maxConcurrentUsers = await GetActiveUserLicenseQuantityAsync(user.TenantId.Value);
|
var maxConcurrentUsers = await GetActiveUserLicenseQuantityAsync(user.TenantId.Value);
|
||||||
|
|
||||||
// Tenant bağlamını explicit olarak set et — hem password hem refresh token akışlarında
|
// Tenant bağlamını explicit olarak set et — hem password hem refresh token akışlarında
|
||||||
|
|
@ -306,7 +315,10 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
using (currentTenant.Change(user.TenantId))
|
using (currentTenant.Change(user.TenantId))
|
||||||
{
|
{
|
||||||
using var uow = unitOfWorkManager.Begin(requiresNew: true, isTransactional: false);
|
using var uow = unitOfWorkManager.Begin(requiresNew: true, isTransactional: false);
|
||||||
var sessions = await identitySessionRepository.GetListAsync();
|
|
||||||
|
// maxResultCount explicit verilir; aksi halde sayfalama limiti sessizce
|
||||||
|
// eksik sayıma ve lisans limitinin aşılmasına yol açabilir.
|
||||||
|
var sessions = await identitySessionRepository.GetListAsync(maxResultCount: int.MaxValue);
|
||||||
|
|
||||||
// Kullanıcının kendi oturumu varsa (refresh) o kişi tekrar sayılmaz.
|
// Kullanıcının kendi oturumu varsa (refresh) o kişi tekrar sayılmaz.
|
||||||
var activeUserIds = sessions
|
var activeUserIds = sessions
|
||||||
|
|
@ -316,11 +328,16 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// Aktif session kullanıcılarını IdentityUserManager üzerinden topluca yükle
|
// Aktif session kullanıcılarını IdentityUserManager üzerinden topluca yükle
|
||||||
// ve yalnızca UserType = 0 (Internal) olanları concurrent limite dahil et.
|
// ve yalnızca UserType = Internal olanları concurrent limite dahil et.
|
||||||
var otherActiveUserCount = (await userManager.Users
|
// (UserType ExtraProperties içinde tutulduğu için filtre bellekte uygulanır.)
|
||||||
.Where(u => activeUserIds.Contains(u.Id))
|
var otherActiveUserCount = activeUserIds.Count == 0
|
||||||
.ToListAsync())
|
? 0
|
||||||
.Count(u => u.GetUserType() == PlatformConsts.UserType.Internal);
|
: (await userManager.Users
|
||||||
|
.Where(u => activeUserIds.Contains(u.Id))
|
||||||
|
.ToListAsync())
|
||||||
|
.Count(u => u.GetUserType() == PlatformConsts.UserType.Internal);
|
||||||
|
|
||||||
|
await uow.CompleteAsync();
|
||||||
|
|
||||||
if (otherActiveUserCount >= maxConcurrentUsers)
|
if (otherActiveUserCount >= maxConcurrentUsers)
|
||||||
{
|
{
|
||||||
|
|
@ -329,8 +346,6 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
user.TenantId, maxConcurrentUsers, otherActiveUserCount);
|
user.TenantId, maxConcurrentUsers, otherActiveUserCount);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await uow.CompleteAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -349,7 +364,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
|
|
||||||
if (activeBranchCount > branchLicenseQuantity)
|
if (activeBranchCount > branchLicenseQuantity)
|
||||||
{
|
{
|
||||||
Logger.LogWarning(PlatformEventIds.UserCannotSignInConcurrentUserLimit,
|
Logger.LogWarning(PlatformEventIds.UserCannotSignInBranchLimit,
|
||||||
"Tenant {TenantId} branch license quantity of {Limit} is lower than active branch count {ActiveCount}.",
|
"Tenant {TenantId} branch license quantity of {Limit} is lower than active branch count {ActiveCount}.",
|
||||||
tenantId, branchLicenseQuantity, activeBranchCount);
|
tenantId, branchLicenseQuantity, activeBranchCount);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -376,19 +391,28 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
||||||
return await GetActiveOrderItemQuantityAsync(tenantId, BranchLicenseProductName);
|
return await GetActiveOrderItemQuantityAsync(tenantId, BranchLicenseProductName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tenant'ın yürürlükteki (onaylı, aktif ve lisans tarihi geçerli) siparişlerindeki
|
||||||
|
/// ilgili ürün adedini toplar. Order kayıtları host tarafında tutulduğu için
|
||||||
|
/// tenant filtresi kapatılır ve TenantId ile açıkça filtrelenir.
|
||||||
|
/// </summary>
|
||||||
private async Task<int> GetActiveOrderItemQuantityAsync(Guid tenantId, string productName)
|
private async Task<int> GetActiveOrderItemQuantityAsync(Guid tenantId, string productName)
|
||||||
{
|
{
|
||||||
using (currentTenant.Change(null))
|
using (currentTenant.Change(null))
|
||||||
{
|
{
|
||||||
var orderQuery = await orderRepository.WithDetailsAsync(order => order.Items);
|
var now = clock.Now;
|
||||||
var activeOrders = await orderQuery
|
// Include gerekmiyor; toplam veritabanında hesaplandığı için GetQueryableAsync yeterli.
|
||||||
.Where(order => order.TenantId == tenantId && order.ApprovalStatus == "Approved" && order.IsActive)
|
var orderQuery = await orderRepository.GetQueryableAsync();
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
return activeOrders
|
return await orderQuery
|
||||||
|
.Where(order => order.TenantId == tenantId
|
||||||
|
&& order.ApprovalStatus == ApprovedOrderStatus
|
||||||
|
&& order.IsActive
|
||||||
|
&& (order.LicenseStartTime == null || order.LicenseStartTime <= now)
|
||||||
|
&& (order.LicenseEndTime == null || order.LicenseEndTime >= now))
|
||||||
.SelectMany(order => order.Items)
|
.SelectMany(order => order.Items)
|
||||||
.Where(item => item.ProductName == productName)
|
.Where(item => item.ProductName == productName)
|
||||||
.Sum(item => item.Quantity);
|
.SumAsync(item => (int?)item.Quantity) ?? 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,8 +149,10 @@ public class PlatformTokenController : TokenController
|
||||||
var result = new PlatformLoginResult(signInResult, LP);
|
var result = new PlatformLoginResult(signInResult, LP);
|
||||||
if (!signInResult.Succeeded)
|
if (!signInResult.Succeeded)
|
||||||
{
|
{
|
||||||
// Max deneme sayisini gectiyse kapca goster
|
// Max deneme sayisini gectiyse kapca goster.
|
||||||
if (user.AccessFailedCount >= maxAccessFailedCount)
|
// Yalnızca gerçek parola hatasında; lisans limiti, tenant pasif, çalışma saati gibi
|
||||||
|
// PreSignInCheck sonuçları ezilirse kullanıcı asıl sebebi göremez.
|
||||||
|
if (result.PResult == PlatformLoginResultType.Failed && user.AccessFailedCount >= maxAccessFailedCount)
|
||||||
{
|
{
|
||||||
result.PResult = PlatformLoginResultType.ShowCaptcha;
|
result.PResult = PlatformLoginResultType.ShowCaptcha;
|
||||||
}
|
}
|
||||||
|
|
@ -263,11 +265,13 @@ Your login code: {twoFactorToken}";
|
||||||
{
|
{
|
||||||
await ReplaceUserSessionAsync(user, request.ClientId, sessionId, checkConcurrentLimit: true);
|
await ReplaceUserSessionAsync(user, request.ClientId, sessionId, checkConcurrentLimit: true);
|
||||||
}
|
}
|
||||||
catch (UserFriendlyException) { throw; }
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogWarning(ex, "Failed to manage AbpSessions. UserId: {UserId}", user.Id);
|
// Oturum satırı oluşmazsa PlatformSessionValidationMiddleware sonraki her isteği 401'e
|
||||||
throw new UserFriendlyException(PlatformConsts.UserCannotSignInErrors.LoginNotAllowed_ConcurrentUserLimit);
|
// düşürür; bu yüzden hata yutulmaz. Lisans limiti zaten UserFriendlyException olarak gelir,
|
||||||
|
// diğer hataları lisans hatası gibi göstermek yanıltıcı olur.
|
||||||
|
Logger.LogError(ex, "Failed to manage AbpSessions. UserId: {UserId}", user.Id);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,10 @@ const PlatformLoginResultType = {
|
||||||
NotAllowedIp: 19, // IpRestriction tablosu, Mesaj: Identity:IpRestrictionError
|
NotAllowedIp: 19, // IpRestriction tablosu, Mesaj: Identity:IpRestrictionError
|
||||||
LoginEndDateDue: 20, // LoginEndDate:>Today, Buton: ShowExtendMyLoginButton, Mesaj: Identity:LoginEndDateError, >LoginEndDate
|
LoginEndDateDue: 20, // LoginEndDate:>Today, Buton: ShowExtendMyLoginButton, Mesaj: Identity:LoginEndDateError, >LoginEndDate
|
||||||
ShowCaptcha: 21, // AccessFailedCount>AccountCaptchaMaxFailedAccessAttempts, Sayfa: Show Captcha
|
ShowCaptcha: 21, // AccessFailedCount>AccountCaptchaMaxFailedAccessAttempts, Sayfa: Show Captcha
|
||||||
ConcurrentUserLimit: 22, // MaxConcurrentUsers limiti dolduğunda, Mesaj: Identity:ConcurrentUserLimitError
|
TenantIsPassive: 22, // Tenant pasif, Mesaj: Identity:TenantIsPassive
|
||||||
|
NotAllowedWorkHour: 23, // WorkHour tanimi disinda giris, Mesaj: Identity:LoginNotAllowed_WorkHour
|
||||||
|
BranchLimit: 24, // Aktif sube sayisi sube lisansini asiyor, Mesaj: Identity:BranchLimitError
|
||||||
|
ConcurrentUserLimit: 25, // Aktif kullanici lisansi dolduğunda, Mesaj: Identity:ConcurrentUserLimitError
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PlatformLoginResultType
|
export default PlatformLoginResultType
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue