Remove unused scope, assert policy on store listing

This commit is contained in:
nicolas.dorier 2019-10-18 23:42:06 +09:00
parent 8643c04a39
commit da2e8665a1
No known key found for this signature in database
GPG key ID: 6618763EF09186FE
9 changed files with 76 additions and 194 deletions

View file

@ -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<StoreData[]>(results["access_token"],
$"api/test/me/stores",
tester.PayTester.HttpClient);
Assert.NotEmpty(stores);
Assert.True(await TestApiAgainstAccessToken<bool>(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<NoSuchElementException>(() => 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<bool>(results["access_token"],
$"api/test/ScopeCanViewApps",
tester.PayTester.HttpClient));
Assert.True(await TestApiAgainstAccessToken<bool>(results["access_token"],
$"api/test/ScopeCanManageApps",
tester.PayTester.HttpClient));
Assert.True(await TestApiAgainstAccessToken<bool>(results["access_token"],
$"api/test/ScopeCanViewStores",
tester.PayTester.HttpClient));
await Assert.ThrowsAnyAsync<HttpRequestException>(async () =>
{
await TestApiAgainstAccessToken<bool>(results["access_token"],
$"api/test/ScopeCanManageStores",
tester.PayTester.HttpClient);
await TestApiAgainstAccessToken<StoreData[]>(results["access_token"],
$"api/test/me/stores",
tester.PayTester.HttpClient);
});
await Assert.ThrowsAnyAsync<HttpRequestException>(async () =>
{
await TestApiAgainstAccessToken<bool>(results["access_token"],
$"api/test/ScopeCanViewProfile",
tester.PayTester.HttpClient);
$"api/test/me/stores/{stores[0].Id}/can-edit",
tester.PayTester.HttpClient);
});
}
}

View file

@ -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);
}
}

View file

@ -48,6 +48,8 @@ namespace BTCPayServer.Controllers.RestApi
}
[HttpGet("me/stores")]
[Authorize(Policy = Policies.CanModifyStoreSettings.Key,
AuthenticationSchemes = AuthenticationSchemes.OpenId)]
public async Task<StoreData[]> 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
}
}

View file

@ -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);

View file

@ -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
{
}
}

View file

@ -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())

View file

@ -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))

View file

@ -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;
}

View file

@ -1,20 +1,13 @@
@using BTCPayServer.Authentication
@using BTCPayServer.Security.OpenId
@using OpenIddict.Abstractions
@model BTCPayServer.Models.Authorization.AuthorizeViewModel
@{
var scopeMappings = new Dictionary<string, (string Title, string Description)>()
{
{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")},
};
}
<form method="post">
<input type="hidden" name="request_id" value="@Model.RequestId"/>