feat: add permission checking and task assignment handling in ProjectBoardHub

This commit is contained in:
2025-12-15 13:49:41 +03:30
parent 2a31b27f9b
commit 000af89fd7
8 changed files with 109 additions and 12 deletions

View File

@@ -56,6 +56,11 @@ public class AuthHelper : IAuthHelper
return Tools.DeserializeFromBsonList<int>(permissions); //Mahan
}
public bool HasPermission(int permission)
{
return GetPermissions().Any(x => x == permission);
}
public long CurrentAccountId()
{
return IsAuthenticated()

View File

@@ -12,6 +12,7 @@ public interface IAuthHelper
string CurrentAccountRole();
AuthViewModel CurrentAccountInfo();
List<int> GetPermissions();
bool HasPermission(int permission);
long CurrentAccountId();
string CurrentAccountMobile();

View File

@@ -106,6 +106,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GozareshgirProgramManager.I
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared.Contracts", "Shared.Contracts\Shared.Contracts.csproj", "{08B234B6-783B-44E9-9961-4F97EAD16308}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{F61F77F5-9B14-49CC-870B-1C61D2636586}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -275,6 +277,7 @@ Global
{B57EB542-C028-4A77-9386-9DFF1E60FDCB} = {9D85672B-D48E-40B5-9804-0CE220E0E64C}
{D2B4F1D7-6336-4B30-910C-219F4119303F} = {D74D1E3B-3BE3-47EE-9914-785A8AD536E5}
{408281FE-615F-4CBE-BD95-2E86F5ACC6C3} = {C0AE9368-D4E7-450B-9713-929D319DE690}
{08B234B6-783B-44E9-9961-4F97EAD16308} = {F61F77F5-9B14-49CC-870B-1C61D2636586}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E6CFB3A7-A7C8-4E82-8F06-F750408F0BA9}

View File

@@ -0,0 +1,26 @@
namespace GozareshgirProgramManager.Application._Common.Constants;
public static class ProgramManagerPermissionCode
{
public const int Code = 99;
/// <summary>
///بخش اجرا
/// </summary>
public static class Board
{
public const int Code = 991;
/// <summary>
/// تب همه
/// </summary>
public static class All
{
public const int Code = 99101;
/// <summary>
/// دیدن همه تسک ها
/// </summary>
public const int ViewAll = 9910101;
}
}
}

View File

@@ -9,4 +9,6 @@ public interface ITaskSectionRepository: IRepository<Guid,TaskSection>
Task<TaskSection?> GetByIdWithFullDataAsync(Guid id, CancellationToken cancellationToken = default);
Task<List<TaskSection>> GetAssignedToUserAsync(long userId);
}

View File

@@ -1,19 +1,70 @@
using _0_Framework.Application;
using GozareshgirProgramManager.Application._Common.Constants;
using Microsoft.AspNetCore.SignalR;
using GozareshgirProgramManager.Domain.ProjectAgg.Repositories;
using System.Collections.Generic;
using System.Linq;
using System;
namespace ServiceHost.Hubs.ProgramManager;
public class ProjectBoardHub : Hub
{
private readonly IAuthHelper _authHelper;
private readonly ITaskSectionRepository _taskSectionRepository;
public ProjectBoardHub(IAuthHelper authHelper, ITaskSectionRepository taskSectionRepository)
{
_authHelper = authHelper;
_taskSectionRepository = taskSectionRepository;
}
public override async Task OnConnectedAsync()
{
var user = Context.User?.FindFirst("pm.userId")?.Value;
if (user != null && user !="0")
// Rule 4: Determine all group memberships server-side.
if (!_authHelper.IsAuthenticated())
{
await Groups.AddToGroupAsync(
Context.ConnectionId,
$"pm.user-{user}"
);
await base.OnConnectedAsync();
return;
}
var connectionId = Context.ConnectionId;
// Rule 2: Add to all permission-based groups based on user's claims.
var permissionGroups = _authHelper.GetPermissions() ?? new List<int>();
// Rule 3: Add to task-specific groups for all tasks assigned to the user (by accountId claim).
var userId =Convert.ToInt32(Context.User?.FindFirst("pm.userId")?.Value);
var taskGroups = new List<string>();
if (userId > 0)
{
var assignedTasks = await _taskSectionRepository.GetAssignedToUserAsync(userId);
if (assignedTasks is { Count: > 0 })
{
taskGroups = assignedTasks
.Select(t => $"pm.task:{t.Id}")
.ToList();
}
}
// Build the full, de-duplicated set of groups to join.
var groupsToJoin = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
// Permission-based groups
foreach (var perm in permissionGroups)
groupsToJoin.Add($"pm.perm:{perm}");
// Task-based groups
foreach (var tg in taskGroups)
groupsToJoin.Add(tg);
// Rule 5: Avoid duplicate joins; join all needed groups concurrently.
if (groupsToJoin.Count > 0)
{
var joinTasks = groupsToJoin
.Select(group => Groups.AddToGroupAsync(connectionId, group));
await Task.WhenAll(joinTasks);
}
await base.OnConnectedAsync();

View File

@@ -1,3 +1,4 @@
using GozareshgirProgramManager.Application._Common.Constants;
using GozareshgirProgramManager.Application.Interfaces;
using GozareshgirProgramManager.Domain.ProjectAgg.Enums;
using Microsoft.AspNetCore.SignalR;
@@ -24,7 +25,15 @@ public class SignalRBoardNotificationPublisher:IBoardNotificationPublisher
NewStatus = newStatus,
SectionId = sectionId
};
_hubContext.Clients.Group($"pm.user-{userId}").SendAsync("ReceiveProjectStatusChanged",payload);
return Task.CompletedTask;
// گروه task-specific (برای همه assigneeهای واقعی)
var taskGroup = $"pm.task:{sectionId}";
// گروه permission-based (مثلاً برای Admin ها)
var permissionGroup = $"pm.perm:{ProgramManagerPermissionCode.Board.All.ViewAll}";
// ارسال به هر دو گروه؛ SignalR خودش duplicate connection رو هندل می‌کنه
return _hubContext.Clients.Groups(taskGroup, permissionGroup)
.SendAsync("ReceiveProjectStatusChanged", payload);
}
}

View File

@@ -19,7 +19,7 @@
"sqlDebugging": true,
"dotnetRunMessages": "true",
"nativeDebugging": true,
"applicationUrl": "https://localhost:5004;http://localhost:5003",
"applicationUrl": "https://localhost:5004;http://localhost:5003;https://192.168.0.117:5006",
"jsWebView2Debugging": false,
"hotReloadEnabled": true
},