Add team roles and content management updates
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
using System.Text.Json;
|
||||
using Backend.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Backend.Security;
|
||||
|
||||
public static class AdminPermissionCatalog
|
||||
{
|
||||
public const string Dashboard = "dashboard";
|
||||
public const string Years = "years";
|
||||
public const string Nominations = "nominations";
|
||||
public const string Categories = "categories";
|
||||
public const string Candidates = "candidates";
|
||||
public const string Clips = "clips";
|
||||
public const string Risk = "risk";
|
||||
public const string Audit = "audit";
|
||||
public const string Analytics = "analytics";
|
||||
public const string Winners = "winners";
|
||||
public const string Content = "content";
|
||||
public const string Settings = "settings";
|
||||
public const string Team = "team";
|
||||
|
||||
public static readonly string[] AllPermissionKeys =
|
||||
[
|
||||
Dashboard,
|
||||
Years,
|
||||
Nominations,
|
||||
Categories,
|
||||
Candidates,
|
||||
Clips,
|
||||
Risk,
|
||||
Audit,
|
||||
Analytics,
|
||||
Winners,
|
||||
Content,
|
||||
Settings,
|
||||
Team,
|
||||
];
|
||||
|
||||
public static readonly string[] SeasonReadPermissionKeys =
|
||||
[
|
||||
Dashboard,
|
||||
Years,
|
||||
Nominations,
|
||||
Categories,
|
||||
Candidates,
|
||||
Clips,
|
||||
Analytics,
|
||||
Winners,
|
||||
];
|
||||
|
||||
public static bool IsKnownPermission(string permissionKey) =>
|
||||
AllPermissionKeys.Contains(permissionKey, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public static string[] DefaultPermissionKeys(string? role)
|
||||
{
|
||||
return AdminRoles.Normalize(role) switch
|
||||
{
|
||||
AdminRoles.Owner => AllPermissionKeys,
|
||||
AdminRoles.Creator => AllPermissionKeys,
|
||||
AdminRoles.Admin => AllPermissionKeys.Where(key => key is not Settings).ToArray(),
|
||||
AdminRoles.Member => [Dashboard, Nominations, Categories, Candidates, Clips, Content],
|
||||
AdminRoles.Reviewer => [Dashboard, Nominations, Clips, Risk, Audit],
|
||||
AdminRoles.OrganizationTeam => [Dashboard, Content, Analytics, Winners, Settings],
|
||||
AdminRoles.ContentAdmin => [Content, Settings],
|
||||
_ => [],
|
||||
};
|
||||
}
|
||||
|
||||
public static async Task<string[]> GetPermissionKeysAsync(
|
||||
AwardsDbContext db,
|
||||
string? role,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var normalizedRole = AdminRoles.Normalize(role);
|
||||
if (normalizedRole is AdminRoles.Owner or AdminRoles.Creator)
|
||||
{
|
||||
return AllPermissionKeys;
|
||||
}
|
||||
|
||||
var fallback = DefaultPermissionKeys(normalizedRole);
|
||||
var overrideJson = await db.TeamRolePermissions
|
||||
.AsNoTracking()
|
||||
.Where(item => item.Role == normalizedRole)
|
||||
.Select(item => item.PermissionsJson)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(overrideJson))
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return (JsonSerializer.Deserialize<string[]>(overrideJson) ?? fallback)
|
||||
.Where(IsKnownPermission)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(item => item)
|
||||
.ToArray();
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<bool> HasPermissionAsync(
|
||||
AwardsDbContext db,
|
||||
string? role,
|
||||
string permissionKey,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var permissions = await GetPermissionKeysAsync(db, role, cancellationToken);
|
||||
return permissions.Contains(permissionKey, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static async Task<bool> HasAnyPermissionAsync(
|
||||
AwardsDbContext db,
|
||||
string? role,
|
||||
IEnumerable<string> permissionKeys,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var permissions = await GetPermissionKeysAsync(db, role, cancellationToken);
|
||||
return permissionKeys.Any(permission => permissions.Contains(permission, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,13 @@ namespace Backend.Security;
|
||||
public static class AdminRoles
|
||||
{
|
||||
public const string Viewer = "viewer";
|
||||
public const string Member = "member";
|
||||
public const string Reviewer = "reviewer";
|
||||
public const string OrganizationTeam = "organization_team";
|
||||
public const string ContentAdmin = "content_admin";
|
||||
public const string Admin = "admin";
|
||||
public const string Owner = "owner";
|
||||
public const string Creator = "creator";
|
||||
|
||||
public static string Normalize(string? role)
|
||||
{
|
||||
@@ -14,8 +18,19 @@ public static class AdminRoles
|
||||
return normalizedRole switch
|
||||
{
|
||||
Owner => Owner,
|
||||
Creator => Creator,
|
||||
Admin => Admin,
|
||||
ContentAdmin => ContentAdmin,
|
||||
"creator_role" => Creator,
|
||||
"creatorrolle" => Creator,
|
||||
"admins" => Admin,
|
||||
"mitglied" => Member,
|
||||
"member" => Member,
|
||||
"reviewer" => Reviewer,
|
||||
"organisation_team" => OrganizationTeam,
|
||||
"organisationteam" => OrganizationTeam,
|
||||
"organization_team" => OrganizationTeam,
|
||||
"organizationteam" => OrganizationTeam,
|
||||
_ => Viewer,
|
||||
};
|
||||
}
|
||||
@@ -23,18 +38,30 @@ public static class AdminRoles
|
||||
public static bool IsKnownRole(string? role)
|
||||
{
|
||||
var normalizedRole = (role ?? string.Empty).Trim().ToLowerInvariant().Replace('-', '_');
|
||||
return normalizedRole is Viewer or ContentAdmin or Admin or Owner;
|
||||
return Normalize(normalizedRole) is Viewer or Member or Reviewer or OrganizationTeam or ContentAdmin or Admin or Owner or Creator;
|
||||
}
|
||||
|
||||
public static bool CanAccessAdmin(string? role) =>
|
||||
Normalize(role) is ContentAdmin or Admin or Owner;
|
||||
Normalize(role) is Member or Reviewer or OrganizationTeam or ContentAdmin or Admin or Owner or Creator;
|
||||
|
||||
public static bool CanManageContent(string? role) =>
|
||||
Normalize(role) is ContentAdmin or Admin or Owner;
|
||||
Normalize(role) is ContentAdmin or Admin or Owner or Creator;
|
||||
|
||||
public static bool CanManageAdminWorkspace(string? role) =>
|
||||
Normalize(role) is Admin or Owner;
|
||||
Normalize(role) is Admin or Owner or Creator;
|
||||
|
||||
public static bool CanManageOperationalSettings(string? role) =>
|
||||
Normalize(role) is Owner;
|
||||
Normalize(role) is Owner or Creator;
|
||||
|
||||
public static bool CanManageTeam(string? role) =>
|
||||
Normalize(role) is Admin or Owner or Creator;
|
||||
|
||||
public static bool CanResetTeamPasswords(string? role) =>
|
||||
Normalize(role) is Owner or Creator;
|
||||
|
||||
public static bool CanDeleteTeamMembers(string? role) =>
|
||||
Normalize(role) is Owner or Creator;
|
||||
|
||||
public static bool IsPrivilegedFullControlRole(string? role) =>
|
||||
Normalize(role) is Owner or Creator;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using Backend.Data;
|
||||
using Backend.Endpoints;
|
||||
using Backend.Services;
|
||||
|
||||
namespace Backend.Security;
|
||||
|
||||
public sealed class AdminSessionFilter(IUserSessionService userSessionService) : IEndpointFilter
|
||||
public sealed class AdminSessionFilter(IUserSessionService userSessionService, AwardsDbContext db) : IEndpointFilter
|
||||
{
|
||||
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
|
||||
{
|
||||
@@ -17,6 +19,23 @@ public sealed class AdminSessionFilter(IUserSessionService userSessionService) :
|
||||
return Results.Json(new { message = "Admin access requires an elevated role." }, statusCode: StatusCodes.Status403Forbidden);
|
||||
}
|
||||
|
||||
var teamMember = await AuthEndpoints.FindTeamMemberForSessionAsync(db, session, context.HttpContext.RequestAborted);
|
||||
if (teamMember is not null)
|
||||
{
|
||||
if (!teamMember.IsActive)
|
||||
{
|
||||
return Results.Unauthorized();
|
||||
}
|
||||
|
||||
if (teamMember.MustChangePassword)
|
||||
{
|
||||
return Results.Json(new { message = "Bitte ändere zuerst dein temporäres Passwort." }, statusCode: StatusCodes.Status403Forbidden);
|
||||
}
|
||||
|
||||
session.DisplayName = teamMember.DisplayName;
|
||||
session.Role = AdminRoles.Normalize(teamMember.Role);
|
||||
}
|
||||
|
||||
context.HttpContext.SetCurrentSession(session);
|
||||
return await next(context);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user