diff --git a/BTCPayServer.Tests/AuthenticationTests.cs b/BTCPayServer.Tests/AuthenticationTests.cs index 1fdb02629..c47ccb2a4 100644 --- a/BTCPayServer.Tests/AuthenticationTests.cs +++ b/BTCPayServer.Tests/AuthenticationTests.cs @@ -133,50 +133,51 @@ namespace BTCPayServer.Tests results = url.Split("#").Last().Split("&").ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); await TestApiAgainstAccessToken(results["access_token"], tester, user); - LogoutFlow(tester, id, s); + var stores = await TestApiAgainstAccessToken(results["access_token"], + $"api/test/me/stores", + tester.PayTester.HttpClient); + Assert.NotEmpty(stores); + + Assert.True(await TestApiAgainstAccessToken(results["access_token"], + $"api/test/me/stores/{stores[0].Id}/can-edit", + tester.PayTester.HttpClient)); + //we dont ask for consent after acquiring it the first time for the same scopes. + LogoutFlow(tester, id, s); s.Driver.Navigate().GoToUrl(implicitAuthorizeUrl); s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); - - Assert.Throws(() => s.Driver.FindElement(By.Id("consent-yes"))); - results = url.Split("#").Last().Split("&") - .ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); - await TestApiAgainstAccessToken(results["access_token"], tester, user); - - //let's test out scopes! + s.Driver.AssertElementNotFound(By.Id("consent-yes")); + + // Let's asks without scopes + LogoutFlow(tester, id, s); + id = Guid.NewGuid().ToString(); + openIdClient = await user.RegisterOpenIdClient( + new OpenIddictApplicationDescriptor() + { + ClientId = id, + DisplayName = id, + Permissions = { OpenIddictConstants.Permissions.GrantTypes.Implicit }, + RedirectUris = { redirecturi }, + }); implicitAuthorizeUrl = new Uri(tester.PayTester.ServerUri, - $"connect/authorize?response_type=token&client_id={id}&redirect_uri={redirecturi.AbsoluteUri}&scope=openid {RestAPIPolicies.BTCPayScopes.AppManagement} {RestAPIPolicies.BTCPayScopes.ViewStores} &nonce={Guid.NewGuid().ToString()}"); - + $"connect/authorize?response_type=token&client_id={id}&redirect_uri={redirecturi.AbsoluteUri}&scope=openid&nonce={Guid.NewGuid().ToString()}"); s.Driver.Navigate().GoToUrl(implicitAuthorizeUrl); - //authorize form should show now that we have asked for more scopes + s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); s.Driver.FindElement(By.Id("consent-yes")).Click(); - url = s.Driver.Url; - results = url.Split("#").Last().Split("&") + results = s.Driver.Url.Split("#").Last().Split("&") .ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); - - Assert.True(await TestApiAgainstAccessToken(results["access_token"], - $"api/test/ScopeCanViewApps", - tester.PayTester.HttpClient)); - - Assert.True(await TestApiAgainstAccessToken(results["access_token"], - $"api/test/ScopeCanManageApps", - tester.PayTester.HttpClient)); - - Assert.True(await TestApiAgainstAccessToken(results["access_token"], - $"api/test/ScopeCanViewStores", - tester.PayTester.HttpClient)); await Assert.ThrowsAnyAsync(async () => { - await TestApiAgainstAccessToken(results["access_token"], - $"api/test/ScopeCanManageStores", - tester.PayTester.HttpClient); + await TestApiAgainstAccessToken(results["access_token"], + $"api/test/me/stores", + tester.PayTester.HttpClient); }); await Assert.ThrowsAnyAsync(async () => { await TestApiAgainstAccessToken(results["access_token"], - $"api/test/ScopeCanViewProfile", - tester.PayTester.HttpClient); + $"api/test/me/stores/{stores[0].Id}/can-edit", + tester.PayTester.HttpClient); }); } } diff --git a/BTCPayServer/Authentication/BTCPayScopes.cs b/BTCPayServer/Authentication/BTCPayScopes.cs deleted file mode 100644 index 0d790530b..000000000 --- a/BTCPayServer/Authentication/BTCPayScopes.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using OpenIddict.Abstractions; - -namespace BTCPayServer.Authentication -{ - public static class RestAPIPolicies - { - public static class BTCPayScopes - { - public const string ViewStores = "view_stores"; - - //Create and manage stores - public const string StoreManagement = "store_management"; - - //create and manage invoices - public const string ViewInvoices = "view_invoices"; - - //create and manage invoices - public const string CreateInvoices = "create_invoice"; - - public const string InvoiceManagement = "manage_invoices"; - - //view apps - public const string ViewApps = "view_apps"; - - //create and manage apps - public const string AppManagement = "app_management"; - public const string WalletManagement = "wallet_management"; - public const string ServerManagement = "server_management"; - } - - public const string CanViewStores = nameof(CanViewStores); - public const string CanManageStores = nameof(CanManageStores); - public const string CanViewInvoices = nameof(CanViewInvoices); - public const string CanCreateInvoices = nameof(CanCreateInvoices); - public const string CanManageInvoices = nameof(CanManageInvoices); - public const string CanManageApps = nameof(CanManageApps); - public const string CanViewApps = nameof(CanViewApps); - public const string CanManageWallet = nameof(CanManageWallet); - public const string CanViewProfile = nameof(CanViewProfile); - } -} diff --git a/BTCPayServer/Controllers/RestApi/TestController.cs b/BTCPayServer/Controllers/RestApi/TestController.cs index d4044bf49..1e020704f 100644 --- a/BTCPayServer/Controllers/RestApi/TestController.cs +++ b/BTCPayServer/Controllers/RestApi/TestController.cs @@ -48,6 +48,8 @@ namespace BTCPayServer.Controllers.RestApi } [HttpGet("me/stores")] + [Authorize(Policy = Policies.CanModifyStoreSettings.Key, + AuthenticationSchemes = AuthenticationSchemes.OpenId)] public async Task GetCurrentUserStores() { return await _storeRepository.GetStoresByUserId(_userManager.GetUserId(User)); @@ -61,56 +63,5 @@ namespace BTCPayServer.Controllers.RestApi { return true; } - - -#region scopes tests - - [Authorize(Policy = RestAPIPolicies.CanViewStores, - AuthenticationSchemes = AuthenticationSchemes.OpenId)] - [HttpGet(nameof(ScopeCanViewStores))] - public bool ScopeCanViewStores() { return true; } - - [Authorize(Policy = RestAPIPolicies.CanManageStores, - AuthenticationSchemes = AuthenticationSchemes.OpenId)] - [HttpGet(nameof(ScopeCanManageStores))] - public bool ScopeCanManageStores() { return true; } - - [Authorize(Policy = RestAPIPolicies.CanViewInvoices, - AuthenticationSchemes = AuthenticationSchemes.OpenId)] - [HttpGet(nameof(ScopeCanViewInvoices))] - public bool ScopeCanViewInvoices() { return true; } - - [Authorize(Policy = RestAPIPolicies.CanCreateInvoices, - AuthenticationSchemes = AuthenticationSchemes.OpenId)] - [HttpGet(nameof(ScopeCanCreateInvoices))] - public bool ScopeCanCreateInvoices() { return true; } - - [Authorize(Policy = RestAPIPolicies.CanManageInvoices, - AuthenticationSchemes = AuthenticationSchemes.OpenId)] - [HttpGet(nameof(ScopeCanManageInvoices))] - public bool ScopeCanManageInvoices() { return true; } - - [Authorize(Policy = RestAPIPolicies.CanManageApps, - AuthenticationSchemes = AuthenticationSchemes.OpenId)] - [HttpGet(nameof(ScopeCanManageApps))] - public bool ScopeCanManageApps() { return true; } - - [Authorize(Policy = RestAPIPolicies.CanViewApps, - AuthenticationSchemes = AuthenticationSchemes.OpenId)] - - [HttpGet(nameof(ScopeCanViewApps))] - public bool ScopeCanViewApps() { return true; } - - [Authorize(Policy = RestAPIPolicies.CanManageWallet, - AuthenticationSchemes = AuthenticationSchemes.OpenId)] - [HttpGet(nameof(ScopeCanManageWallet))] - public bool ScopeCanManageWallet() { return true; } - - [Authorize(Policy = RestAPIPolicies.CanViewProfile, - AuthenticationSchemes = AuthenticationSchemes.OpenId)] - [HttpGet(nameof(ScopeCanViewProfile))] - public bool ScopeCanViewProfile() { return true; } - -#endregion } } diff --git a/BTCPayServer/Hosting/Startup.cs b/BTCPayServer/Hosting/Startup.cs index dec0bf3c0..ed6b37572 100644 --- a/BTCPayServer/Hosting/Startup.cs +++ b/BTCPayServer/Hosting/Startup.cs @@ -184,13 +184,9 @@ namespace BTCPayServer.Hosting options.RegisterScopes( OpenIddictConstants.Scopes.OpenId, - RestAPIPolicies.BTCPayScopes.ViewStores, - RestAPIPolicies.BTCPayScopes.CreateInvoices, - RestAPIPolicies.BTCPayScopes.StoreManagement, - RestAPIPolicies.BTCPayScopes.ViewApps, - RestAPIPolicies.BTCPayScopes.ServerManagement, - RestAPIPolicies.BTCPayScopes.AppManagement - ); + BTCPayScopes.StoreManagement, + BTCPayScopes.ServerManagement + ); options.AddEventHandler(PasswordGrantTypeEventHandler.Descriptor); options.AddEventHandler(OpenIdGrantHandlerCheckCanSignIn.Descriptor); options.AddEventHandler(ClientCredentialsGrantTypeEventHandler.Descriptor); diff --git a/BTCPayServer/Security/OpenId/BTCPayScopes.cs b/BTCPayServer/Security/OpenId/BTCPayScopes.cs new file mode 100644 index 000000000..71d92e24e --- /dev/null +++ b/BTCPayServer/Security/OpenId/BTCPayScopes.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using OpenIddict.Abstractions; + +namespace BTCPayServer.Security.OpenId +{ + public static class BTCPayScopes + { + public const string StoreManagement = "store_management"; + public const string ServerManagement = "server_management"; + } + public static class RestAPIPolicies + { + + } +} diff --git a/BTCPayServer/Security/OpenId/OpenIdExtensions.cs b/BTCPayServer/Security/OpenId/OpenIdExtensions.cs index e696facbc..c37cce64a 100644 --- a/BTCPayServer/Security/OpenId/OpenIdExtensions.cs +++ b/BTCPayServer/Security/OpenId/OpenIdExtensions.cs @@ -11,7 +11,6 @@ using Microsoft.AspNetCore.Identity; using OpenIddict.Abstractions; using OpenIddict.Core; using OpenIddict.Server; -using static BTCPayServer.Authentication.RestAPIPolicies; namespace BTCPayServer.Security.OpenId { @@ -93,7 +92,7 @@ namespace BTCPayServer.Security.OpenId await Task.WhenAll(scopeTasks.Select((tuple) => tuple.Item1)); var authorizationsWithSufficientScopes = scopeTasks - .Select((tuple) => (tuple.Id, Scopes: tuple.Item1.Result)) + .Select((tuple) => (Id: tuple.Id, Scopes: tuple.Item1.Result)) .Where((tuple) => !request.GetScopes().Except(tuple.Scopes).Any()); if (authorizationsWithSufficientScopes.Any()) diff --git a/BTCPayServer/Security/OpenIdAuthorizationHandler.cs b/BTCPayServer/Security/OpenIdAuthorizationHandler.cs index bd42947f5..3b0abf02d 100644 --- a/BTCPayServer/Security/OpenIdAuthorizationHandler.cs +++ b/BTCPayServer/Security/OpenIdAuthorizationHandler.cs @@ -11,8 +11,9 @@ using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Authentication; using BTCPayServer.Authentication; using Microsoft.Extensions.Primitives; -using static BTCPayServer.Authentication.RestAPIPolicies; +using static BTCPayServer.Security.OpenId.RestAPIPolicies; using OpenIddict.Abstractions; +using BTCPayServer.Security.OpenId; namespace BTCPayServer.Security { @@ -38,33 +39,6 @@ namespace BTCPayServer.Security bool success = false; switch (requirement.Policy) { - case RestAPIPolicies.CanViewStores: - success = context.HasScopes(BTCPayScopes.StoreManagement) || context.HasScopes(BTCPayScopes.ViewStores); - break; - case RestAPIPolicies.CanManageStores: - success = context.HasScopes(BTCPayScopes.StoreManagement); - break; - case RestAPIPolicies.CanViewInvoices: - success = context.HasScopes(BTCPayScopes.ViewInvoices) || context.HasScopes(BTCPayScopes.InvoiceManagement); - break; - case RestAPIPolicies.CanCreateInvoices: - success = context.HasScopes(BTCPayScopes.CreateInvoices) || context.HasScopes(BTCPayScopes.InvoiceManagement); - break; - case RestAPIPolicies.CanViewApps: - success = context.HasScopes(BTCPayScopes.AppManagement) || context.HasScopes(BTCPayScopes.ViewApps); - break; - case RestAPIPolicies.CanManageInvoices: - success = context.HasScopes(BTCPayScopes.InvoiceManagement); - break; - case RestAPIPolicies.CanManageApps: - success = context.HasScopes(BTCPayScopes.AppManagement); - break; - case RestAPIPolicies.CanManageWallet: - success = context.HasScopes(BTCPayScopes.WalletManagement); - break; - case RestAPIPolicies.CanViewProfile: - success = context.HasScopes(OpenIddictConstants.Scopes.Profile); - break; case Policies.CanModifyStoreSettings.Key: if (!context.HasScopes(BTCPayScopes.StoreManagement)) break; @@ -73,15 +47,20 @@ namespace BTCPayServer.Security // to the access_token string storeId = _HttpContext.GetImplicitStoreId(); if (storeId == null) - break; - var userid = _userManager.GetUserId(context.User); - if (string.IsNullOrEmpty(userid)) - break; - var store = await _storeRepository.FindStore((string)storeId, userid); - if (store == null) - break; - success = true; - _HttpContext.SetStoreData(store); + { + success = true; + } + else + { + var userid = _userManager.GetUserId(context.User); + if (string.IsNullOrEmpty(userid)) + break; + var store = await _storeRepository.FindStore((string)storeId, userid); + if (store == null) + break; + success = true; + _HttpContext.SetStoreData(store); + } break; case Policies.CanModifyServerSettings.Key: if (!context.HasScopes(BTCPayScopes.ServerManagement)) diff --git a/BTCPayServer/Security/Policies.cs b/BTCPayServer/Security/Policies.cs index 69f015a1a..8ea09d01d 100644 --- a/BTCPayServer/Security/Policies.cs +++ b/BTCPayServer/Security/Policies.cs @@ -15,16 +15,6 @@ namespace BTCPayServer.Security options.AddPolicy(CanCreateInvoice.Key); options.AddPolicy(CanGetRates.Key); options.AddPolicy(CanModifyServerSettings.Key); - - options.AddPolicy(RestAPIPolicies.CanCreateInvoices); - options.AddPolicy(RestAPIPolicies.CanManageApps); - options.AddPolicy(RestAPIPolicies.CanManageInvoices); - options.AddPolicy(RestAPIPolicies.CanManageStores); - options.AddPolicy(RestAPIPolicies.CanManageWallet); - options.AddPolicy(RestAPIPolicies.CanViewApps); - options.AddPolicy(RestAPIPolicies.CanViewInvoices); - options.AddPolicy(RestAPIPolicies.CanViewProfile); - options.AddPolicy(RestAPIPolicies.CanViewStores); return options; } diff --git a/BTCPayServer/Views/Authorization/Authorize.cshtml b/BTCPayServer/Views/Authorization/Authorize.cshtml index ac2923135..f4d9cb170 100644 --- a/BTCPayServer/Views/Authorization/Authorize.cshtml +++ b/BTCPayServer/Views/Authorization/Authorize.cshtml @@ -1,20 +1,13 @@ -@using BTCPayServer.Authentication +@using BTCPayServer.Security.OpenId @using OpenIddict.Abstractions @model BTCPayServer.Models.Authorization.AuthorizeViewModel @{ var scopeMappings = new Dictionary() { - {RestAPIPolicies.BTCPayScopes.AppManagement, ("Manage your apps", "The app will be able to create, modify and delete all your apps.")}, - {RestAPIPolicies.BTCPayScopes.ViewApps, ("View your apps", "The app will be able to list and view all your apps.")}, - {RestAPIPolicies.BTCPayScopes.CreateInvoices, ("Create invoices", "The app will be able to create new invoices.")}, - {RestAPIPolicies.BTCPayScopes.InvoiceManagement, ("Manage invoices", "The app will be able to create new invoices and mark existing invoices as complete or invalid.")}, - {RestAPIPolicies.BTCPayScopes.StoreManagement, ("Manage your stores", "The app will be able to create, modify and delete all your stores.")}, - {RestAPIPolicies.BTCPayScopes.ViewStores, ("View your stores", "The app will be able to list and view all your stores.")}, - {RestAPIPolicies.BTCPayScopes.WalletManagement, ("Manage your wallet", "The app will be able to manage your wallet associate to stores. This includes configuring it, transaction creation and signing.")}, - {RestAPIPolicies.BTCPayScopes.ServerManagement, ("Manage your server", "The app will have total control on your server")}, - {RestAPIPolicies.BTCPayScopes.ViewInvoices, ("View your invoices", "The app will be able to list and view all your apps.")}, - }; + {BTCPayScopes.StoreManagement, ("Manage your stores", "The app will be able to create, modify and delete all your stores.")}, + {BTCPayScopes.ServerManagement, ("Manage your server", "The app will have total control on your server")}, + }; }