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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ public static class PlatformEventIds
|
|||
|
||||
public static EventId UserCannotSignInConcurrentUserLimit =
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// AbpSessions tablosundaki atıl (idle) oturumları temizleyen Hangfire job'ı.
|
||||
/// Hem Hangfire (DoWorkAsync) hem de PlatformBackgroundWorker altyapısından (ISessionCleanupWorker) çağrılabilir.
|
||||
/// Atıl sayılma eşiği: OpenIddict refresh token ömrü + 5 dakika.
|
||||
/// AbpSessions tablosundaki atıl (idle) oturumları temizler; böylece kapatılan tarayıcıların
|
||||
/// tuttuğu concurrent user lisansları serbest kalır.
|
||||
/// 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>
|
||||
public class PlatformSessionCleanupWorker : ISessionCleanupWorker, ITransientDependency
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
|||
{
|
||||
private const string UserLicenseProductName = "Public.products.userLicense";
|
||||
private const string BranchLicenseProductName = "Public.products.branchHosting";
|
||||
private const string ApprovedOrderStatus = "Approved";
|
||||
|
||||
private readonly IClock clock;
|
||||
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.
|
||||
/// 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.
|
||||
/// Yalnızca Internal kullanıcılar lisans tüketir.
|
||||
/// </summary>
|
||||
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.
|
||||
if (!user.TenantId.HasValue)
|
||||
{
|
||||
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);
|
||||
|
||||
// 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 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.
|
||||
var activeUserIds = sessions
|
||||
|
|
@ -316,11 +328,16 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
|||
.ToList();
|
||||
|
||||
// Aktif session kullanıcılarını IdentityUserManager üzerinden topluca yükle
|
||||
// ve yalnızca UserType = 0 (Internal) olanları concurrent limite dahil et.
|
||||
var otherActiveUserCount = (await userManager.Users
|
||||
.Where(u => activeUserIds.Contains(u.Id))
|
||||
.ToListAsync())
|
||||
.Count(u => u.GetUserType() == PlatformConsts.UserType.Internal);
|
||||
// ve yalnızca UserType = Internal olanları concurrent limite dahil et.
|
||||
// (UserType ExtraProperties içinde tutulduğu için filtre bellekte uygulanır.)
|
||||
var otherActiveUserCount = activeUserIds.Count == 0
|
||||
? 0
|
||||
: (await userManager.Users
|
||||
.Where(u => activeUserIds.Contains(u.Id))
|
||||
.ToListAsync())
|
||||
.Count(u => u.GetUserType() == PlatformConsts.UserType.Internal);
|
||||
|
||||
await uow.CompleteAsync();
|
||||
|
||||
if (otherActiveUserCount >= maxConcurrentUsers)
|
||||
{
|
||||
|
|
@ -329,8 +346,6 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
|||
user.TenantId, maxConcurrentUsers, otherActiveUserCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
await uow.CompleteAsync();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -349,7 +364,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
|||
|
||||
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}.",
|
||||
tenantId, branchLicenseQuantity, activeBranchCount);
|
||||
return false;
|
||||
|
|
@ -376,19 +391,28 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
|
|||
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)
|
||||
{
|
||||
using (currentTenant.Change(null))
|
||||
{
|
||||
var orderQuery = await orderRepository.WithDetailsAsync(order => order.Items);
|
||||
var activeOrders = await orderQuery
|
||||
.Where(order => order.TenantId == tenantId && order.ApprovalStatus == "Approved" && order.IsActive)
|
||||
.ToListAsync();
|
||||
var now = clock.Now;
|
||||
// Include gerekmiyor; toplam veritabanında hesaplandığı için GetQueryableAsync yeterli.
|
||||
var orderQuery = await orderRepository.GetQueryableAsync();
|
||||
|
||||
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)
|
||||
.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);
|
||||
if (!signInResult.Succeeded)
|
||||
{
|
||||
// Max deneme sayisini gectiyse kapca goster
|
||||
if (user.AccessFailedCount >= maxAccessFailedCount)
|
||||
// Max deneme sayisini gectiyse kapca goster.
|
||||
// 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;
|
||||
}
|
||||
|
|
@ -263,11 +265,13 @@ Your login code: {twoFactorToken}";
|
|||
{
|
||||
await ReplaceUserSessionAsync(user, request.ClientId, sessionId, checkConcurrentLimit: true);
|
||||
}
|
||||
catch (UserFriendlyException) { throw; }
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Failed to manage AbpSessions. UserId: {UserId}", user.Id);
|
||||
throw new UserFriendlyException(PlatformConsts.UserCannotSignInErrors.LoginNotAllowed_ConcurrentUserLimit);
|
||||
// Oturum satırı oluşmazsa PlatformSessionValidationMiddleware sonraki her isteği 401'e
|
||||
// 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
|
||||
LoginEndDateDue: 20, // LoginEndDate:>Today, Buton: ShowExtendMyLoginButton, Mesaj: Identity:LoginEndDateError, >LoginEndDate
|
||||
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
|
||||
|
|
|
|||
Loading…
Reference in a new issue