学院管理员限制在所属学院。 辅导员通过稳定账号 ID 绑定行政班,避免重名串班。 教师只能访问本人档案、授课课程和所授课学生。 学生只能访问本人档案及所在班级课程。 教师/学生角色会自动校验并绑定工号或学号档案。 超级管理员可在用户页面调整角色、学院、工号/学号,并预览生效后的数据范围。
90 lines
2.7 KiB
C#
90 lines
2.7 KiB
C#
using System.Security.Claims;
|
|
using Jiaowu.Api.Domain.Identity;
|
|
|
|
namespace Jiaowu.Api.Infrastructure.Auth;
|
|
|
|
public interface ICurrentUserDataScope
|
|
{
|
|
CurrentUserScope Current { get; }
|
|
}
|
|
|
|
public sealed record CurrentUserScope(
|
|
Guid UserId,
|
|
string? DisplayName,
|
|
Guid? CollegeId,
|
|
DataScope Scope,
|
|
IReadOnlySet<string> Roles)
|
|
{
|
|
public bool IsInRole(string role) => Roles.Contains(role);
|
|
|
|
public bool CanAccessCollege(Guid collegeId) =>
|
|
Scope == DataScope.All ||
|
|
Scope == DataScope.College && CollegeId == collegeId;
|
|
|
|
public Guid? RestrictedCollegeId => Scope switch
|
|
{
|
|
DataScope.All => null,
|
|
DataScope.College => CollegeId ?? Guid.Empty,
|
|
_ => Guid.Empty
|
|
};
|
|
}
|
|
|
|
public sealed class CurrentUserDataScope(IHttpContextAccessor httpContextAccessor)
|
|
: ICurrentUserDataScope
|
|
{
|
|
public CurrentUserScope Current
|
|
{
|
|
get
|
|
{
|
|
var principal = httpContextAccessor.HttpContext?.User;
|
|
var roles = new HashSet<string>(
|
|
principal?.FindAll(ClaimTypes.Role).Select(x => x.Value) ?? [],
|
|
StringComparer.OrdinalIgnoreCase);
|
|
|
|
return new CurrentUserScope(
|
|
ParseGuid(principal?.FindFirstValue(ClaimTypes.NameIdentifier)),
|
|
principal?.FindFirstValue("display_name") ??
|
|
principal?.FindFirstValue(ClaimTypes.Name),
|
|
ParseNullableGuid(principal?.FindFirstValue("college_id")),
|
|
EffectiveDataScopeResolver.Resolve(roles),
|
|
roles);
|
|
}
|
|
}
|
|
|
|
private static Guid ParseGuid(string? value) =>
|
|
Guid.TryParse(value, out var id) ? id : Guid.Empty;
|
|
|
|
private static Guid? ParseNullableGuid(string? value) =>
|
|
Guid.TryParse(value, out var id) ? id : null;
|
|
}
|
|
|
|
public static class EffectiveDataScopeResolver
|
|
{
|
|
private static readonly IReadOnlyDictionary<string, DataScope> RoleScopes =
|
|
new Dictionary<string, DataScope>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
[SystemRoles.SuperAdmin] = DataScope.All,
|
|
[SystemRoles.AcademicAdmin] = DataScope.All,
|
|
[SystemRoles.Leader] = DataScope.All,
|
|
[SystemRoles.CollegeAdmin] = DataScope.College,
|
|
[SystemRoles.Counselor] = DataScope.Class,
|
|
[SystemRoles.Teacher] = DataScope.Self,
|
|
[SystemRoles.Student] = DataScope.Self
|
|
};
|
|
|
|
public static DataScope Resolve(IEnumerable<string> roles)
|
|
{
|
|
var effectiveScope = DataScope.Self;
|
|
foreach (var role in roles)
|
|
{
|
|
if (RoleScopes.TryGetValue(role, out var roleScope) &&
|
|
roleScope > effectiveScope)
|
|
{
|
|
effectiveScope = roleScope;
|
|
}
|
|
}
|
|
|
|
return effectiveScope;
|
|
}
|
|
}
|