diff --git a/TASVideos.Core/Extensions/WikiHelper.cs b/TASVideos.Core/Extensions/WikiHelper.cs index 4ff860bc1..3b6f33b45 100644 --- a/TASVideos.Core/Extensions/WikiHelper.cs +++ b/TASVideos.Core/Extensions/WikiHelper.cs @@ -5,8 +5,10 @@ namespace TASVideos.Extensions; public static class WikiHelper { - public static bool UserCanEditWikiPage(string? pageName, string? userName, IReadOnlyCollection userPermissions) + public static bool UserCanEditWikiPage(string? pageName, string? userName, IReadOnlyCollection userPermissions, out HashSet relevantPermissions) { + relevantPermissions = []; + if (string.IsNullOrWhiteSpace(pageName) || string.IsNullOrWhiteSpace(userName)) { return false; @@ -17,43 +19,52 @@ public static bool UserCanEditWikiPage(string? pageName, string? userName, IRead // Anyone who can edit anything (including the user's own homepage) should be allowed to edit his if (pageName == "SandBox") { - return userPermissions.Contains(PermissionTo.EditGameResources) - || userPermissions.Contains(PermissionTo.EditHomePage) - || userPermissions.Contains(PermissionTo.EditWikiPages) - || userPermissions.Contains(PermissionTo.EditSystemPages) - || userPermissions.Contains(PermissionTo.EditSubmissions) - || userPermissions.Contains(PermissionTo.EditPublicationMetaData); + relevantPermissions = [ + PermissionTo.EditGameResources, + PermissionTo.EditHomePage, + PermissionTo.EditWikiPages, + PermissionTo.EditSystemPages, + PermissionTo.EditSubmissions, + PermissionTo.EditPublicationMetaData + ]; + return relevantPermissions.Any(userPermissions.Contains); } if (IsPublicationPage(pageName, out _)) { - return userPermissions.Contains(PermissionTo.EditPublicationMetaData); + relevantPermissions = [PermissionTo.EditPublicationMetaData]; + return relevantPermissions.Any(userPermissions.Contains); } if (IsSubmissionPage(pageName, out _)) { - return userPermissions.Contains(PermissionTo.EditSubmissions); + relevantPermissions = [PermissionTo.EditSubmissions]; + return relevantPermissions.Any(userPermissions.Contains); } if (pageName.StartsWith("GameResources/")) { - return userPermissions.Contains(PermissionTo.EditGameResources); + relevantPermissions = [PermissionTo.EditGameResources]; + return relevantPermissions.Any(userPermissions.Contains); } if (pageName.StartsWith("System/")) { - return userPermissions.Contains(PermissionTo.EditSystemPages); + relevantPermissions = [PermissionTo.EditSystemPages]; + return relevantPermissions.Any(userPermissions.Contains); } if (pageName.StartsWith(LinkConstants.HomePages)) { + relevantPermissions = [PermissionTo.EditHomePage]; + // A home page is defined as Homepages/[UserName] // If a user can exploit this fact to create an exploit // then we should first reconsider rules about allowed patterns of usernames and what defines a valid wiki page // before deciding to nuke this feature var homepage = pageName[LinkConstants.HomePages.Length..].Split('/')[0]; if (string.Equals(homepage, userName, StringComparison.OrdinalIgnoreCase) - && userPermissions.Contains(PermissionTo.EditHomePage)) + && relevantPermissions.Any(userPermissions.Contains)) { return true; } @@ -61,7 +72,8 @@ public static bool UserCanEditWikiPage(string? pageName, string? userName, IRead // Notice we fall back to EditWikiPages if it is not the user's homepage, regular editors should be able to edit homepages } - return userPermissions.Contains(PermissionTo.EditWikiPages); + relevantPermissions = [PermissionTo.EditWikiPages]; + return relevantPermissions.Any(userPermissions.Contains); } public static bool IsValidWikiPageName(string pageName) diff --git a/TASVideos/Extensions/ClaimsPrincipalExtensions.cs b/TASVideos/Extensions/ClaimsPrincipalExtensions.cs index ee9865766..f611abca2 100644 --- a/TASVideos/Extensions/ClaimsPrincipalExtensions.cs +++ b/TASVideos/Extensions/ClaimsPrincipalExtensions.cs @@ -5,5 +5,5 @@ namespace TASVideos; public static class ClaimsPrincipalExtensions { public static bool CanEditWiki(this ClaimsPrincipal user, string pageName) - => WikiHelper.UserCanEditWikiPage(pageName, user.Name(), user.Permissions()); + => WikiHelper.UserCanEditWikiPage(pageName, user.Name(), user.Permissions(), out _); } diff --git a/TASVideos/Extensions/HttpContextExtensions.cs b/TASVideos/Extensions/HttpContextExtensions.cs index c8d87e15f..ddbf55d30 100644 --- a/TASVideos/Extensions/HttpContextExtensions.cs +++ b/TASVideos/Extensions/HttpContextExtensions.cs @@ -1,4 +1,6 @@ -namespace TASVideos.Extensions; +using TASVideos.Pages; + +namespace TASVideos.Extensions; public static class HttpContextExtensions { @@ -6,4 +8,17 @@ public static string CurrentPathToReturnUrl(this HttpContext? context) { return context is null ? "" : $"{context.Request.Path}{context.Request.QueryString}"; } + + public static void SetRequiredPermissionsView(this HttpContext? context, RequirePermissionsView requiredPermissions) + { + if (context is not null) + { + context.Items["RequiredPermissions"] = requiredPermissions; + } + } + + public static RequirePermissionsView? GetRequiredPermissionsView(this HttpContext? context) + { + return context?.Items["RequiredPermissions"] as RequirePermissionsView; + } } diff --git a/TASVideos/Pages/RequireBase.cs b/TASVideos/Pages/RequireBase.cs index a336fbf50..feeb771ad 100644 --- a/TASVideos/Pages/RequireBase.cs +++ b/TASVideos/Pages/RequireBase.cs @@ -1,4 +1,5 @@ -using System.Net; +using System.Collections.Generic; +using System.Net; using Microsoft.AspNetCore.Mvc.Filters; namespace TASVideos.Pages; @@ -40,4 +41,15 @@ protected static async Task> GetUserPermission return context.HttpContext.User.Permissions(); } + + protected static void SetRequiredPermissionsView(PageHandlerExecutingContext context, HashSet requiredPermissions, bool matchAny) + { + context.HttpContext.SetRequiredPermissionsView(new RequirePermissionsView { Permissions = requiredPermissions, MatchAny = matchAny }); + } +} + +public class RequirePermissionsView +{ + public HashSet Permissions { get; set; } = []; + public bool MatchAny { get; set; } } diff --git a/TASVideos/Pages/RequireEdit.cs b/TASVideos/Pages/RequireEdit.cs index 42399d6b5..3e7633194 100644 --- a/TASVideos/Pages/RequireEdit.cs +++ b/TASVideos/Pages/RequireEdit.cs @@ -28,10 +28,12 @@ public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext contex var userPerms = await GetUserPermissions(context); var canEdit = WikiHelper - .UserCanEditWikiPage(pageToEdit, user.Name(), userPerms); + .UserCanEditWikiPage(pageToEdit, user.Name(), userPerms, out HashSet relevantPermissions); if (canEdit) { + SetRequiredPermissionsView(context, relevantPermissions, matchAny: true); + await next.Invoke(); } else diff --git a/TASVideos/Pages/RequirePermission.cs b/TASVideos/Pages/RequirePermission.cs index ffa18a358..1a6767a44 100644 --- a/TASVideos/Pages/RequirePermission.cs +++ b/TASVideos/Pages/RequirePermission.cs @@ -38,6 +38,7 @@ public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext contex if ((MatchAny && RequiredPermissions.Any(r => userPerms.Contains(r))) || RequiredPermissions.IsSubsetOf(userPerms)) { + SetRequiredPermissionsView(context, RequiredPermissions, MatchAny); await next.Invoke(); } else diff --git a/TASVideos/Pages/Shared/_Layout.cshtml b/TASVideos/Pages/Shared/_Layout.cshtml index 25cd8753b..dec49614e 100644 --- a/TASVideos/Pages/Shared/_Layout.cshtml +++ b/TASVideos/Pages/Shared/_Layout.cshtml @@ -100,6 +100,9 @@ } } + var permissionsRequired = Context.GetRequiredPermissionsView() as RequirePermissionsView; + bool requiresPermissions = permissionsRequired is not null && permissionsRequired.Permissions.Count > 0; +

@@ -112,8 +115,32 @@ @heading } +

+ + }
diff --git a/TASVideos/Pages/Wiki/PageHistory.cshtml b/TASVideos/Pages/Wiki/PageHistory.cshtml index 4a664ee4a..42fc43c9e 100644 --- a/TASVideos/Pages/Wiki/PageHistory.cshtml +++ b/TASVideos/Pages/Wiki/PageHistory.cshtml @@ -4,7 +4,7 @@ ViewData.SetTitle("Page History For " + Model.PageName); ViewData.UseDiff(); bool hasDiff = Model.FromRevision.HasValue && Model.ToRevision.HasValue; - var canEdit = WikiHelper.UserCanEditWikiPage(Model.Path, User.Name(), User.Permissions()); + var canEdit = WikiHelper.UserCanEditWikiPage(Model.Path, User.Name(), User.Permissions(), out _); } @functions { diff --git a/TASVideos/wwwroot/js/site.js b/TASVideos/wwwroot/js/site.js index 3282fdc2e..4ae7664c3 100644 --- a/TASVideos/wwwroot/js/site.js +++ b/TASVideos/wwwroot/js/site.js @@ -50,3 +50,11 @@ if (location.hash) { } } } + +Array.from(document.querySelectorAll('[data-id="permission-modal-button"]')).forEach(popupBtn => { + popupBtn.addEventListener('click', () => { + let modal = new bootstrap.Modal('#permission-modal') + modal.show(); + return false; + }) +}); \ No newline at end of file diff --git a/tests/TASVideos.Test/Extensions/WikiHelperTests.cs b/tests/TASVideos.Test/Extensions/WikiHelperTests.cs index 81b8c3bfc..f070f7641 100644 --- a/tests/TASVideos.Test/Extensions/WikiHelperTests.cs +++ b/tests/TASVideos.Test/Extensions/WikiHelperTests.cs @@ -58,7 +58,8 @@ public void UserCanEditWikiPage( var actual = WikiHelper.UserCanEditWikiPage( pageName, userName, - userPermissions.ToList()); + userPermissions.ToList(), + out _); Assert.AreEqual(expected, actual); }