Repository: btcpayserver/btcpayserver (csharp)

Issue:
Make Role Permissions more human legible
Got feedback from a user in Riga that our Role Permissions could be a bit more human readable, as opposed to simply listing them out as we do.

We could simply list out the more human legible permissions on this view as we do on the Create view...? e.g.
`Can View Invoices`
`Can Modify Invoices`

Tracking, will need to adjust the copy.

<img width="1409" alt="Screen Shot 2024-08-26 at 1 39 04 AM" src="https://github.com/user-attachments/assets/37aa30f1-8301-4fd3-885d-4734f269ef8e">

Pull request description:
Had to rename `CanModifyStoreWebhooks` to `CanModifyWebhooks` for this, but the value stayed the same, so I don't think it's a big deal.

Closes #6183.

![grafik](https://github.com/user-attachments/assets/2fd4bbd9-6557-4269-aefd-01e72f3b1ebb)

Patch:
diff --git a/BTCPayServer.Client/Permissions.cs b/BTCPayServer.Client/Permissions.cs
index 8087a620fb..d8faa3bd74 100644
--- a/BTCPayServer.Client/Permissions.cs
+++ b/BTCPayServer.Client/Permissions.cs
@@ -1,8 +1,10 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Globalization;
 using System.Linq;
 using System.Linq.Expressions;
+using System.Text.RegularExpressions;
 
 namespace BTCPayServer.Client
 {
@@ -16,7 +18,7 @@ public class Policies
         public const string CanUseLightningNodeInStore = "btcpay.store.canuselightningnode";
         public const string CanModifyServerSettings = "btcpay.server.canmodifyserversettings";
         public const string CanModifyStoreSettings = "btcpay.store.canmodifystoresettings";
-        public const string CanModifyStoreWebhooks = "btcpay.store.webhooks.canmodifywebhooks";
+        public const string CanModifyWebhooks = "btcpay.store.webhooks.canmodifywebhooks";
         public const string CanModifyStoreSettingsUnscoped = "btcpay.store.canmodifystoresettings:";
         public const string CanViewStoreSettings = "btcpay.store.canviewstoresettings";
         public const string CanViewReports = "btcpay.store.canviewreports";
@@ -48,7 +50,7 @@ public static IEnumerable<string> AllPolicies
                 yield return CanViewInvoices;
                 yield return CanCreateInvoice;
                 yield return CanModifyInvoices;
-                yield return CanModifyStoreWebhooks;
+                yield return CanModifyWebhooks;
                 yield return CanModifyServerSettings;
                 yield return CanModifyStoreSettings;
                 yield return CanViewStoreSettings;
@@ -104,6 +106,16 @@ public static bool IsUserPolicy(string policy)
         {
             return policy.StartsWith("btcpay.user", StringComparison.OrdinalIgnoreCase);
         }
+
+        private static readonly CultureInfo _culture = new (CultureInfo.InvariantCulture.Name);
+        public static string DisplayName(string policy)
+        {
+            var p = policy.Split(".");
+            if (p.Length < 3 || p[0] != "btcpay") return policy;
+            var constName = typeof(Policies).GetFields().Select(f => f.Name).FirstOrDefault(f => f.Equals(p[^1], StringComparison.OrdinalIgnoreCase));
+            var perm = string.IsNullOrEmpty(constName) ? string.Join(' ', p[2..]) : Regex.Replace(constName, "([A-Z])", " $1", RegexOptions.Compiled).Trim();
+            return $"{_culture.TextInfo.ToTitleCase(p[1])}: {_culture.TextInfo.ToTitleCase(perm)}";
+        }
     }
 
     public class PermissionSet
@@ -247,7 +259,7 @@ private static ReadOnlyDictionary<string, HashSet<string>> Init()
                 Policies.CanManagePullPayments,
                 Policies.CanModifyInvoices,
                 Policies.CanViewStoreSettings,
-                Policies.CanModifyStoreWebhooks,
+                Policies.CanModifyWebhooks,
                 Policies.CanModifyPaymentRequests,
                 Policies.CanManagePayouts,
                 Policies.CanUseLightningNodeInStore);
