IsNotAllowed_BranchLimit

This commit is contained in:
Sedat Öztürk 2026-07-07 21:19:37 +03:00
parent 4ae697600b
commit 6dae9c4e94
7 changed files with 78 additions and 8 deletions

View file

@ -58,6 +58,10 @@ public static class PlatformSignInResultExtensions
{
return PlatformConsts.UserCannotSignInErrors.LoginNotAllowed_WorkHour;
}
if (resultP.IsNotAllowed_BranchLimit)
{
return PlatformConsts.UserCannotSignInErrors.LoginNotAllowed_BranchLimit;
}
if (resultP.IsNotAllowed_ConcurrentUserLimit)
{
return PlatformConsts.UserCannotSignInErrors.LoginNotAllowed_ConcurrentUserLimit;

View file

@ -162,6 +162,7 @@ public static class PlatformConsts
public const string TenantIsPassive = GroupName + ".TenantIsPassive";
public const string LoginNotAllowed_WorkHour = GroupName + ".LoginNotAllowed_WorkHour";
public const string ConcurrentUserLimitError = GroupName + ".ConcurrentUserLimitError";
public const string BranchLimitError = GroupName + ".BranchLimitError";
public const string CaptchaWrongCode = GroupName + ".CaptchaWrongCode";
public const string TwoFactorWrongCode = GroupName + ".TwoFactorWrongCode";
public const string SignOut = GroupName + ".SignOut";
@ -548,6 +549,7 @@ public static class PlatformConsts
public static string LoginNotAllowed_TenantIsPassive { get; set; } = "UserCannotSignInTenantIsPassive";
public static string LoginNotAllowed_TenantNotFound { get; set; } = "UserCannotSignInTenantNotFound";
public static string LoginNotAllowed_WorkHour { get; set; } = "UserCannotSignInWorkHour";
public static string LoginNotAllowed_BranchLimit { get; set; } = "UserCannotSignInBranchLimit";
public static string LoginNotAllowed_ConcurrentUserLimit { get; set; } = "UserCannotSignInConcurrentUserLimit";
}

View file

@ -30,6 +30,8 @@ public class PlatformSignInResult : SignInResult
public bool IsNotAllowed_WorkHour { get; set; }
public bool IsNotAllowed_BranchLimit { get; set; }
public bool IsNotAllowed_ConcurrentUserLimit { get; set; }
public override string ToString()
@ -42,6 +44,7 @@ public class PlatformSignInResult : SignInResult
ShouldChangePasswordPeriodic ? "ShouldChangePasswordPeriodic" :
IsNotAllowed_TenantIsPassive ? "NotAllowed_TenantIsPassive" :
IsNotAllowed_WorkHour ? "NotAllowed_WorkHour" :
IsNotAllowed_BranchLimit ? "NotAllowed_BranchLimit" :
IsNotAllowed_ConcurrentUserLimit ? "NotAllowed_ConcurrentUserLimit" :
base.ToString();
}

View file

