diff --git a/BTCPayServer.Tests/PlaywrightTests.cs b/BTCPayServer.Tests/PlaywrightTests.cs index d2008bfef..899595093 100644 --- a/BTCPayServer.Tests/PlaywrightTests.cs +++ b/BTCPayServer.Tests/PlaywrightTests.cs @@ -2463,6 +2463,124 @@ namespace BTCPayServer.Tests Assert.Equal(spentOutpoint, tx.Inputs[0].PrevOut); } + [Fact] + [Trait("Playwright", "Playwright")] + [Trait("Lightning", "Lightning")] + public async Task CanAccessUserStoreAsAdmin() + { + await using var s = CreatePlaywrightTester(newDb: true); + s.Server.ActivateLightning(); + await s.StartAsync(); + await s.Server.EnsureChannelsSetup(); + + // Setup user, store and wallets + await s.RegisterNewUser(); + var (_, storeId) = await s.CreateNewStore(); + await s.GoToStore(); + await s.GenerateWallet(isHotWallet: true); + await s.AddLightningNode(LightningConnectionType.CLightning, false); + + // Add apps + await s.CreateApp("PointOfSale"); + await s.CreateApp("Crowdfund"); + await s.Logout(); + + // Setup admin and check access + await s.GoToRegister(); + await s.RegisterNewUser(true); + string GetStorePath(string subPath) => $"/stores/{storeId}/{subPath}"; + + // Admin access + await s.AssertPageAccess(false, GetStorePath("")); + await s.AssertPageAccess(true, GetStorePath("reports")); + await s.AssertPageAccess(true, GetStorePath("invoices")); + await s.AssertPageAccess(false, GetStorePath("invoices/create")); + await s.AssertPageAccess(true, GetStorePath("payment-requests")); + await s.AssertPageAccess(false, GetStorePath("payment-requests/edit")); + await s.AssertPageAccess(true, GetStorePath("pull-payments")); + await s.AssertPageAccess(true, GetStorePath("payouts")); + await s.AssertPageAccess(false, GetStorePath("onchain/BTC")); + await s.AssertPageAccess(false, GetStorePath("onchain/BTC/settings")); + await s.AssertPageAccess(false, GetStorePath("lightning/BTC")); + await s.AssertPageAccess(false, GetStorePath("lightning/BTC/settings")); + await s.AssertPageAccess(false, GetStorePath("apps/create")); + + var storeSettingsPaths = new [] {"settings", "rates", "checkout", "tokens", "users", "roles", "webhooks", + "payout-processors", "payout-processors/onchain-automated/BTC", "payout-processors/lightning-automated/BTC", + "emails/rules", "email-settings", "forms"}; + foreach (var path in storeSettingsPaths) + { // should have view access to settings, but no submit buttons or create links + s.TestLogs.LogInformation($"Checking access to store page {path} as admin"); + await s.AssertPageAccess(true, $"stores/{storeId}/{path}"); + if (path != "payout-processors") + { + Assert.Equal(0, await s.Page.Locator("#mainContent .btn-primary").CountAsync()); + } + } + } + + [Fact] + [Trait("Playwright", "Playwright")] + public async Task CanChangeUserRoles() + { + await using var s = CreatePlaywrightTester(newDb: true); + await s.StartAsync(); + + // Setup users and store + var employee = await s.RegisterNewUser(); + await s.GoToHome(); + await s.Logout(); + await s.GoToRegister(); + var owner = await s.RegisterNewUser(true); + var (_, storeId) = await s.CreateNewStore(); + await s.GoToStore(); + await s.AddUserToStore(storeId, employee, "Employee"); + + // Should successfully change the role + var userRows = await s.Page.Locator("#StoreUsersList tr").AllAsync(); + Assert.Equal(2, userRows.Count); + ILocator employeeRow = null; + foreach (var row in userRows) + { + if ((await row.InnerTextAsync()).Contains(employee, StringComparison.InvariantCultureIgnoreCase)) employeeRow = row; + } + Assert.NotNull(employeeRow); + await employeeRow.Locator("a[data-bs-target='#EditModal']").ClickAsync(); + Assert.Equal(employee, await s.Page.InnerTextAsync("#EditUserEmail")); + await s.Page.SelectOptionAsync("#EditUserRole", "Manager"); + await s.Page.ClickAsync("#EditContinue"); + await s.FindAlertMessage(partialText: $"The role of {employee} has been changed to Manager."); + + // Should not see a message when not changing role + userRows = await s.Page.Locator("#StoreUsersList tr").AllAsync(); + Assert.Equal(2, userRows.Count); + employeeRow = null; + foreach (var row in userRows) + { + if ((await row.InnerTextAsync()).Contains(employee, StringComparison.InvariantCultureIgnoreCase)) employeeRow = row; + } + Assert.NotNull(employeeRow); + await employeeRow.Locator("a[data-bs-target='#EditModal']").ClickAsync(); + Assert.Equal(employee, await s.Page.InnerTextAsync("#EditUserEmail")); + await s.Page.ClickAsync("#EditContinue"); + await s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error, "The user already has the role Manager."); + + // Should not change last owner + userRows = await s.Page.Locator("#StoreUsersList tr").AllAsync(); + Assert.Equal(2, userRows.Count); + ILocator ownerRow = null; + foreach (var row in userRows) + { + if ((await row.InnerTextAsync()).Contains(owner, StringComparison.InvariantCultureIgnoreCase)) ownerRow = row; + } + Assert.NotNull(ownerRow); + await ownerRow.Locator("a[data-bs-target='#EditModal']").ClickAsync(); + Assert.Equal(owner, await s.Page.InnerTextAsync("#EditUserEmail")); + await s.Page.SelectOptionAsync("#EditUserRole", "Employee"); + await s.Page.ClickAsync("#EditContinue"); + await s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error, "The user is the last owner. Their role cannot be changed."); + } + } } diff --git a/BTCPayServer.Tests/SeleniumTests.cs b/BTCPayServer.Tests/SeleniumTests.cs index 7d4f8fe1c..6da6f99da 100644 --- a/BTCPayServer.Tests/SeleniumTests.cs +++ b/BTCPayServer.Tests/SeleniumTests.cs @@ -1567,125 +1567,6 @@ retry: Assert.DoesNotContain(Policies.CanModifyServerSettings,s.Driver.PageSource); } - [Fact] - [Trait("Selenium", "Selenium")] - [Trait("Lightning", "Lightning")] - public async Task CanAccessUserStoreAsAdmin() - { - using var s = CreateSeleniumTester(newDb: true); - s.Server.ActivateLightning(); - await s.StartAsync(); - await s.Server.EnsureChannelsSetup(); - - // Setup user, store and wallets - s.RegisterNewUser(); - (_, string storeId) = s.CreateNewStore(); - s.GoToStore(); - s.GenerateWallet(isHotWallet: true); - s.AddLightningNode(LightningConnectionType.CLightning, false); - - // Add apps - (_, string _) = s.CreateApp("PointOfSale"); - (_, string _) = s.CreateApp("Crowdfund"); - s.Logout(); - - // Setup admin and check access - s.GoToRegister(); - s.RegisterNewUser(true); - string GetStorePath(string subPath) => $"/stores/{storeId}/{subPath}"; - - // Admin access - s.AssertPageAccess(false, GetStorePath("")); - s.AssertPageAccess(true, GetStorePath("reports")); - s.AssertPageAccess(true, GetStorePath("invoices")); - s.AssertPageAccess(false, GetStorePath("invoices/create")); - s.AssertPageAccess(true, GetStorePath("payment-requests")); - s.AssertPageAccess(false, GetStorePath("payment-requests/edit")); - s.AssertPageAccess(true, GetStorePath("pull-payments")); - s.AssertPageAccess(true, GetStorePath("payouts")); - s.AssertPageAccess(false, GetStorePath("onchain/BTC")); - s.AssertPageAccess(false, GetStorePath("onchain/BTC/settings")); - s.AssertPageAccess(false, GetStorePath("lightning/BTC")); - s.AssertPageAccess(false, GetStorePath("lightning/BTC/settings")); - s.AssertPageAccess(false, GetStorePath("apps/create")); - - var storeSettingsPaths = new [] {"settings", "rates", "checkout", "tokens", "users", "roles", "webhooks", - "payout-processors", "payout-processors/onchain-automated/BTC", "payout-processors/lightning-automated/BTC", - "emails/rules", "email-settings", "forms"}; - foreach (var path in storeSettingsPaths) - { // should have view access to settings, but no submit buttons or create links - TestLogs.LogInformation($"Checking access to store page {path} as admin"); - s.AssertPageAccess(true, $"stores/{storeId}/{path}"); - if (path != "payout-processors") - { - s.Driver.ElementDoesNotExist(By.CssSelector("#mainContent .btn-primary")); - } - } - } - - [Fact] - [Trait("Selenium", "Selenium")] - public async Task CanChangeUserRoles() - { - using var s = CreateSeleniumTester(newDb: true); - await s.StartAsync(); - - // Setup users and store - var employee = s.RegisterNewUser(); - s.Logout(); - s.GoToRegister(); - var owner = s.RegisterNewUser(true); - (_, string storeId) = s.CreateNewStore(); - s.GoToStore(); - s.AddUserToStore(storeId, employee, "Employee"); - - // Should successfully change the role - var userRows = s.Driver.FindElements(By.CssSelector("#StoreUsersList tr")); - Assert.Equal(2, userRows.Count); - IWebElement employeeRow = null; - foreach (var row in userRows) - { - if (row.Text.Contains(employee, StringComparison.InvariantCultureIgnoreCase)) employeeRow = row; - } - Assert.NotNull(employeeRow); - employeeRow.FindElement(By.CssSelector("a[data-bs-target=\"#EditModal\"]")).Click(); - Assert.Equal(s.Driver.WaitForElement(By.Id("EditUserEmail")).Text, employee); - new SelectElement(s.Driver.FindElement(By.Id("EditUserRole"))).SelectByValue("Manager"); - s.Driver.FindElement(By.Id("EditContinue")).Click(); - Assert.Contains($"The role of {employee} has been changed to Manager.", s.FindAlertMessage().Text); - - // Should not see a message when not changing role - userRows = s.Driver.FindElements(By.CssSelector("#StoreUsersList tr")); - Assert.Equal(2, userRows.Count); - employeeRow = null; - foreach (var row in userRows) - { - if (row.Text.Contains(employee, StringComparison.InvariantCultureIgnoreCase)) employeeRow = row; - } - Assert.NotNull(employeeRow); - employeeRow.FindElement(By.CssSelector("a[data-bs-target=\"#EditModal\"]")).Click(); - Assert.Equal(s.Driver.WaitForElement(By.Id("EditUserEmail")).Text, employee); - // no change, no alert message - s.Driver.FindElement(By.Id("EditContinue")).Click(); - Assert.Contains("The user already has the role Manager.", s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error).Text); - - // Should not change last owner - userRows = s.Driver.FindElements(By.CssSelector("#StoreUsersList tr")); - Assert.Equal(2, userRows.Count); - IWebElement ownerRow = null; - foreach (var row in userRows) - { - if (row.Text.Contains(owner, StringComparison.InvariantCultureIgnoreCase)) ownerRow = row; - } - Assert.NotNull(ownerRow); - ownerRow.FindElement(By.CssSelector("a[data-bs-target=\"#EditModal\"]")).Click(); - Assert.Equal(s.Driver.WaitForElement(By.Id("EditUserEmail")).Text, owner); - new SelectElement(s.Driver.FindElement(By.Id("EditUserRole"))).SelectByValue("Employee"); - s.Driver.FindElement(By.Id("EditContinue")).Click(); - Assert.Contains("The user is the last owner. Their role cannot be changed.", s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error).Text); - } - - private static string AssertUrlHasPairingCode(SeleniumTester s) {