diff --git a/BTCPayServer/Controllers/GreenField/GreenfieldStoreWebhooksController.cs b/BTCPayServer/Controllers/GreenField/GreenfieldStoreWebhooksController.cs
index 3d9387dd75..f5d7f15fae 100644
--- a/BTCPayServer/Controllers/GreenField/GreenfieldStoreWebhooksController.cs
+++ b/BTCPayServer/Controllers/GreenField/GreenfieldStoreWebhooksController.cs
@@ -24,7 +24,7 @@ namespace BTCPayServer.Controllers.Greenfield
 {
     [ApiController]
     [Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield,
-               Policy = Policies.CanModifyStoreWebhooks)]
+               Policy = Policies.CanModifyWebhooks)]
     [EnableCors(CorsPolicies.All)]
     public class GreenfieldStoreWebhooksController : ControllerBase
     {
diff --git a/BTCPayServer/Controllers/UIManageController.APIKeys.cs b/BTCPayServer/Controllers/UIManageController.APIKeys.cs
index b4d2576353..92f182ab79 100644
--- a/BTCPayServer/Controllers/UIManageController.APIKeys.cs
+++ b/BTCPayServer/Controllers/UIManageController.APIKeys.cs
@@ -513,8 +513,8 @@ public class PermissionValueItem
                     {Policies.CanDeleteUser, ("Delete user", "Allows deleting the user to whom it is assigned. Admin users can delete any user without this permission.")},
                     {Policies.CanModifyStoreSettings, ("Modify your stores", "Allows managing invoices on all your stores and modify their settings.")},
                     {$"{Policies.CanModifyStoreSettings}:", ("Manage selected stores", "Allows managing invoices on the selected stores and modify their settings.")},
-                    {Policies.CanModifyStoreWebhooks, ("Modify stores webhooks", "Allows modifying the webhooks of all your stores.")},
-                    {$"{Policies.CanModifyStoreWebhooks}:", ("Modify selected stores' webhooks", "Allows modifying the webhooks of the selected stores.")},
+                    {Policies.CanModifyWebhooks, ("Modify stores webhooks", "Allows modifying the webhooks of all your stores.")},
+                    {$"{Policies.CanModifyWebhooks}:", ("Modify selected stores' webhooks", "Allows modifying the webhooks of the selected stores.")},
                     {Policies.CanViewStoreSettings, ("View your stores", "Allows viewing stores settings.")},
                     {$"{Policies.CanViewStoreSettings}:", ("View your stores", "Allows viewing the selected stores' settings.")},
                     {Policies.CanViewReports, ("View your reports", "Allows viewing reports.")},
diff --git a/BTCPayServer/Views/Shared/ListRoles.cshtml b/BTCPayServer/Views/Shared/ListRoles.cshtml
index a298819108..ceb2e552c3 100644
--- a/BTCPayServer/Views/Shared/ListRoles.cshtml
+++ b/BTCPayServer/Views/Shared/ListRoles.cshtml
@@ -96,7 +96,7 @@
                     {
                         @foreach (var policy in role.Permissions)
                         {
-                            <code class="d-block text-break">@policy</code>
+                            <code class="d-block text-break">@Policies.DisplayName(policy)</code>
                         }
                     }
                 </td>


Test patch:
diff --git a/BTCPayServer.Tests/BTCPayServer.Tests.csproj b/BTCPayServer.Tests/BTCPayServer.Tests.csproj
index eb427b6193..18e1452d31 100644
--- a/BTCPayServer.Tests/BTCPayServer.Tests.csproj
+++ b/BTCPayServer.Tests/BTCPayServer.Tests.csproj
@@ -34,7 +34,7 @@
     <PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.15" />
     <PackageReference Include="Selenium.Support" Version="4.1.1" />
     <PackageReference Include="Selenium.WebDriver" Version="4.22.0" />
-	<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="125.0.6422.14100" />
+	<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="128.0.6613.11900" />
 	<PackageReference Include="xunit" Version="2.6.6" />
 	<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
       <PrivateAssets>all</PrivateAssets>
diff --git a/BTCPayServer.Tests/GreenfieldAPITests.cs b/BTCPayServer.Tests/GreenfieldAPITests.cs
index 906a5271a7..2d9a2ccc13 100644
--- a/BTCPayServer.Tests/GreenfieldAPITests.cs
+++ b/BTCPayServer.Tests/GreenfieldAPITests.cs
@@ -1711,7 +1711,7 @@ void AssertHook(FakeServer fakeServer, StoreWebhookData hook)
             var user = tester.NewAccount();
             user.GrantAccess();
             user.RegisterDerivationScheme("BTC");
-            var clientProfile = await user.CreateClient(Policies.CanModifyStoreWebhooks, Policies.CanCreateInvoice);
+            var clientProfile = await user.CreateClient(Policies.CanModifyWebhooks, Policies.CanCreateInvoice);
             var hook = await clientProfile.CreateWebhook(user.StoreId, new CreateStoreWebhookRequest()
             {
                 Url = fakeServer.ServerUri.AbsoluteUri,
diff --git a/BTCPayServer.Tests/SeleniumTester.cs b/BTCPayServer.Tests/SeleniumTester.cs
index 7a5a54d6a1..24b66d57a3 100644
--- a/BTCPayServer.Tests/SeleniumTester.cs
+++ b/BTCPayServer.Tests/SeleniumTester.cs
@@ -55,6 +55,7 @@ public async Task StartAsync()
             options.AddArguments($"window-size={windowSize.Width}x{windowSize.Height}");
             options.AddArgument("shm-size=2g");
             options.AddArgument("start-maximized");
+            options.AddArgument("disable-search-engine-choice-screen");
             if (Server.PayTester.InContainer)
             {
                 // Shot in the dark to fix https://stackoverflow.com/questions/53902507/unknown-error-session-deleted-because-of-page-crash-from-unknown-error-cannot
diff --git a/BTCPayServer.Tests/TestAccount.cs b/BTCPayServer.Tests/TestAccount.cs
index eba7b92e89..90889ff8ea 100644
--- a/BTCPayServer.Tests/TestAccount.cs
+++ b/BTCPayServer.Tests/TestAccount.cs
@@ -527,7 +527,7 @@ public async Task SetupWebhook()
         {
             var server = new FakeServer();
             await server.Start();
-            var client = await CreateClient(Policies.CanModifyStoreWebhooks);
+            var client = await CreateClient(Policies.CanModifyWebhooks);
             var wh = await client.CreateWebhook(StoreId, new CreateStoreWebhookRequest()
             {
                 AutomaticRedelivery = false,