@ -64,6 +64,11 @@ public class PlatformLoginResult : AbpLoginResult
PResult = PlatformLoginResultType.NotAllowedWorkHour;
Description = L[PlatformConsts.AbpIdentity.User.LoginNotAllowed_WorkHour];
}
else if (resultP.IsNotAllowed_BranchLimit)
{
PResult = PlatformLoginResultType.BranchLimit;
Description = L[PlatformConsts.AbpIdentity.User.BranchLimitError];
}
else if (resultP.IsNotAllowed_ConcurrentUserLimit)
{
PResult = PlatformLoginResultType.ConcurrentUserLimit;

View file

@ -16,6 +16,7 @@ public enum PlatformLoginResultType : byte
ShowCaptcha,
TenantIsPassive,
NotAllowedWorkHour,
BranchLimit,
ConcurrentUserLimit
}

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
using Sozsoft.Platform.Entities;
@ -33,6 +32,8 @@ public interface IPlatformSignInManager
public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
{
private const string UserLicenseProductName = "Public.products.userLicense";
private const string BranchLicenseProductName = "Public.products.branchHosting";
private readonly IClock clock;
private readonly IRepository<IpRestriction, Guid> repositoryIp;
private readonly IRepository<WorkHour, Guid> repositoryWorkHour;
@ -42,6 +43,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
private readonly ICurrentTenant currentTenant;
private readonly IUnitOfWorkManager unitOfWorkManager;
private readonly IRepository<Order, Guid> orderRepository;
private readonly IRepository<Branch, Guid> branchRepository;
public PlatformSignInManager(
IdentityUserManager userManager,
@ -60,7 +62,8 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
IIdentitySessionRepository identitySessionRepository,
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
IRepository<Order, Guid> orderRepository
IRepository<Order, Guid> orderRepository,
IRepository<Branch, Guid> branchRepository
) : base(
userManager,
contextAccessor,
@ -81,6 +84,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
this.currentTenant = currentTenant;
this.unitOfWorkManager = unitOfWorkManager;
this.orderRepository = orderRepository;
this.branchRepository = branchRepository;
}
public async Task<bool> PreSignInCheckAsync(IdentityUser user)
@ -115,6 +119,10 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
{
return new PlatformSignInResult() { IsNotAllowed_WorkHour = true };
}
if (!await CheckBranchLicenseLimitAsync(user))
{
return new PlatformSignInResult() { IsNotAllowed_BranchLimit = true };
}
if (!await CheckConcurrentLimitAsync(user))
{
return new PlatformSignInResult() { IsNotAllowed_ConcurrentUserLimit = true };
@ -284,16 +292,14 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
/// </summary>
public async Task<bool> CheckConcurrentLimitAsync(IdentityUser user)
{
// Eğer tenantId yoksa, kullanıcı tenant'a bağlı değil demektir.
// Bu durumda concurrent limit kontrolü yapılmaz.
if (!user.TenantId.HasValue)
{
return true;
}
var maxConcurrentUsers = await GetActiveUserLicenseQuantityAsync(user.TenantId.Value);
if (maxConcurrentUsers == 0)
{
return true;
}
// Tenant bağlamını explicit olarak set et — hem password hem refresh token akışlarında
// doğru tenant izolasyonu için güvenli bir şekilde değiştirilir.
@ -324,7 +330,47 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
return true;
}
private async Task<bool> CheckBranchLicenseLimitAsync(IdentityUser user)
{
if (!user.TenantId.HasValue)
{
return true;
}
var tenantId = user.TenantId.Value;
var activeBranchCount = await GetActiveBranchCountAsync(tenantId);
var branchLicenseQuantity = await GetActiveBranchLicenseQuantityAsync(tenantId);
if (activeBranchCount > branchLicenseQuantity)
{
Logger.LogWarning(PlatformEventIds.UserCannotSignInConcurrentUserLimit,
"Tenant {TenantId} branch license quantity of {Limit} is lower than active branch count {ActiveCount}.",
tenantId, branchLicenseQuantity, activeBranchCount);
return false;
}
return true;
}
private async Task<int> GetActiveBranchCountAsync(Guid tenantId)
{
using (currentTenant.Change(tenantId))
{
return await branchRepository.CountAsync(branch => branch.TenantId == tenantId && branch.IsActive == true);
}
}
private async Task<int> GetActiveUserLicenseQuantityAsync(Guid tenantId)
{
return await GetActiveOrderItemQuantityAsync(tenantId, UserLicenseProductName);
}
private async Task<int> GetActiveBranchLicenseQuantityAsync(Guid tenantId)
{
return await GetActiveOrderItemQuantityAsync(tenantId, BranchLicenseProductName);
}
private async Task<int> GetActiveOrderItemQuantityAsync(Guid tenantId, string productName)
{
using (currentTenant.Change(null))
{
@ -335,7 +381,7 @@ public class PlatformSignInManager : AbpSignInManager, IPlatformSignInManager
return activeOrders
.SelectMany(order => order.Items)
.Where(item => item.ProductName == UserLicenseProductName)
.Where(item => item.ProductName == productName)
.Sum(item => item.Quantity);
}
}

View file

@ -362,7 +362,16 @@ export const PaymentForm: React.FC<PaymentFormProps> = ({ onBack, onComplete, ca
className="h-10 w-10 shrink-0 rounded-md object-cover"
/>
)}
<div className="min-w-0 font-medium">{translate('::' + item.product.name)}</div>
<div className="min-w-0">
<div className="truncate font-medium">
{translate('::' + item.product.name)}
</div>
{item.product.isQuantityBased && (
<div className="mt-1 text-xs font-medium text-gray-500 dark:text-gray-400">
x {item.quantity}
</div>
)}
</div>
</div>
<div className="font-medium text-right">{formatPrice(item.totalPrice)}</div>
</div>