From 605741182d594c15a2b00bf020fd21a8fa2ba2f6 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Fri, 1 Dec 2023 09:12:02 +0100 Subject: [PATCH] enhance fine grain permissions (#5502) Co-authored-by: d11n --- .../TagHelpers/PermissionTagHelper.cs | 70 +++++++++++++++---- BTCPayServer.Client/Permissions.cs | 13 +++- BTCPayServer.Tests/SeleniumTests.cs | 3 +- .../Components/MainNav/Default.cshtml | 8 +-- .../Components/StoreSelector/Default.cshtml | 14 +--- .../Components/StoreSelector/StoreSelector.cs | 2 +- .../StoreSelector/StoreSelectorViewModel.cs | 3 +- .../GreenfieldPullPaymentController.cs | 14 ++-- .../GreenField/GreenfieldReportsController.cs | 2 +- .../Controllers/UIAccountController.cs | 1 - BTCPayServer/Controllers/UIHomeController.cs | 12 ++-- .../Controllers/UIManageController.APIKeys.cs | 8 +++ .../Controllers/UIPaymentRequestController.cs | 7 +- .../Controllers/UIReportsController.cs | 6 +- ...torePullPaymentsController.PullPayments.cs | 5 +- .../Controllers/UIStoresController.cs | 25 +++++++ .../EditPaymentRequest.cshtml | 13 ++-- .../GetPaymentRequests.cshtml | 9 +-- .../Views/UIStorePullPayments/Payouts.cshtml | 6 +- .../UIStorePullPayments/PullPayments.cshtml | 13 ++-- BTCPayServer/Views/UIStores/Index.cshtml | 0 .../wwwroot/swagger/v1/swagger.template.json | 4 +- 22 files changed, 167 insertions(+), 71 deletions(-) create mode 100644 BTCPayServer/Views/UIStores/Index.cshtml diff --git a/BTCPayServer.Abstractions/TagHelpers/PermissionTagHelper.cs b/BTCPayServer.Abstractions/TagHelpers/PermissionTagHelper.cs index 436405271..18ae24c53 100644 --- a/BTCPayServer.Abstractions/TagHelpers/PermissionTagHelper.cs +++ b/BTCPayServer.Abstractions/TagHelpers/PermissionTagHelper.cs @@ -2,12 +2,13 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Razor.TagHelpers; -using Microsoft.Extensions.Logging; +using System; +using System.Linq; namespace BTCPayServer.Abstractions.TagHelpers; [HtmlTargetElement(Attributes = "[permission]")] -[HtmlTargetElement(Attributes = "[not-permission]" )] +[HtmlTargetElement(Attributes = "[not-permission]")] public class PermissionTagHelper : TagHelper { private readonly IAuthorizationService _authorizationService; @@ -22,29 +23,72 @@ public class PermissionTagHelper : TagHelper public string Permission { get; set; } public string NotPermission { get; set; } public string PermissionResource { get; set; } - + public bool AndMode { get; set; } = false; public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { - if (string.IsNullOrEmpty(Permission) && string.IsNullOrEmpty(NotPermission)) + var permissions = Permission?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty(); + var notPermissions = NotPermission?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty(); + + if (!permissions.Any() && !notPermissions.Any()) return; if (_httpContextAccessor.HttpContext is null) return; - var expectedResult = !string.IsNullOrEmpty(Permission); - var key = $"{Permission??NotPermission}_{PermissionResource}"; - if (!_httpContextAccessor.HttpContext.Items.TryGetValue(key, out var o) || - o is not AuthorizationResult res) + bool shouldRender = true; // Assume tag should be rendered unless a check fails + + // Process 'Permission' - User must have these permissions + if (permissions.Any()) { - res = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, - PermissionResource, - Permission); - _httpContextAccessor.HttpContext.Items.Add(key, res); + bool finalResult = AndMode; + foreach (var perm in permissions) + { + var key = $"{perm}_{PermissionResource}"; + AuthorizationResult res = await GetOrAddAuthorizationResult(key, perm); + + if (AndMode) + finalResult &= res.Succeeded; + else + finalResult |= res.Succeeded; + + if (!AndMode && finalResult) break; + } + + shouldRender = finalResult; } - if (expectedResult != res.Succeeded) + + // Process 'NotPermission' - User must not have these permissions + if (shouldRender && notPermissions.Any()) + { + foreach (var notPerm in notPermissions) + { + var key = $"{notPerm}_{PermissionResource}"; + AuthorizationResult res = await GetOrAddAuthorizationResult(key, notPerm); + + if (res.Succeeded) // If the user has a 'NotPermission', they should not see the tag + { + shouldRender = false; + break; + } + } + } + + if (!shouldRender) { output.SuppressOutput(); } + } + private async Task GetOrAddAuthorizationResult(string key, string permission) + { + if (!_httpContextAccessor.HttpContext.Items.TryGetValue(key, out var cachedResult)) + { + var res = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, + PermissionResource, permission); + _httpContextAccessor.HttpContext.Items[key] = res; + return res; + } + + return cachedResult as AuthorizationResult; } } diff --git a/BTCPayServer.Client/Permissions.cs b/BTCPayServer.Client/Permissions.cs index 86a834c66..3e5072b5a 100644 --- a/BTCPayServer.Client/Permissions.cs +++ b/BTCPayServer.Client/Permissions.cs @@ -19,6 +19,7 @@ namespace BTCPayServer.Client public const string CanModifyStoreWebhooks = "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"; public const string CanViewInvoices = "btcpay.store.canviewinvoices"; public const string CanCreateInvoice = "btcpay.store.cancreateinvoice"; public const string CanModifyInvoices = "btcpay.store.canmodifyinvoices"; @@ -34,7 +35,10 @@ namespace BTCPayServer.Client public const string CanDeleteUser = "btcpay.user.candeleteuser"; public const string CanManagePullPayments = "btcpay.store.canmanagepullpayments"; public const string CanArchivePullPayments = "btcpay.store.canarchivepullpayments"; + public const string CanManagePayouts = "btcpay.store.canmanagepayouts"; + public const string CanViewPayouts = "btcpay.store.canviewpayouts"; public const string CanCreatePullPayments = "btcpay.store.cancreatepullpayments"; + public const string CanViewPullPayments = "btcpay.store.canviewpullpayments"; public const string CanCreateNonApprovedPullPayments = "btcpay.store.cancreatenonapprovedpullpayments"; public const string CanViewCustodianAccounts = "btcpay.store.canviewcustodianaccounts"; public const string CanManageCustodianAccounts = "btcpay.store.canmanagecustodianaccounts"; @@ -53,6 +57,7 @@ namespace BTCPayServer.Client yield return CanModifyServerSettings; yield return CanModifyStoreSettings; yield return CanViewStoreSettings; + yield return CanViewReports; yield return CanViewPaymentRequests; yield return CanModifyPaymentRequests; yield return CanModifyProfile; @@ -72,6 +77,7 @@ namespace BTCPayServer.Client yield return CanManagePullPayments; yield return CanArchivePullPayments; yield return CanCreatePullPayments; + yield return CanViewPullPayments; yield return CanCreateNonApprovedPullPayments; yield return CanViewCustodianAccounts; yield return CanManageCustodianAccounts; @@ -79,6 +85,8 @@ namespace BTCPayServer.Client yield return CanWithdrawFromCustodianAccounts; yield return CanTradeCustodianAccount; yield return CanManageUsers; + yield return CanManagePayouts; + yield return CanViewPayouts; } } public static bool IsValidPolicy(string policy) @@ -252,11 +260,13 @@ namespace BTCPayServer.Client Policies.CanViewStoreSettings, Policies.CanModifyStoreWebhooks, Policies.CanModifyPaymentRequests, + Policies.CanManagePayouts, Policies.CanUseLightningNodeInStore); PolicyHasChild(policyMap,Policies.CanManageUsers, Policies.CanCreateUser); PolicyHasChild(policyMap,Policies.CanManagePullPayments, Policies.CanCreatePullPayments, Policies.CanArchivePullPayments); PolicyHasChild(policyMap,Policies.CanCreatePullPayments, Policies.CanCreateNonApprovedPullPayments); + PolicyHasChild(policyMap, Policies.CanCreateNonApprovedPullPayments, Policies.CanViewPullPayments); PolicyHasChild(policyMap,Policies.CanModifyPaymentRequests, Policies.CanViewPaymentRequests); PolicyHasChild(policyMap,Policies.CanModifyProfile, Policies.CanViewProfile); PolicyHasChild(policyMap,Policies.CanUseLightningNodeInStore, Policies.CanViewLightningInvoiceInStore, Policies.CanCreateLightningInvoiceInStore); @@ -267,7 +277,8 @@ namespace BTCPayServer.Client PolicyHasChild(policyMap, Policies.CanUseInternalLightningNode, Policies.CanCreateLightningInvoiceInternalNode, Policies.CanViewLightningInvoiceInternalNode); PolicyHasChild(policyMap, Policies.CanManageCustodianAccounts, Policies.CanViewCustodianAccounts); PolicyHasChild(policyMap, Policies.CanModifyInvoices, Policies.CanViewInvoices, Policies.CanCreateInvoice, Policies.CanCreateLightningInvoiceInStore); - PolicyHasChild(policyMap, Policies.CanViewStoreSettings, Policies.CanViewInvoices, Policies.CanViewPaymentRequests); + PolicyHasChild(policyMap, Policies.CanViewStoreSettings, Policies.CanViewInvoices, Policies.CanViewPaymentRequests, Policies.CanViewReports, Policies.CanViewPullPayments, Policies.CanViewPayouts); + PolicyHasChild(policyMap, Policies.CanManagePayouts, Policies.CanViewPayouts); var missingPolicies = Policies.AllPolicies.ToHashSet(); //recurse through the tree to see which policies are not included in the tree diff --git a/BTCPayServer.Tests/SeleniumTests.cs b/BTCPayServer.Tests/SeleniumTests.cs index f4ce7b957..75b232f23 100644 --- a/BTCPayServer.Tests/SeleniumTests.cs +++ b/BTCPayServer.Tests/SeleniumTests.cs @@ -938,8 +938,9 @@ namespace BTCPayServer.Tests Policies.CanModifyServerSettings }); + s.GoToUrl("/logout"); await alice.MakeAdmin(); - s.Logout(); + s.GoToLogin(); s.LogIn(alice.Email, alice.Password); s.GoToUrl($"/cheat/permissions/stores/{alice.StoreId}"); diff --git a/BTCPayServer/Components/MainNav/Default.cshtml b/BTCPayServer/Components/MainNav/Default.cshtml index 868c67de4..23c9d3835 100644 --- a/BTCPayServer/Components/MainNav/Default.cshtml +++ b/BTCPayServer/Components/MainNav/Default.cshtml @@ -135,25 +135,25 @@ Invoices - - - -
  • - @if (option.IsOwner) - { - @StoreName(option.Text) - } - else - { - @StoreName(option.Text) - } + @StoreName(option.Text)
  • } @if (Model.Options.Any()) {
  • } -
  • Create Store
  • +
  • Create Store
  • @if (Model.ArchivedCount > 0) {
  • diff --git a/BTCPayServer/Components/StoreSelector/StoreSelector.cs b/BTCPayServer/Components/StoreSelector/StoreSelector.cs index f541763b6..c99f4b4ee 100644 --- a/BTCPayServer/Components/StoreSelector/StoreSelector.cs +++ b/BTCPayServer/Components/StoreSelector/StoreSelector.cs @@ -48,7 +48,7 @@ namespace BTCPayServer.Components.StoreSelector Value = store.Id, Selected = store.Id == currentStore?.Id, WalletId = walletId, - IsOwner = role != null && role.Permissions.Contains(Policies.CanModifyStoreSettings) + Store = store, }; }) .OrderBy(s => s.Text) diff --git a/BTCPayServer/Components/StoreSelector/StoreSelectorViewModel.cs b/BTCPayServer/Components/StoreSelector/StoreSelectorViewModel.cs index 812693977..6ab4e41e5 100644 --- a/BTCPayServer/Components/StoreSelector/StoreSelectorViewModel.cs +++ b/BTCPayServer/Components/StoreSelector/StoreSelectorViewModel.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using BTCPayServer.Data; namespace BTCPayServer.Components.StoreSelector { @@ -14,9 +15,9 @@ namespace BTCPayServer.Components.StoreSelector public class StoreSelectorOption { public bool Selected { get; set; } - public bool IsOwner { get; set; } public string Text { get; set; } public string Value { get; set; } public WalletId WalletId { get; set; } + public StoreData Store { get; set; } } } diff --git a/BTCPayServer/Controllers/GreenField/GreenfieldPullPaymentController.cs b/BTCPayServer/Controllers/GreenField/GreenfieldPullPaymentController.cs index 7efad4560..93821d620 100644 --- a/BTCPayServer/Controllers/GreenField/GreenfieldPullPaymentController.cs +++ b/BTCPayServer/Controllers/GreenField/GreenfieldPullPaymentController.cs @@ -58,7 +58,7 @@ namespace BTCPayServer.Controllers.Greenfield } [HttpGet("~/api/v1/stores/{storeId}/pull-payments")] - [Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [Authorize(Policy = Policies.CanViewPullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] public async Task GetPullPayments(string storeId, bool includeArchived = false) { using var ctx = _dbContextFactory.CreateContext(); @@ -456,7 +456,7 @@ namespace BTCPayServer.Controllers.Greenfield [HttpGet("~/api/v1/stores/{storeId}/payouts")] - [Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [Authorize(Policy = Policies.CanViewPayouts, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] public async Task GetStorePayouts(string storeId, bool includeCancelled = false) { var payouts = await _pullPaymentService.GetPayouts(new PullPaymentHostedService.PayoutQuery() @@ -471,7 +471,7 @@ namespace BTCPayServer.Controllers.Greenfield } [HttpDelete("~/api/v1/stores/{storeId}/payouts/{payoutId}")] - [Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [Authorize(Policy = Policies.CanManagePayouts, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] public async Task CancelPayout(string storeId, string payoutId) { var res = await _pullPaymentService.Cancel(new PullPaymentHostedService.CancelRequest(new[] { payoutId }, new[] { storeId })); @@ -479,7 +479,7 @@ namespace BTCPayServer.Controllers.Greenfield } [HttpPost("~/api/v1/stores/{storeId}/payouts/{payoutId}")] - [Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [Authorize(Policy = Policies.CanManagePayouts, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] public async Task ApprovePayout(string storeId, string payoutId, ApprovePayoutRequest approvePayoutRequest, CancellationToken cancellationToken = default) { using var ctx = _dbContextFactory.CreateContext(); @@ -533,7 +533,7 @@ namespace BTCPayServer.Controllers.Greenfield } [HttpPost("~/api/v1/stores/{storeId}/payouts/{payoutId}/mark-paid")] - [Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [Authorize(Policy = Policies.CanManagePayouts, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] public async Task MarkPayoutPaid(string storeId, string payoutId, CancellationToken cancellationToken = default) { return await MarkPayout(storeId, payoutId, new Client.Models.MarkPayoutRequest() @@ -544,7 +544,7 @@ namespace BTCPayServer.Controllers.Greenfield } [HttpPost("~/api/v1/stores/{storeId}/payouts/{payoutId}/mark")] - [Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [Authorize(Policy = Policies.CanManagePayouts, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] public async Task MarkPayout(string storeId, string payoutId, Client.Models.MarkPayoutRequest request) { request ??= new(); @@ -571,7 +571,7 @@ namespace BTCPayServer.Controllers.Greenfield } [HttpGet("~/api/v1/stores/{storeId}/payouts/{payoutId}")] - [Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [Authorize(Policy = Policies.CanViewPayouts, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] public async Task GetStorePayout(string storeId, string payoutId) { await using var ctx = _dbContextFactory.CreateContext(); diff --git a/BTCPayServer/Controllers/GreenField/GreenfieldReportsController.cs b/BTCPayServer/Controllers/GreenField/GreenfieldReportsController.cs index 5e280d63e..38fd4d14c 100644 --- a/BTCPayServer/Controllers/GreenField/GreenfieldReportsController.cs +++ b/BTCPayServer/Controllers/GreenField/GreenfieldReportsController.cs @@ -32,7 +32,7 @@ public class GreenfieldReportsController : Controller public ApplicationDbContextFactory DBContextFactory { get; } public ReportService ReportService { get; } - [Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [Authorize(Policy = Policies.CanViewReports, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [HttpPost("~/api/v1/stores/{storeId}/reports")] [NonAction] // Disabling this endpoint as we still need to figure out the request/response model public async Task StoreReports(string storeId, [FromBody] StoreReportRequest? vm = null, CancellationToken cancellationToken = default) diff --git a/BTCPayServer/Controllers/UIAccountController.cs b/BTCPayServer/Controllers/UIAccountController.cs index 8d40d3d18..e6ca2864e 100644 --- a/BTCPayServer/Controllers/UIAccountController.cs +++ b/BTCPayServer/Controllers/UIAccountController.cs @@ -212,7 +212,6 @@ namespace BTCPayServer.Controllers } } - var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: true); if (result.Succeeded) { diff --git a/BTCPayServer/Controllers/UIHomeController.cs b/BTCPayServer/Controllers/UIHomeController.cs index 18e3f6139..2730576a3 100644 --- a/BTCPayServer/Controllers/UIHomeController.cs +++ b/BTCPayServer/Controllers/UIHomeController.cs @@ -197,12 +197,14 @@ namespace BTCPayServer.Controllers { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } - - public RedirectToActionResult RedirectToStore(string userId, StoreData store) + public static RedirectToActionResult RedirectToStore(string userId, StoreData store) { - return store.HasPermission(userId, Policies.CanModifyStoreSettings) - ? RedirectToAction("Dashboard", "UIStores", new { storeId = store.Id }) - : RedirectToAction("ListInvoices", "UIInvoice", new { storeId = store.Id }); + var perms = store.GetPermissionSet(userId); + if (perms.Contains(Policies.CanModifyStoreSettings, store.Id)) + return new RedirectToActionResult("Dashboard", "UIStores", new {storeId = store.Id}); + if (perms.Contains(Policies.CanViewInvoices, store.Id)) + return new RedirectToActionResult("ListInvoices", "UIInvoice", new { storeId = store.Id }); + return new RedirectToActionResult("Index", "UIStores", new {storeId = store.Id}); } } } diff --git a/BTCPayServer/Controllers/UIManageController.APIKeys.cs b/BTCPayServer/Controllers/UIManageController.APIKeys.cs index 314a7b177..7c88329af 100644 --- a/BTCPayServer/Controllers/UIManageController.APIKeys.cs +++ b/BTCPayServer/Controllers/UIManageController.APIKeys.cs @@ -527,6 +527,8 @@ namespace BTCPayServer.Controllers {$"{Policies.CanModifyStoreWebhooks}:", ("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.")}, + {$"{Policies.CanViewReports}:", ("View your selected stores' reports", "Allows viewing the selected stores' reports.")}, {Policies.CanModifyServerSettings, ("Manage your server", "Grants total control on the server settings of your server.")}, {Policies.CanViewProfile, ("View your profile", "Allows viewing your user profile.")}, {Policies.CanModifyProfile, ("Manage your profile", "Allows viewing and modifying your user profile.")}, @@ -542,12 +544,18 @@ namespace BTCPayServer.Controllers {$"{Policies.CanModifyPaymentRequests}:", ("Manage selected stores' payment requests", "Allows viewing, modifying, deleting and creating new payment requests on the selected stores.")}, {Policies.CanViewPaymentRequests, ("View your payment requests", "Allows viewing payment requests.")}, {$"{Policies.CanViewPaymentRequests}:", ("View your payment requests", "Allows viewing the selected stores' payment requests.")}, + {Policies.CanViewPullPayments, ("View your pull payments", "Allows viewing pull payments on all your stores.")}, + {$"{Policies.CanViewPullPayments}:", ("View selected stores' pull payments", "Allows viewing pull payments on the selected stores.")}, {Policies.CanManagePullPayments, ("Manage your pull payments", "Allows viewing, modifying, deleting and creating pull payments on all your stores.")}, {$"{Policies.CanManagePullPayments}:", ("Manage selected stores' pull payments", "Allows viewing, modifying, deleting and creating pull payments on the selected stores.")}, {Policies.CanArchivePullPayments, ("Archive your pull payments", "Allows deleting pull payments on all your stores.")}, {$"{Policies.CanArchivePullPayments}:", ("Archive selected stores' pull payments", "Allows deleting pull payments on the selected stores.")}, {Policies.CanCreatePullPayments, ("Create pull payments", "Allows creating pull payments on all your stores.")}, {$"{Policies.CanCreatePullPayments}:", ("Create pull payments in selected stores", "Allows creating pull payments on the selected stores.")}, + {Policies.CanManagePayouts, ("Manage payouts", "Allows managing payouts on all your stores.")}, + {$"{Policies.CanManagePayouts}:", ("Manage payouts in selected stores", "Allows managing payouts on the selected stores.")}, + {Policies.CanViewPayouts, ("View payouts", "Allows viewing payouts on all your stores.")}, + {$"{Policies.CanViewPayouts}:", ("View payouts in selected stores", "Allows viewing payouts on the selected stores.")}, {Policies.CanCreateNonApprovedPullPayments, ("Create non-approved pull payments", "Allows creating pull payments without automatic approval on all your stores.")}, {$"{Policies.CanCreateNonApprovedPullPayments}:", ("Create non-approved pull payments in selected stores", "Allows viewing, modifying, deleting and creating pull payments without automatic approval on the selected stores.")}, {Policies.CanUseInternalLightningNode, ("Use the internal lightning node", "Allows using the internal BTCPay Server lightning node to create BOLT11 invoices, connect to other nodes, open new channels and pay BOLT11 invoices.")}, diff --git a/BTCPayServer/Controllers/UIPaymentRequestController.cs b/BTCPayServer/Controllers/UIPaymentRequestController.cs index 895569c6f..dddc7ff4c 100644 --- a/BTCPayServer/Controllers/UIPaymentRequestController.cs +++ b/BTCPayServer/Controllers/UIPaymentRequestController.cs @@ -71,8 +71,9 @@ namespace BTCPayServer.Controllers FormDataService = formDataService; } - [BitpayAPIConstraint(false)] + [HttpGet("/stores/{storeId}/payment-requests")] + [Authorize(Policy = Policies.CanViewPaymentRequests, AuthenticationSchemes = AuthenticationSchemes.Cookie)] public async Task GetPaymentRequests(string storeId, ListPaymentRequestsViewModel model = null) { model = this.ParseListQuery(model ?? new ListPaymentRequestsViewModel()); @@ -105,6 +106,7 @@ namespace BTCPayServer.Controllers } [HttpGet("/stores/{storeId}/payment-requests/edit/{payReqId?}")] + [Authorize(Policy = Policies.CanViewPaymentRequests, AuthenticationSchemes = AuthenticationSchemes.Cookie)] public async Task EditPaymentRequest(string storeId, string payReqId) { var store = GetCurrentStore(); @@ -127,6 +129,7 @@ namespace BTCPayServer.Controllers } [HttpPost("/stores/{storeId}/payment-requests/edit/{payReqId?}")] + [Authorize(Policy = Policies.CanModifyPaymentRequests, AuthenticationSchemes = AuthenticationSchemes.Cookie)] public async Task EditPaymentRequest(string payReqId, UpdatePaymentRequestViewModel viewModel) { if (!string.IsNullOrEmpty(viewModel.Currency) && @@ -386,6 +389,7 @@ namespace BTCPayServer.Controllers } [HttpGet("{payReqId}/clone")] + [Authorize(Policy = Policies.CanModifyPaymentRequests, AuthenticationSchemes = AuthenticationSchemes.Cookie)] public async Task ClonePaymentRequest(string payReqId) { var store = GetCurrentStore(); @@ -405,6 +409,7 @@ namespace BTCPayServer.Controllers } [HttpGet("{payReqId}/archive")] + [Authorize(Policy = Policies.CanModifyPaymentRequests, AuthenticationSchemes = AuthenticationSchemes.Cookie)] public async Task TogglePaymentRequestArchival(string payReqId) { var store = GetCurrentStore(); diff --git a/BTCPayServer/Controllers/UIReportsController.cs b/BTCPayServer/Controllers/UIReportsController.cs index 5a0cf5ab4..00a768f73 100644 --- a/BTCPayServer/Controllers/UIReportsController.cs +++ b/BTCPayServer/Controllers/UIReportsController.cs @@ -18,7 +18,7 @@ using Newtonsoft.Json.Linq; namespace BTCPayServer.Controllers; -[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] +[Authorize(Policy = Policies.CanViewReports, AuthenticationSchemes = AuthenticationSchemes.Cookie)] [AutoValidateAntiforgeryToken] public partial class UIReportsController : Controller { @@ -49,7 +49,7 @@ public partial class UIReportsController : Controller [HttpPost("stores/{storeId}/reports")] [AcceptMediaTypeConstraint("application/json")] - [Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] + [Authorize(Policy = Policies.CanViewReports, AuthenticationSchemes = AuthenticationSchemes.Cookie)] [IgnoreAntiforgeryToken] public async Task StoreReportsJson(string storeId, [FromBody] StoreReportRequest? request = null, bool fakeData = false, CancellationToken cancellation = default) { @@ -64,7 +64,7 @@ public partial class UIReportsController : Controller [HttpGet("stores/{storeId}/reports")] [AcceptMediaTypeConstraint("text/html")] - [Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] + [Authorize(Policy = Policies.CanViewReports, AuthenticationSchemes = AuthenticationSchemes.Cookie)] public IActionResult StoreReports( string storeId, string ? viewName = null) diff --git a/BTCPayServer/Controllers/UIStorePullPaymentsController.PullPayments.cs b/BTCPayServer/Controllers/UIStorePullPaymentsController.PullPayments.cs index 8d868e6c2..b2f9f8498 100644 --- a/BTCPayServer/Controllers/UIStorePullPaymentsController.PullPayments.cs +++ b/BTCPayServer/Controllers/UIStorePullPaymentsController.PullPayments.cs @@ -28,7 +28,7 @@ using StoreData = BTCPayServer.Data.StoreData; namespace BTCPayServer.Controllers { - [Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] + [Authorize(Policy = Policies.CanViewPullPayments, AuthenticationSchemes = AuthenticationSchemes.Cookie)] [AutoValidateAntiforgeryToken] public class UIStorePullPaymentsController : Controller { @@ -278,7 +278,7 @@ namespace BTCPayServer.Controllers return RedirectToAction(nameof(PullPayments), new { storeId }); } - [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] + [Authorize(Policy = Policies.CanManagePayouts, AuthenticationSchemes = AuthenticationSchemes.Cookie)] [HttpPost("stores/{storeId}/pull-payments/payouts")] [HttpPost("stores/{storeId}/pull-payments/{pullPaymentId}/payouts")] [HttpPost("stores/{storeId}/payouts")] @@ -472,6 +472,7 @@ namespace BTCPayServer.Controllers [HttpGet("stores/{storeId}/pull-payments/{pullPaymentId}/payouts")] [HttpGet("stores/{storeId}/payouts")] + [Authorize(Policy = Policies.CanViewPayouts, AuthenticationSchemes = AuthenticationSchemes.Cookie)] public async Task Payouts( string storeId, string pullPaymentId, string paymentMethodId, PayoutState payoutState, int skip = 0, int count = 50) diff --git a/BTCPayServer/Controllers/UIStoresController.cs b/BTCPayServer/Controllers/UIStoresController.cs index 2a8ab5e50..e0b2af5c5 100644 --- a/BTCPayServer/Controllers/UIStoresController.cs +++ b/BTCPayServer/Controllers/UIStoresController.cs @@ -37,6 +37,7 @@ using StoreData = BTCPayServer.Data.StoreData; namespace BTCPayServer.Controllers { + [Route("stores")] [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)] [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] @@ -127,6 +128,30 @@ namespace BTCPayServer.Controllers { get; set; } + + [AllowAnonymous] + [HttpGet("{storeId}/index")] + public async Task Index(string storeId) + { + var userId = _UserManager.GetUserId(User); + if(userId is null) + return Forbid(); + var store = await _Repo.FindStore(storeId, _UserManager.GetUserId(User)); + if (store is null) + { + return Forbid(); + } + if (store.GetPermissionSet(userId).Contains(Policies.CanModifyStoreSettings, storeId)) + { + return RedirectToAction("Dashboard", new { storeId }); + } + if (store.GetPermissionSet(userId).Contains(Policies.CanViewInvoices, storeId)) + { + return RedirectToAction("ListInvoices", "UIInvoice", new { storeId }); + } + HttpContext.SetStoreData(store); + return View(); + } [HttpGet] [Route("{storeId}/users")] diff --git a/BTCPayServer/Views/UIPaymentRequest/EditPaymentRequest.cshtml b/BTCPayServer/Views/UIPaymentRequest/EditPaymentRequest.cshtml index d8c846812..8b004ba5c 100644 --- a/BTCPayServer/Views/UIPaymentRequest/EditPaymentRequest.cshtml +++ b/BTCPayServer/Views/UIPaymentRequest/EditPaymentRequest.cshtml @@ -1,7 +1,7 @@ @using BTCPayServer.Services.PaymentRequests @using System.Globalization +@using BTCPayServer.Client @using BTCPayServer.Forms -@using BTCPayServer.Services.Stores @using BTCPayServer.TagHelpers @using Microsoft.AspNetCore.Mvc.TagHelpers @inject FormDataService FormDataService @@ -28,11 +28,11 @@
    @if (string.IsNullOrEmpty(Model.Id)) { - + } else { - + View }
    @@ -152,18 +152,19 @@ {
    Invoices - Clone + Clone @if (!Model.Archived) { - Archive + Archive } else { - Unarchive + Unarchive }
    } diff --git a/BTCPayServer/Views/UIPaymentRequest/GetPaymentRequests.cshtml b/BTCPayServer/Views/UIPaymentRequest/GetPaymentRequests.cshtml index eab74ac30..2d5e3e3ed 100644 --- a/BTCPayServer/Views/UIPaymentRequest/GetPaymentRequests.cshtml +++ b/BTCPayServer/Views/UIPaymentRequest/GetPaymentRequests.cshtml @@ -2,6 +2,7 @@ @using Microsoft.AspNetCore.Html @using Microsoft.AspNetCore.Mvc.TagHelpers @using BTCPayServer.Components +@using BTCPayServer.Client @model BTCPayServer.Models.PaymentRequestViewModels.ListPaymentRequestsViewModel @{ Layout = "_Layout"; @@ -29,7 +30,7 @@ - + Create Request @@ -117,10 +118,10 @@ diff --git a/BTCPayServer/Views/UIStorePullPayments/Payouts.cshtml b/BTCPayServer/Views/UIStorePullPayments/Payouts.cshtml index c1e7908d4..e228cc262 100644 --- a/BTCPayServer/Views/UIStorePullPayments/Payouts.cshtml +++ b/BTCPayServer/Views/UIStorePullPayments/Payouts.cshtml @@ -73,9 +73,9 @@

    Payouts allow you to process pull payments, in the form of refunds, salary payouts, or withdrawals. - You can also + You can also configure payout processors - to automate payouts. + to automate payouts.

    Learn More
    @@ -93,7 +93,7 @@ PaymentMethods = new[] { Model.PaymentMethodId } })).Any()) { -