mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-20 13:34:37 +01:00
Refactor authorizations
This commit is contained in:
parent
bd94b5f84e
commit
281a2461ad
55 changed files with 732 additions and 646 deletions
|
@ -73,7 +73,6 @@ namespace BTCPayServer.Data
|
|||
}
|
||||
|
||||
[NotMapped]
|
||||
[Obsolete]
|
||||
public string Role
|
||||
{
|
||||
get; set;
|
||||
|
@ -88,9 +87,6 @@ namespace BTCPayServer.Data
|
|||
public string DefaultCrypto { get; set; }
|
||||
public List<PairedSINData> PairedSINs { get; set; }
|
||||
public IEnumerable<APIKeyData> APIKeys { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public List<Claim> AdditionalClaims { get; set; } = new List<Claim>();
|
||||
}
|
||||
|
||||
public enum NetworkFeeMode
|
||||
|
|
|
@ -25,6 +25,7 @@ using Newtonsoft.Json;
|
|||
using Newtonsoft.Json.Linq;
|
||||
using OpenIddict.Abstractions;
|
||||
using OpenQA.Selenium;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
|
@ -89,6 +90,7 @@ namespace BTCPayServer.Tests
|
|||
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.MakeAdmin();
|
||||
var token = await RegisterPasswordClientAndGetAccessToken(user, null, tester);
|
||||
await TestApiAgainstAccessToken(token, tester, user);
|
||||
token = await RegisterPasswordClientAndGetAccessToken(user, "secret", tester);
|
||||
|
@ -205,6 +207,7 @@ namespace BTCPayServer.Tests
|
|||
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.MakeAdmin();
|
||||
var id = Guid.NewGuid().ToString();
|
||||
var redirecturi = new Uri("http://127.0.0.1/oidc-callback");
|
||||
var secret = "secret";
|
||||
|
@ -399,7 +402,7 @@ namespace BTCPayServer.Tests
|
|||
$"api/test/me/stores/{testAccount.StoreId}/can-edit",
|
||||
tester.PayTester.HttpClient));
|
||||
|
||||
Assert.Equal(testAccount.RegisterDetails.IsAdmin, await TestApiAgainstAccessToken<bool>(accessToken,
|
||||
Assert.True(await TestApiAgainstAccessToken<bool>(accessToken,
|
||||
$"api/test/me/is-admin",
|
||||
tester.PayTester.HttpClient));
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ using BTCPayServer.Services;
|
|||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||
using System.Threading.Tasks;
|
||||
using AuthenticationSchemes = BTCPayServer.Security.AuthenticationSchemes;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
|
@ -295,7 +296,7 @@ namespace BTCPayServer.Tests
|
|||
public string SSHPassword { get; internal set; }
|
||||
public string SSHKeyFile { get; internal set; }
|
||||
public string SSHConnection { get; set; }
|
||||
public T GetController<T>(string userId = null, string storeId = null, Claim[] additionalClaims = null) where T : Controller
|
||||
public T GetController<T>(string userId = null, string storeId = null, bool isAdmin = false) where T : Controller
|
||||
{
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.Host = new HostString("127.0.0.1", Port);
|
||||
|
@ -305,9 +306,9 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
List<Claim> claims = new List<Claim>();
|
||||
claims.Add(new Claim(OpenIdConnectConstants.Claims.Subject, userId));
|
||||
if (additionalClaims != null)
|
||||
claims.AddRange(additionalClaims);
|
||||
context.User = new ClaimsPrincipal(new ClaimsIdentity(claims.ToArray(), Policies.CookieAuthentication));
|
||||
if (isAdmin)
|
||||
claims.Add(new Claim(ClaimTypes.Role, Roles.ServerAdmin));
|
||||
context.User = new ClaimsPrincipal(new ClaimsIdentity(claims.ToArray(), AuthenticationSchemes.Cookie));
|
||||
}
|
||||
if (storeId != null)
|
||||
{
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace BTCPayServer.Tests
|
|||
var controller = tester.PayTester.GetController<StoresController>(user.UserId, user.StoreId);
|
||||
|
||||
|
||||
var storeBlob = controller.StoreData.GetStoreBlob();
|
||||
var storeBlob = controller.CurrentStore.GetStoreBlob();
|
||||
Assert.Null(storeBlob.ChangellySettings);
|
||||
|
||||
var updateModel = new UpdateChangellySettingsViewModel()
|
||||
|
@ -55,7 +55,7 @@ namespace BTCPayServer.Tests
|
|||
await controller.UpdateChangellySettings(user.StoreId, updateModel, "save")).ActionName);
|
||||
|
||||
var store = await tester.PayTester.StoreRepository.FindStore(user.StoreId);
|
||||
storeBlob = controller.StoreData.GetStoreBlob();
|
||||
storeBlob = controller.CurrentStore.GetStoreBlob();
|
||||
Assert.NotNull(storeBlob.ChangellySettings);
|
||||
Assert.NotNull(storeBlob.ChangellySettings);
|
||||
Assert.IsType<ChangellySettings>(storeBlob.ChangellySettings);
|
||||
|
@ -224,6 +224,7 @@ namespace BTCPayServer.Tests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Fast", "Fast")]
|
||||
public void CanComputeBaseAmount()
|
||||
{
|
||||
|
|
|
@ -24,30 +24,8 @@ namespace BTCPayServer.Tests
|
|||
Logs.Tester = new XUnitLog(helper) {Name = "Tests"};
|
||||
Logs.LogProvider = new XUnitLogProvider(helper);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
public async Task CanCreateInvoice()
|
||||
{
|
||||
using (var s = SeleniumTester.Create())
|
||||
{
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser();
|
||||
var store = s.CreateNewStore().storeName;
|
||||
s.AddDerivationScheme();
|
||||
|
||||
s.CreateInvoice(store);
|
||||
|
||||
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
s.Driver.Navigate().Back();
|
||||
s.Driver.FindElement(By.ClassName("invoice-checkout-link")).Click();
|
||||
Assert.NotEmpty(s.Driver.FindElements(By.Id("checkoutCtrl")));
|
||||
s.Driver.Quit();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanHandleRefundEmailForm()
|
||||
{
|
||||
|
||||
|
@ -63,7 +41,12 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
s.GoToHome();
|
||||
var invoiceId = s.CreateInvoice(store.storeName);
|
||||
s.GoToInvoiceCheckout(invoiceId);
|
||||
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
s.Driver.Navigate().Back();
|
||||
s.Driver.FindElement(By.ClassName("invoice-checkout-link")).Click();
|
||||
Assert.NotEmpty(s.Driver.FindElements(By.Id("checkoutCtrl")));
|
||||
|
||||
Assert.True(s.Driver.FindElement(By.Id("emailAddressFormInput")).Displayed);
|
||||
s.Driver.FindElement(By.Id("emailAddressFormInput")).SendKeys("xxx");
|
||||
s.Driver.FindElement(By.Id("emailAddressForm")).FindElement(By.CssSelector("button.action-button"))
|
||||
|
@ -74,14 +57,11 @@ namespace BTCPayServer.Tests
|
|||
Assert.Contains("form-input-invalid", formInput.GetAttribute("class"));
|
||||
formInput = s.Driver.FindElement(By.Id("emailAddressFormInput"));
|
||||
formInput.SendKeys("@g.com");
|
||||
|
||||
s.Driver.FindElement(By.Id("emailAddressForm")).FindElement(By.CssSelector("button.action-button"))
|
||||
.Click();
|
||||
await Task.Delay(1000);
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
|
||||
s.Driver.Navigate().Refresh();
|
||||
var actionButton = s.Driver.FindElement(By.Id("emailAddressForm")).FindElement(By.CssSelector("button.action-button"));
|
||||
actionButton.Click();
|
||||
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
s.Driver.Navigate().Refresh();
|
||||
s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace BTCPayServer.Tests
|
|||
var controller = tester.PayTester.GetController<StoresController>(user.UserId, user.StoreId);
|
||||
|
||||
|
||||
var storeBlob = controller.StoreData.GetStoreBlob();
|
||||
var storeBlob = controller.CurrentStore.GetStoreBlob();
|
||||
Assert.Null(storeBlob.CoinSwitchSettings);
|
||||
|
||||
var updateModel = new UpdateCoinSwitchSettingsViewModel()
|
||||
|
@ -42,7 +42,7 @@ namespace BTCPayServer.Tests
|
|||
await controller.UpdateCoinSwitchSettings(user.StoreId, updateModel, "save")).ActionName);
|
||||
|
||||
var store = await tester.PayTester.StoreRepository.FindStore(user.StoreId);
|
||||
storeBlob = controller.StoreData.GetStoreBlob();
|
||||
storeBlob = controller.CurrentStore.GetStoreBlob();
|
||||
Assert.NotNull(storeBlob.CoinSwitchSettings);
|
||||
Assert.NotNull(storeBlob.CoinSwitchSettings);
|
||||
Assert.IsType<CoinSwitchSettings>(storeBlob.CoinSwitchSettings);
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using System.Text;
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -68,20 +70,26 @@ namespace BTCPayServer.Tests
|
|||
return Assert.IsType<T>(vr.Model);
|
||||
}
|
||||
|
||||
public static IWebElement AssertElementNotFound(this IWebDriver driver, By by)
|
||||
public static void AssertElementNotFound(this IWebDriver driver, By by)
|
||||
{
|
||||
try
|
||||
{
|
||||
var webElement = driver.FindElement(by);
|
||||
Assert.False(webElement.Displayed);
|
||||
return webElement;
|
||||
}
|
||||
catch (NoSuchElementException)
|
||||
{
|
||||
|
||||
}
|
||||
DateTimeOffset now = DateTimeOffset.Now;
|
||||
var wait = SeleniumTester.ImplicitWait;
|
||||
|
||||
return null;
|
||||
while (DateTimeOffset.UtcNow - now < wait)
|
||||
{
|
||||
try
|
||||
{
|
||||
var webElement = driver.FindElement(by);
|
||||
if (!webElement.Displayed)
|
||||
return;
|
||||
}
|
||||
catch (NoSuchElementException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
Assert.False(true, "Elements was found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace BTCPayServer.Tests
|
|||
Assert.Equal("paid", invoice.Status);
|
||||
});
|
||||
|
||||
var walletController = tester.PayTester.GetController<WalletsController>(user.UserId);
|
||||
var walletController = user.GetController<WalletsController>();
|
||||
var walletId = new WalletId(user.StoreId, "BTC");
|
||||
var sendDestination = new Key().PubKey.Hash.GetAddress(user.SupportedNetwork.NBitcoinNetwork).ToString();
|
||||
var sendModel = new WalletSendModel()
|
||||
|
|
|
@ -65,11 +65,12 @@ namespace BTCPayServer.Tests
|
|||
Logs.Tester.LogInformation("Selenium: Using chrome driver");
|
||||
Logs.Tester.LogInformation("Selenium: Browsing to " + Server.PayTester.ServerUri);
|
||||
Logs.Tester.LogInformation($"Selenium: Resolution {Driver.Manage().Window.Size}");
|
||||
Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
|
||||
Driver.Manage().Timeouts().ImplicitWait = ImplicitWait;
|
||||
Driver.Navigate().GoToUrl(Server.PayTester.ServerUri);
|
||||
Driver.AssertNoError();
|
||||
}
|
||||
|
||||
public static readonly TimeSpan ImplicitWait = TimeSpan.FromSeconds(10);
|
||||
public string Link(string relativeLink)
|
||||
{
|
||||
return Server.PayTester.ServerUri.AbsoluteUri.WithoutEndingSlash() + relativeLink.WithStartingSlash();
|
||||
|
|
|
@ -8,6 +8,7 @@ using OpenQA.Selenium.Interactions;
|
|||
using System.Linq;
|
||||
using NBitcoin;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
|
@ -233,8 +234,56 @@ namespace BTCPayServer.Tests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
public async Task CanUsePairing()
|
||||
{
|
||||
using (var s = SeleniumTester.Create())
|
||||
{
|
||||
await s.StartAsync();
|
||||
s.Driver.Navigate().GoToUrl(s.Link("/api-access-request"));
|
||||
Assert.Contains("ReturnUrl", s.Driver.Url);
|
||||
|
||||
var alice = s.RegisterNewUser();
|
||||
var store = s.CreateNewStore().storeName;
|
||||
s.AddDerivationScheme();
|
||||
|
||||
s.Driver.FindElement(By.Id("Tokens")).Click();
|
||||
s.Driver.FindElement(By.Id("CreateNewToken")).Click();
|
||||
s.Driver.FindElement(By.Id("RequestPairing")).Click();
|
||||
|
||||
var regex = Regex.Match(new Uri(s.Driver.Url, UriKind.Absolute).Query, "pairingCode=([^&]*)");
|
||||
Assert.True(regex.Success, $"{s.Driver.Url} does not match expected regex");
|
||||
var pairingCode = regex.Groups[1].Value;
|
||||
|
||||
s.Driver.FindElement(By.Id("ApprovePairing")).Click();
|
||||
Assert.Contains(pairingCode, s.Driver.PageSource);
|
||||
|
||||
var client = new NBitpayClient.Bitpay(new Key(), s.Server.PayTester.ServerUri);
|
||||
await client.AuthorizeClient(new NBitpayClient.PairingCode(pairingCode));
|
||||
await client.CreateInvoiceAsync(new NBitpayClient.Invoice()
|
||||
{
|
||||
Price = 0.000000012m,
|
||||
Currency = "USD",
|
||||
FullNotifications = true
|
||||
}, NBitpayClient.Facade.Merchant);
|
||||
|
||||
client = new NBitpayClient.Bitpay(new Key(), s.Server.PayTester.ServerUri);
|
||||
|
||||
var code = await client.RequestClientAuthorizationAsync("hehe", NBitpayClient.Facade.Merchant);
|
||||
s.Driver.Navigate().GoToUrl(code.CreateLink(s.Server.PayTester.ServerUri));
|
||||
s.Driver.FindElement(By.Id("ApprovePairing")).Click();
|
||||
|
||||
await client.CreateInvoiceAsync(new NBitpayClient.Invoice()
|
||||
{
|
||||
Price = 0.000000012m,
|
||||
Currency = "USD",
|
||||
FullNotifications = true
|
||||
}, NBitpayClient.Facade.Merchant);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
public async Task CanCreateAppPoS()
|
||||
{
|
||||
|
@ -316,7 +365,7 @@ namespace BTCPayServer.Tests
|
|||
using (var s = SeleniumTester.Create())
|
||||
{
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser();
|
||||
s.RegisterNewUser(true);
|
||||
s.CreateNewStore();
|
||||
|
||||
// In this test, we try to spend from a manual seed. We import the xpub 49'/0'/0', then try to use the seed
|
||||
|
@ -332,6 +381,10 @@ namespace BTCPayServer.Tests
|
|||
|
||||
s.ClickOnAllSideMenus();
|
||||
|
||||
// Make sure we can rescan, because we are admin!
|
||||
s.Driver.FindElement(By.Id("WalletRescan")).ForceClick();
|
||||
Assert.Contains("The batch size make sure", s.Driver.PageSource);
|
||||
|
||||
// We setup the fingerprint and the account key path
|
||||
s.Driver.FindElement(By.Id("WalletSettings")).ForceClick();
|
||||
s.Driver.FindElement(By.Id("AccountKeys_0__MasterFingerprint")).SendKeys("8bafd160");
|
||||
|
|
|
@ -20,6 +20,7 @@ using BTCPayServer.Lightning.CLightning;
|
|||
using BTCPayServer.Data;
|
||||
using OpenIddict.Abstractions;
|
||||
using OpenIddict.Core;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
|
@ -37,6 +38,13 @@ namespace BTCPayServer.Tests
|
|||
GrantAccessAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task MakeAdmin()
|
||||
{
|
||||
var userManager = parent.PayTester.GetService<UserManager<ApplicationUser>>();
|
||||
var u = await userManager.FindByIdAsync(UserId);
|
||||
await userManager.AddToRoleAsync(u, Roles.ServerAdmin);
|
||||
}
|
||||
|
||||
public void Register()
|
||||
{
|
||||
RegisterAsync().GetAwaiter().GetResult();
|
||||
|
@ -78,7 +86,8 @@ namespace BTCPayServer.Tests
|
|||
|
||||
public T GetController<T>(bool setImplicitStore = true) where T : Controller
|
||||
{
|
||||
return parent.PayTester.GetController<T>(UserId, setImplicitStore ? StoreId : null);
|
||||
var controller = parent.PayTester.GetController<T>(UserId, setImplicitStore ? StoreId : null, IsAdmin);
|
||||
return controller;
|
||||
}
|
||||
|
||||
public async Task CreateStoreAsync()
|
||||
|
@ -140,6 +149,7 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
get; set;
|
||||
}
|
||||
public bool IsAdmin { get; internal set; }
|
||||
|
||||
public void RegisterLightningNode(string cryptoCode, LightningConnectionType connectionType)
|
||||
{
|
||||
|
|
|
@ -745,7 +745,7 @@ namespace BTCPayServer.Tests
|
|||
pairingCode = acc.BitPay.RequestClientAuthorization("test1", Facade.Merchant);
|
||||
acc.CreateStore();
|
||||
var store2 = acc.GetController<StoresController>();
|
||||
store2.Pair(pairingCode.ToString(), store2.StoreData.Id).GetAwaiter().GetResult();
|
||||
await store2.Pair(pairingCode.ToString(), store2.CurrentStore.Id);
|
||||
Assert.Contains(nameof(PairingResult.ReusedKey), store2.StatusMessage, StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
@ -780,7 +780,7 @@ namespace BTCPayServer.Tests
|
|||
var btcDerivationScheme = acc.DerivationScheme;
|
||||
acc.RegisterDerivationScheme("LTC", true);
|
||||
|
||||
var walletController = tester.PayTester.GetController<WalletsController>(acc.UserId);
|
||||
var walletController = acc.GetController<WalletsController>();
|
||||
WalletId walletId = new WalletId(acc.StoreId, "LTC");
|
||||
var rescan = Assert.IsType<RescanWalletModel>(Assert.IsType<ViewResult>(walletController.WalletRescan(walletId).Result).Model);
|
||||
Assert.False(rescan.Ok);
|
||||
|
@ -789,8 +789,9 @@ namespace BTCPayServer.Tests
|
|||
Assert.False(rescan.IsServerAdmin);
|
||||
|
||||
walletId = new WalletId(acc.StoreId, "BTC");
|
||||
var serverAdminClaim = new[] { new Claim(Policies.CanModifyServerSettings.Key, "true") };
|
||||
walletController = tester.PayTester.GetController<WalletsController>(acc.UserId, additionalClaims: serverAdminClaim);
|
||||
acc.IsAdmin = true;
|
||||
walletController = acc.GetController<WalletsController>();
|
||||
|
||||
rescan = Assert.IsType<RescanWalletModel>(Assert.IsType<ViewResult>(walletController.WalletRescan(walletId).Result).Model);
|
||||
Assert.True(rescan.Ok);
|
||||
Assert.True(rescan.IsFullySync);
|
||||
|
@ -921,32 +922,32 @@ namespace BTCPayServer.Tests
|
|||
acc.RegisterDerivationScheme("LTC");
|
||||
|
||||
var rateController = acc.GetController<RateController>();
|
||||
var GetBaseCurrencyRatesResult = JObject.Parse(((JsonResult)rateController.GetBaseCurrencyRates("BTC", acc.StoreId, default)
|
||||
var GetBaseCurrencyRatesResult = JObject.Parse(((JsonResult)rateController.GetBaseCurrencyRates("BTC", default)
|
||||
.GetAwaiter().GetResult()).Value.ToJson()).ToObject<DataWrapper<Rate[]>>();
|
||||
Assert.NotNull(GetBaseCurrencyRatesResult);
|
||||
Assert.NotNull(GetBaseCurrencyRatesResult.Data);
|
||||
Assert.Equal(2, GetBaseCurrencyRatesResult.Data.Length);
|
||||
Assert.Single(GetBaseCurrencyRatesResult.Data.Where(o => o.Code == "LTC"));
|
||||
|
||||
var GetRatesResult = JObject.Parse(((JsonResult)rateController.GetRates(null, acc.StoreId, default)
|
||||
var GetRatesResult = JObject.Parse(((JsonResult)rateController.GetRates(null, default)
|
||||
.GetAwaiter().GetResult()).Value.ToJson()).ToObject<DataWrapper<Rate[]>>();
|
||||
// We don't have any default currencies, so this should be failing
|
||||
Assert.Null(GetRatesResult?.Data);
|
||||
|
||||
var store = acc.GetController<StoresController>();
|
||||
var ratesVM = (RatesViewModel)(Assert.IsType<ViewResult>(store.Rates(acc.StoreId)).Model);
|
||||
var ratesVM = (RatesViewModel)(Assert.IsType<ViewResult>(store.Rates()).Model);
|
||||
ratesVM.DefaultCurrencyPairs = "BTC_USD,LTC_USD";
|
||||
store.Rates(ratesVM).Wait();
|
||||
store = acc.GetController<StoresController>();
|
||||
rateController = acc.GetController<RateController>();
|
||||
GetRatesResult = JObject.Parse(((JsonResult)rateController.GetRates(null, acc.StoreId, default)
|
||||
GetRatesResult = JObject.Parse(((JsonResult)rateController.GetRates(null, default)
|
||||
.GetAwaiter().GetResult()).Value.ToJson()).ToObject<DataWrapper<Rate[]>>();
|
||||
// Now we should have a result
|
||||
Assert.NotNull(GetRatesResult);
|
||||
Assert.NotNull(GetRatesResult.Data);
|
||||
Assert.Equal(2, GetRatesResult.Data.Length);
|
||||
|
||||
var GetCurrencyPairRateResult = JObject.Parse(((JsonResult)rateController.GetCurrencyPairRate("BTC", "LTC", acc.StoreId, default)
|
||||
var GetCurrencyPairRateResult = JObject.Parse(((JsonResult)rateController.GetCurrencyPairRate("BTC", "LTC", default)
|
||||
.GetAwaiter().GetResult()).Value.ToJson()).ToObject<DataWrapper<Rate>>();
|
||||
|
||||
Assert.NotNull(GetCurrencyPairRateResult);
|
||||
|
@ -957,7 +958,9 @@ namespace BTCPayServer.Tests
|
|||
var rates = acc.BitPay.GetRates();
|
||||
HttpClient client = new HttpClient();
|
||||
// Unauthentified requests should also be ok
|
||||
var response = client.GetAsync($"http://127.0.0.1:{tester.PayTester.Port}/api/rates?storeId={acc.StoreId}").GetAwaiter().GetResult();
|
||||
var response = await client.GetAsync($"http://127.0.0.1:{tester.PayTester.Port}/api/rates?storeId={acc.StoreId}");
|
||||
response.EnsureSuccessStatusCode();
|
||||
response = await client.GetAsync($"http://127.0.0.1:{tester.PayTester.Port}/rates?storeId={acc.StoreId}");
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
}
|
||||
|
@ -1226,7 +1229,7 @@ namespace BTCPayServer.Tests
|
|||
private static decimal CreateInvoice(ServerTester tester, TestAccount user, string exchange, string currency = "USD")
|
||||
{
|
||||
var storeController = user.GetController<StoresController>();
|
||||
var vm = (RatesViewModel)((ViewResult)storeController.Rates(user.StoreId)).Model;
|
||||
var vm = (RatesViewModel)((ViewResult)storeController.Rates()).Model;
|
||||
vm.PreferredExchange = exchange;
|
||||
storeController.Rates(vm).Wait();
|
||||
var invoice2 = user.BitPay.CreateInvoice(new Invoice()
|
||||
|
@ -1252,12 +1255,12 @@ namespace BTCPayServer.Tests
|
|||
user.GrantAccess();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
|
||||
Logs.Tester.LogInformation("StoreId without anyone can create invoice = 401");
|
||||
Logs.Tester.LogInformation("StoreId without anyone can create invoice = 403");
|
||||
var response = await tester.PayTester.HttpClient.SendAsync(new HttpRequestMessage(HttpMethod.Post, $"invoices?storeId={user.StoreId}")
|
||||
{
|
||||
Content = new StringContent("{\"Price\": 5000, \"currency\": \"USD\"}", Encoding.UTF8, "application/json"),
|
||||
});
|
||||
Assert.Equal(401, (int)response.StatusCode);
|
||||
Assert.Equal(403, (int)response.StatusCode);
|
||||
|
||||
Logs.Tester.LogInformation("No store without anyone can create invoice = 404 because the bitpay API can't know the storeid");
|
||||
response = await tester.PayTester.HttpClient.SendAsync(new HttpRequestMessage(HttpMethod.Post, $"invoices")
|
||||
|
@ -1268,12 +1271,12 @@ namespace BTCPayServer.Tests
|
|||
|
||||
user.ModifyStore(s => s.AnyoneCanCreateInvoice = true);
|
||||
|
||||
Logs.Tester.LogInformation("Bad store with anyone can create invoice = 401");
|
||||
Logs.Tester.LogInformation("Bad store with anyone can create invoice = 403");
|
||||
response = await tester.PayTester.HttpClient.SendAsync(new HttpRequestMessage(HttpMethod.Post, $"invoices?storeId=badid")
|
||||
{
|
||||
Content = new StringContent("{\"Price\": 5000, \"currency\": \"USD\"}", Encoding.UTF8, "application/json"),
|
||||
});
|
||||
Assert.Equal(401, (int)response.StatusCode);
|
||||
Assert.Equal(403, (int)response.StatusCode);
|
||||
|
||||
Logs.Tester.LogInformation("Good store with anyone can create invoice = 200");
|
||||
response = await tester.PayTester.HttpClient.SendAsync(new HttpRequestMessage(HttpMethod.Post, $"invoices?storeId={user.StoreId}")
|
||||
|
@ -1307,7 +1310,7 @@ namespace BTCPayServer.Tests
|
|||
Assert.Equal(Money.Coins(1.0m), invoice1.BtcPrice);
|
||||
|
||||
var storeController = user.GetController<StoresController>();
|
||||
var vm = (RatesViewModel)((ViewResult)storeController.Rates(user.StoreId)).Model;
|
||||
var vm = (RatesViewModel)((ViewResult)storeController.Rates()).Model;
|
||||
Assert.Equal(0.0, vm.Spread);
|
||||
vm.Spread = 40;
|
||||
storeController.Rates(vm).Wait();
|
||||
|
@ -1406,7 +1409,7 @@ namespace BTCPayServer.Tests
|
|||
user.RegisterDerivationScheme("BTC");
|
||||
|
||||
var store = user.GetController<StoresController>();
|
||||
var rateVm = Assert.IsType<RatesViewModel>(Assert.IsType<ViewResult>(store.Rates(user.StoreId)).Model);
|
||||
var rateVm = Assert.IsType<RatesViewModel>(Assert.IsType<ViewResult>(store.Rates()).Model);
|
||||
Assert.False(rateVm.ShowScripting);
|
||||
Assert.Equal("coinaverage", rateVm.PreferredExchange);
|
||||
Assert.Equal(0.0, rateVm.Spread);
|
||||
|
@ -1414,7 +1417,7 @@ namespace BTCPayServer.Tests
|
|||
|
||||
rateVm.PreferredExchange = "bitflyer";
|
||||
Assert.IsType<RedirectToActionResult>(store.Rates(rateVm, "Save").Result);
|
||||
rateVm = Assert.IsType<RatesViewModel>(Assert.IsType<ViewResult>(store.Rates(user.StoreId)).Model);
|
||||
rateVm = Assert.IsType<RatesViewModel>(Assert.IsType<ViewResult>(store.Rates()).Model);
|
||||
Assert.Equal("bitflyer", rateVm.PreferredExchange);
|
||||
|
||||
rateVm.ScriptTest = "BTC_JPY,BTC_CAD";
|
||||
|
@ -1431,7 +1434,7 @@ namespace BTCPayServer.Tests
|
|||
Assert.IsType<RedirectToActionResult>(store.ShowRateRulesPost(true).Result);
|
||||
Assert.IsType<RedirectToActionResult>(store.Rates(rateVm, "Save").Result);
|
||||
store = user.GetController<StoresController>();
|
||||
rateVm = Assert.IsType<RatesViewModel>(Assert.IsType<ViewResult>(store.Rates(user.StoreId)).Model);
|
||||
rateVm = Assert.IsType<RatesViewModel>(Assert.IsType<ViewResult>(store.Rates()).Model);
|
||||
Assert.Equal(rateVm.StoreId, user.StoreId);
|
||||
Assert.Equal(rateVm.DefaultScript, rateVm.Script);
|
||||
Assert.True(rateVm.ShowScripting);
|
||||
|
@ -1449,7 +1452,7 @@ namespace BTCPayServer.Tests
|
|||
Assert.True(rateVm.TestRateRules.All(t => !t.Error));
|
||||
Assert.IsType<RedirectToActionResult>(store.Rates(rateVm, "Save").Result);
|
||||
store = user.GetController<StoresController>();
|
||||
rateVm = Assert.IsType<RatesViewModel>(Assert.IsType<ViewResult>(store.Rates(user.StoreId)).Model);
|
||||
rateVm = Assert.IsType<RatesViewModel>(Assert.IsType<ViewResult>(store.Rates()).Model);
|
||||
Assert.Equal(50, rateVm.Spread);
|
||||
Assert.True(rateVm.ShowScripting);
|
||||
Assert.Contains("DOGE_X", rateVm.Script, StringComparison.OrdinalIgnoreCase);
|
||||
|
|
|
@ -33,7 +33,6 @@ namespace BTCPayServer.Authentication
|
|||
}
|
||||
|
||||
public const string CanViewStores = nameof(CanViewStores);
|
||||
public const string CanEditStore = nameof(CanEditStore);
|
||||
public const string CanManageStores = nameof(CanManageStores);
|
||||
public const string CanViewInvoices = nameof(CanViewInvoices);
|
||||
public const string CanCreateInvoices = nameof(CanCreateInvoices);
|
||||
|
@ -42,66 +41,5 @@ namespace BTCPayServer.Authentication
|
|||
public const string CanViewApps = nameof(CanViewApps);
|
||||
public const string CanManageWallet = nameof(CanManageWallet);
|
||||
public const string CanViewProfile = nameof(CanViewProfile);
|
||||
|
||||
public static AuthorizationOptions AddBTCPayRESTApiPolicies(this AuthorizationOptions options)
|
||||
{
|
||||
AddScopePolicy(options, CanViewStores,
|
||||
context => context.HasScopes(BTCPayScopes.StoreManagement) ||
|
||||
context.HasScopes(BTCPayScopes.ViewStores));
|
||||
options.AddPolicy(CanEditStore, p => p.RequireClaim(CanEditStore));
|
||||
AddScopePolicy(options, CanManageStores,
|
||||
context => context.HasScopes(BTCPayScopes.StoreManagement));
|
||||
AddScopePolicy(options, CanViewInvoices,
|
||||
context => context.HasScopes(BTCPayScopes.ViewInvoices) ||
|
||||
context.HasScopes(BTCPayScopes.InvoiceManagement));
|
||||
AddScopePolicy(options, CanCreateInvoices,
|
||||
context => context.HasScopes(BTCPayScopes.CreateInvoices) ||
|
||||
context.HasScopes(BTCPayScopes.InvoiceManagement));
|
||||
AddScopePolicy(options, CanViewApps,
|
||||
context => context.HasScopes(BTCPayScopes.AppManagement) || context.HasScopes(BTCPayScopes.ViewApps));
|
||||
AddScopePolicy(options, CanManageInvoices,
|
||||
context => context.HasScopes(BTCPayScopes.InvoiceManagement));
|
||||
AddScopePolicy(options, CanManageApps,
|
||||
context => context.HasScopes(BTCPayScopes.AppManagement));
|
||||
AddScopePolicy(options, CanManageWallet,
|
||||
context => context.HasScopes(BTCPayScopes.WalletManagement));
|
||||
AddScopePolicy(options, CanViewProfile,
|
||||
context => context.HasScopes(OpenIddictConstants.Scopes.Profile));
|
||||
return options;
|
||||
}
|
||||
|
||||
private static void AddScopePolicy(AuthorizationOptions options, string name,
|
||||
Func<AuthorizationHandlerContext, bool> scopeGroups)
|
||||
{
|
||||
options.AddPolicy(name,
|
||||
builder => builder.AddRequirements(new LambdaRequirement(scopeGroups)));
|
||||
}
|
||||
|
||||
public static bool HasScopes(this AuthorizationHandlerContext context, params string[] scopes)
|
||||
{
|
||||
return scopes.All(s => context.User.HasClaim(OpenIddictConstants.Claims.Scope, s));
|
||||
}
|
||||
}
|
||||
|
||||
public class LambdaRequirement :
|
||||
AuthorizationHandler<LambdaRequirement>, IAuthorizationRequirement
|
||||
{
|
||||
private readonly Func<AuthorizationHandlerContext, bool> _Func;
|
||||
|
||||
public LambdaRequirement(Func<AuthorizationHandlerContext, bool> func)
|
||||
{
|
||||
_Func = func;
|
||||
}
|
||||
|
||||
protected override Task HandleRequirementAsync(
|
||||
AuthorizationHandlerContext context, LambdaRequirement requirement)
|
||||
{
|
||||
if (_Func.Invoke(context))
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using BTCPayServer.Authentication;
|
||||
using BTCPayServer.Filters;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Security;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -13,7 +14,7 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Authorize(AuthenticationSchemes = Security.Policies.BitpayAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = Security.AuthenticationSchemes.Bitpay)]
|
||||
[BitpayAPIConstraint()]
|
||||
public class AccessTokenController : Controller
|
||||
{
|
||||
|
|
|
@ -26,7 +26,7 @@ using BTCPayServer.Data;
|
|||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Route("[controller]/[action]")]
|
||||
public class AccountController : Controller
|
||||
{
|
||||
|
|
|
@ -19,7 +19,7 @@ using NBitcoin.DataEncoders;
|
|||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[AutoValidateAntiforgeryToken]
|
||||
[Route("apps")]
|
||||
public partial class AppsController : Controller
|
||||
|
|
|
@ -165,7 +165,6 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
}
|
||||
var store = await _AppService.GetStore(app);
|
||||
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
|
||||
var invoice = await _InvoiceController.CreateInvoiceCore(new CreateInvoiceRequest()
|
||||
{
|
||||
ItemCode = choice?.Id,
|
||||
|
@ -283,7 +282,6 @@ namespace BTCPayServer.Controllers
|
|||
return NotFound("Contribution Amount is more than is currently allowed.");
|
||||
}
|
||||
|
||||
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
|
||||
try
|
||||
{
|
||||
var invoice = await _InvoiceController.CreateInvoiceCore(new CreateInvoiceRequest()
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace BTCPayServer.Controllers
|
|||
_IdentityOptions = identityOptions;
|
||||
}
|
||||
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[HttpGet("/connect/authorize")]
|
||||
public async Task<IActionResult> Authorize(OpenIddictRequest openIdConnectRequest)
|
||||
{
|
||||
|
@ -89,7 +89,7 @@ namespace BTCPayServer.Controllers
|
|||
});
|
||||
}
|
||||
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[HttpPost("/connect/authorize")]
|
||||
public async Task<IActionResult> Authorize(OpenIddictRequest openIdConnectRequest,
|
||||
string consent, bool createAuthorization = true)
|
||||
|
|
|
@ -12,7 +12,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[BitpayAPIConstraint]
|
||||
[Authorize(Policies.CanCreateInvoice.Key, AuthenticationSchemes = Policies.BitpayAuthentication)]
|
||||
[Authorize(Policies.CanCreateInvoice.Key, AuthenticationSchemes = AuthenticationSchemes.Bitpay)]
|
||||
public class InvoiceControllerAPI : Controller
|
||||
{
|
||||
private InvoiceController _InvoiceController;
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
[HttpGet]
|
||||
[Route("invoices/{invoiceId}")]
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
public async Task<IActionResult> Invoice(string invoiceId)
|
||||
{
|
||||
var invoice = (await _InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||
|
@ -394,7 +394,7 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
[HttpGet]
|
||||
[Route("invoices")]
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[BitpayAPIConstraint(false)]
|
||||
public async Task<IActionResult> ListInvoices(string searchTerm = null, int skip = 0, int count = 50, int timezoneOffset = 0)
|
||||
{
|
||||
|
@ -454,7 +454,7 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
|
||||
[HttpGet]
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[BitpayAPIConstraint(false)]
|
||||
public async Task<IActionResult> Export(string format, string searchTerm = null, int timezoneOffset = 0)
|
||||
{
|
||||
|
@ -488,7 +488,7 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
[HttpGet]
|
||||
[Route("invoices/create")]
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[BitpayAPIConstraint(false)]
|
||||
public async Task<IActionResult> CreateInvoice()
|
||||
{
|
||||
|
@ -504,31 +504,19 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
[HttpPost]
|
||||
[Route("invoices/create")]
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(Policy = Policies.CanCreateInvoice.Key, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[BitpayAPIConstraint(false)]
|
||||
public async Task<IActionResult> CreateInvoice(CreateInvoiceModel model, CancellationToken cancellationToken)
|
||||
{
|
||||
var stores = await _StoreRepository.GetStoresByUserId(GetUserId());
|
||||
model.Stores = new SelectList(stores, nameof(StoreData.Id), nameof(StoreData.StoreName), model.StoreId);
|
||||
|
||||
model.AvailablePaymentMethods = GetPaymentMethodsSelectList();
|
||||
|
||||
var store = stores.FirstOrDefault(s => s.Id == model.StoreId);
|
||||
if (store == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.StoreId), "Store not found");
|
||||
}
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
StatusMessage = null;
|
||||
if (!store.HasClaim(Policies.CanCreateInvoice.Key))
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.StoreId), "You need to be owner of this store to create an invoice");
|
||||
return View(model);
|
||||
}
|
||||
|
||||
if (store.GetSupportedPaymentMethods(_NetworkProvider).Count() == 0)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.StoreId), "You need to configure the derivation scheme in order to create an invoice");
|
||||
|
@ -576,7 +564,7 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
[HttpPost]
|
||||
[Route("invoices/{invoiceId}/changestate/{newState}")]
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[BitpayAPIConstraint(false)]
|
||||
public async Task<IActionResult> ChangeInvoiceState(string invoiceId, string newState)
|
||||
{
|
||||
|
@ -585,15 +573,12 @@ namespace BTCPayServer.Controllers
|
|||
InvoiceId = new[] {invoiceId},
|
||||
UserId = GetUserId()
|
||||
})).FirstOrDefault();
|
||||
|
||||
var model = new InvoiceStateChangeModel();
|
||||
if (invoice == null)
|
||||
{
|
||||
model.NotFound = true;
|
||||
return NotFound(model);
|
||||
}
|
||||
|
||||
|
||||
if (newState == "invalid")
|
||||
{
|
||||
await _InvoiceRepository.UpdatePaidInvoiceToInvalid(invoiceId);
|
||||
|
|
|
@ -66,8 +66,6 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
internal async Task<DataWrapper<InvoiceResponse>> CreateInvoiceCore(CreateInvoiceRequest invoice, StoreData store, string serverUrl, List<string> additionalTags = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!store.HasClaim(Policies.CanCreateInvoice.Key))
|
||||
throw new UnauthorizedAccessException();
|
||||
invoice.Currency = invoice.Currency?.ToUpperInvariant() ?? "USD";
|
||||
InvoiceLogs logs = new InvoiceLogs();
|
||||
logs.Write("Creation of invoice starting");
|
||||
|
|
|
@ -26,7 +26,7 @@ using IWebHostEnvironment = Microsoft.AspNetCore.Hosting.IHostingEnvironment;
|
|||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Route("[controller]/[action]")]
|
||||
public partial class ManageController : Controller
|
||||
{
|
||||
|
|
|
@ -31,7 +31,7 @@ using NBitpayClient;
|
|||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Route("payment-requests")]
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
public class PaymentRequestController : Controller
|
||||
{
|
||||
private readonly InvoiceController _InvoiceController;
|
||||
|
@ -289,7 +289,6 @@ namespace BTCPayServer.Controllers
|
|||
var pr = await _PaymentRequestRepository.FindPaymentRequest(id, null);
|
||||
var blob = pr.GetBlob();
|
||||
var store = pr.StoreData;
|
||||
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
|
||||
try
|
||||
{
|
||||
var redirectUrl = Request.GetDisplayUrl().TrimEnd("/pay", StringComparison.InvariantCulture)
|
||||
|
|
|
@ -15,14 +15,21 @@ using BTCPayServer.Authentication;
|
|||
using Microsoft.AspNetCore.Cors;
|
||||
using System.Threading;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Security;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Authorize(AuthenticationSchemes = Security.Policies.BitpayAuthentication)]
|
||||
[AllowAnonymous]
|
||||
[EnableCors(CorsPolicies.All)]
|
||||
[Authorize(Policy = Policies.CanGetRates.Key, AuthenticationSchemes = Security.AuthenticationSchemes.Bitpay)]
|
||||
public class RateController : Controller
|
||||
{
|
||||
public StoreData CurrentStore
|
||||
{
|
||||
get
|
||||
{
|
||||
return HttpContext.GetStoreData();
|
||||
}
|
||||
}
|
||||
RateFetcher _RateProviderFactory;
|
||||
BTCPayNetworkProvider _NetworkProvider;
|
||||
CurrencyNameTable _CurrencyNameTable;
|
||||
|
@ -47,40 +54,28 @@ namespace BTCPayServer.Controllers
|
|||
[Route("rates/{baseCurrency}")]
|
||||
[HttpGet]
|
||||
[BitpayAPIConstraint]
|
||||
public async Task<IActionResult> GetBaseCurrencyRates(string baseCurrency, string storeId, CancellationToken cancellationToken)
|
||||
public async Task<IActionResult> GetBaseCurrencyRates(string baseCurrency, CancellationToken cancellationToken)
|
||||
{
|
||||
storeId = await GetStoreId(storeId);
|
||||
var store = this.HttpContext.GetStoreData();
|
||||
if (store == null || store.Id != storeId)
|
||||
store = await _StoreRepo.FindStore(storeId);
|
||||
if (store == null)
|
||||
{
|
||||
var err = Json(new BitpayErrorsModel() { Error = "Store not found" });
|
||||
err.StatusCode = 404;
|
||||
return err;
|
||||
}
|
||||
var supportedMethods = store.GetSupportedPaymentMethods(_NetworkProvider);
|
||||
var supportedMethods = CurrentStore.GetSupportedPaymentMethods(_NetworkProvider);
|
||||
|
||||
var currencyCodes = supportedMethods.Where(method => !string.IsNullOrEmpty(method.PaymentId.CryptoCode))
|
||||
.Select(method => method.PaymentId.CryptoCode).Distinct();
|
||||
|
||||
var currencypairs = BuildCurrencyPairs(currencyCodes, baseCurrency);
|
||||
|
||||
var result = await GetRates2(currencypairs, store.Id, cancellationToken);
|
||||
|
||||
var result = await GetRates2(currencypairs, null, cancellationToken);
|
||||
var rates = (result as JsonResult)?.Value as Rate[];
|
||||
if (rates == null)
|
||||
return result;
|
||||
return Json(new DataWrapper<Rate[]>(rates));
|
||||
}
|
||||
|
||||
|
||||
[Route("rates/{baseCurrency}/{currency}")]
|
||||
[HttpGet]
|
||||
[BitpayAPIConstraint]
|
||||
public async Task<IActionResult> GetCurrencyPairRate(string baseCurrency, string currency, string storeId, CancellationToken cancellationToken)
|
||||
public async Task<IActionResult> GetCurrencyPairRate(string baseCurrency, string currency, CancellationToken cancellationToken)
|
||||
{
|
||||
storeId = await GetStoreId(storeId);
|
||||
var result = await GetRates2($"{baseCurrency}_{currency}", storeId, cancellationToken);
|
||||
var result = await GetRates2($"{baseCurrency}_{currency}", null, cancellationToken);
|
||||
var rates = (result as JsonResult)?.Value as Rate[];
|
||||
if (rates == null)
|
||||
return result;
|
||||
|
@ -90,54 +85,27 @@ namespace BTCPayServer.Controllers
|
|||
[Route("rates")]
|
||||
[HttpGet]
|
||||
[BitpayAPIConstraint]
|
||||
public async Task<IActionResult> GetRates(string currencyPairs, string storeId, CancellationToken cancellationToken)
|
||||
public async Task<IActionResult> GetRates(string currencyPairs, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await GetRates2(currencyPairs, storeId, cancellationToken);
|
||||
var result = await GetRates2(currencyPairs, null, cancellationToken);
|
||||
var rates = (result as JsonResult)?.Value as Rate[];
|
||||
if (rates == null)
|
||||
return result;
|
||||
return Json(new DataWrapper<Rate[]>(rates));
|
||||
}
|
||||
|
||||
private async Task<string> GetStoreId(string storeId)
|
||||
{
|
||||
if (storeId != null && this.HttpContext.GetStoreData()?.Id == storeId)
|
||||
return storeId;
|
||||
if(storeId == null)
|
||||
{
|
||||
var tokens = await this.TokenRepository.GetTokens(this.User.GetSIN());
|
||||
storeId = tokens.Select(s => s.StoreId).Where(s => s != null).FirstOrDefault();
|
||||
}
|
||||
if (storeId == null)
|
||||
return null;
|
||||
var store = await _StoreRepo.FindStore(storeId);
|
||||
if (store == null)
|
||||
return null;
|
||||
this.HttpContext.SetStoreData(store);
|
||||
return storeId;
|
||||
}
|
||||
|
||||
[Route("api/rates")]
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> GetRates2(string currencyPairs, string storeId, CancellationToken cancellationToken)
|
||||
{
|
||||
storeId = await GetStoreId(storeId);
|
||||
if (storeId == null)
|
||||
{
|
||||
var result = Json(new BitpayErrorsModel() { Error = "You need to specify storeId (in your store settings)" });
|
||||
result.StatusCode = 400;
|
||||
return result;
|
||||
}
|
||||
var store = this.HttpContext.GetStoreData();
|
||||
if (store == null || store.Id != storeId)
|
||||
store = await _StoreRepo.FindStore(storeId);
|
||||
var store = this.CurrentStore ?? await this._StoreRepo.FindStore(storeId);
|
||||
if (store == null)
|
||||
{
|
||||
var result = Json(new BitpayErrorsModel() { Error = "Store not found" });
|
||||
result.StatusCode = 404;
|
||||
return result;
|
||||
var err = Json(new BitpayErrorsModel() { Error = "Store not found" });
|
||||
err.StatusCode = 404;
|
||||
return err;
|
||||
}
|
||||
|
||||
if (currencyPairs == null)
|
||||
{
|
||||
currencyPairs = store.GetStoreBlob().GetDefaultCurrencyPairString();
|
||||
|
@ -186,7 +154,7 @@ namespace BTCPayServer.Controllers
|
|||
bool first = true;
|
||||
foreach (var currencyCode in currencyCodes)
|
||||
{
|
||||
if(!first)
|
||||
if (!first)
|
||||
currencyPairsBuilder.Append(",");
|
||||
first = false;
|
||||
currencyPairsBuilder.Append($"{baseCrypto}_{currencyCode}");
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace BTCPayServer.Controllers.RestApi
|
|||
/// </summary>
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
[Authorize(AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.OpenId)]
|
||||
public class TestController : ControllerBase
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
@ -44,9 +44,10 @@ namespace BTCPayServer.Controllers.RestApi
|
|||
}
|
||||
|
||||
[HttpGet("me/is-admin")]
|
||||
[Authorize(Policy = Policies.CanModifyServerSettings.Key, AuthenticationSchemes = AuthenticationSchemes.OpenId)]
|
||||
public bool AmIAnAdmin()
|
||||
{
|
||||
return User.IsInRole(Roles.ServerAdmin);
|
||||
return true;
|
||||
}
|
||||
|
||||
[HttpGet("me/stores")]
|
||||
|
@ -57,8 +58,8 @@ namespace BTCPayServer.Controllers.RestApi
|
|||
|
||||
|
||||
[HttpGet("me/stores/{storeId}/can-edit")]
|
||||
[Authorize(Policy = RestAPIPolicies.CanEditStore,
|
||||
AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings.Key,
|
||||
AuthenticationSchemes = AuthenticationSchemes.OpenId)]
|
||||
public bool CanEdit(string storeId)
|
||||
{
|
||||
return true;
|
||||
|
@ -68,48 +69,48 @@ namespace BTCPayServer.Controllers.RestApi
|
|||
#region scopes tests
|
||||
|
||||
[Authorize(Policy = RestAPIPolicies.CanViewStores,
|
||||
AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
|
||||
AuthenticationSchemes = AuthenticationSchemes.OpenId)]
|
||||
[HttpGet(nameof(ScopeCanViewStores))]
|
||||
public bool ScopeCanViewStores() { return true; }
|
||||
|
||||
[Authorize(Policy = RestAPIPolicies.CanManageStores,
|
||||
AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
|
||||
AuthenticationSchemes = AuthenticationSchemes.OpenId)]
|
||||
[HttpGet(nameof(ScopeCanManageStores))]
|
||||
public bool ScopeCanManageStores() { return true; }
|
||||
|
||||
[Authorize(Policy = RestAPIPolicies.CanViewInvoices,
|
||||
AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
|
||||
AuthenticationSchemes = AuthenticationSchemes.OpenId)]
|
||||
[HttpGet(nameof(ScopeCanViewInvoices))]
|
||||
public bool ScopeCanViewInvoices() { return true; }
|
||||
|
||||
[Authorize(Policy = RestAPIPolicies.CanCreateInvoices,
|
||||
AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
|
||||
AuthenticationSchemes = AuthenticationSchemes.OpenId)]
|
||||
[HttpGet(nameof(ScopeCanCreateInvoices))]
|
||||
public bool ScopeCanCreateInvoices() { return true; }
|
||||
|
||||
[Authorize(Policy = RestAPIPolicies.CanManageInvoices,
|
||||
AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
|
||||
AuthenticationSchemes = AuthenticationSchemes.OpenId)]
|
||||
[HttpGet(nameof(ScopeCanManageInvoices))]
|
||||
public bool ScopeCanManageInvoices() { return true; }
|
||||
|
||||
[Authorize(Policy = RestAPIPolicies.CanManageApps,
|
||||
AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
|
||||
AuthenticationSchemes = AuthenticationSchemes.OpenId)]
|
||||
[HttpGet(nameof(ScopeCanManageApps))]
|
||||
public bool ScopeCanManageApps() { return true; }
|
||||
|
||||
[Authorize(Policy = RestAPIPolicies.CanViewApps,
|
||||
AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
|
||||
AuthenticationSchemes = AuthenticationSchemes.OpenId)]
|
||||
|
||||
[HttpGet(nameof(ScopeCanViewApps))]
|
||||
public bool ScopeCanViewApps() { return true; }
|
||||
|
||||
[Authorize(Policy = RestAPIPolicies.CanManageWallet,
|
||||
AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
|
||||
AuthenticationSchemes = AuthenticationSchemes.OpenId)]
|
||||
[HttpGet(nameof(ScopeCanManageWallet))]
|
||||
public bool ScopeCanManageWallet() { return true; }
|
||||
|
||||
[Authorize(Policy = RestAPIPolicies.CanViewProfile,
|
||||
AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
|
||||
AuthenticationSchemes = AuthenticationSchemes.OpenId)]
|
||||
|
||||
[HttpGet(nameof(ScopeCanViewProfile))]
|
||||
public bool ScopeCanViewProfile() { return true; }
|
||||
|
|
|
@ -38,7 +38,8 @@ using Microsoft.EntityFrameworkCore;
|
|||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Authorize(Policy = BTCPayServer.Security.Policies.CanModifyServerSettings.Key)]
|
||||
[Authorize(Policy = BTCPayServer.Security.Policies.CanModifyServerSettings.Key,
|
||||
AuthenticationSchemes = BTCPayServer.Security.AuthenticationSchemes.Cookie)]
|
||||
public partial class ServerController : Controller
|
||||
{
|
||||
private UserManager<ApplicationUser> _UserManager;
|
||||
|
|
|
@ -39,8 +39,8 @@ using IWebHostEnvironment = Microsoft.AspNetCore.Hosting.IHostingEnvironment;
|
|||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Route("stores")]
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings.Key, AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings.Key, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[AutoValidateAntiforgeryToken]
|
||||
public partial class StoresController : Controller
|
||||
{
|
||||
|
@ -124,8 +124,8 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
private async Task FillUsers(StoreUsersViewModel vm)
|
||||
{
|
||||
var users = await _Repo.GetStoreUsers(StoreData.Id);
|
||||
vm.StoreId = StoreData.Id;
|
||||
var users = await _Repo.GetStoreUsers(CurrentStore.Id);
|
||||
vm.StoreId = CurrentStore.Id;
|
||||
vm.Users = users.Select(u => new StoreUsersViewModel.StoreUserViewModel()
|
||||
{
|
||||
Email = u.Email,
|
||||
|
@ -134,7 +134,7 @@ namespace BTCPayServer.Controllers
|
|||
}).ToList();
|
||||
}
|
||||
|
||||
public StoreData StoreData
|
||||
public StoreData CurrentStore
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -163,7 +163,7 @@ namespace BTCPayServer.Controllers
|
|||
ModelState.AddModelError(nameof(vm.Role), "Invalid role");
|
||||
return View(vm);
|
||||
}
|
||||
if (!await _Repo.AddStoreUser(StoreData.Id, user.Id, vm.Role))
|
||||
if (!await _Repo.AddStoreUser(CurrentStore.Id, user.Id, vm.Role))
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.Email), "The user already has access to this store");
|
||||
return View(vm);
|
||||
|
@ -199,13 +199,13 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
[HttpGet]
|
||||
[Route("{storeId}/rates")]
|
||||
public IActionResult Rates(string storeId)
|
||||
public IActionResult Rates()
|
||||
{
|
||||
var storeBlob = StoreData.GetStoreBlob();
|
||||
var storeBlob = CurrentStore.GetStoreBlob();
|
||||
var vm = new RatesViewModel();
|
||||
vm.SetExchangeRates(GetSupportedExchanges(), storeBlob.PreferredExchange ?? CoinAverageRateProvider.CoinAverageName);
|
||||
vm.Spread = (double)(storeBlob.Spread * 100m);
|
||||
vm.StoreId = storeId;
|
||||
vm.StoreId = CurrentStore.Id;
|
||||
vm.Script = storeBlob.GetRateRules(_NetworkProvider).ToString();
|
||||
vm.DefaultScript = storeBlob.GetDefaultRateRules(_NetworkProvider).ToString();
|
||||
vm.AvailableExchanges = GetSupportedExchanges();
|
||||
|
@ -239,7 +239,7 @@ namespace BTCPayServer.Controllers
|
|||
if (model.PreferredExchange != null)
|
||||
model.PreferredExchange = model.PreferredExchange.Trim().ToLowerInvariant();
|
||||
|
||||
var blob = StoreData.GetStoreBlob();
|
||||
var blob = CurrentStore.GetStoreBlob();
|
||||
model.DefaultScript = blob.GetDefaultRateRules(_NetworkProvider).ToString();
|
||||
model.AvailableExchanges = GetSupportedExchanges();
|
||||
|
||||
|
@ -311,14 +311,14 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
else // command == Save
|
||||
{
|
||||
if (StoreData.SetStoreBlob(blob))
|
||||
if (CurrentStore.SetStoreBlob(blob))
|
||||
{
|
||||
await _Repo.UpdateStore(StoreData);
|
||||
await _Repo.UpdateStore(CurrentStore);
|
||||
StatusMessage = "Rate settings updated";
|
||||
}
|
||||
return RedirectToAction(nameof(Rates), new
|
||||
{
|
||||
storeId = StoreData.Id
|
||||
storeId = CurrentStore.Id
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -342,22 +342,22 @@ namespace BTCPayServer.Controllers
|
|||
[Route("{storeId}/rates/confirm")]
|
||||
public async Task<IActionResult> ShowRateRulesPost(bool scripting)
|
||||
{
|
||||
var blob = StoreData.GetStoreBlob();
|
||||
var blob = CurrentStore.GetStoreBlob();
|
||||
blob.RateScripting = scripting;
|
||||
blob.RateScript = blob.GetDefaultRateRules(_NetworkProvider).ToString();
|
||||
StoreData.SetStoreBlob(blob);
|
||||
await _Repo.UpdateStore(StoreData);
|
||||
CurrentStore.SetStoreBlob(blob);
|
||||
await _Repo.UpdateStore(CurrentStore);
|
||||
StatusMessage = "Rate rules scripting activated";
|
||||
return RedirectToAction(nameof(Rates), new { storeId = StoreData.Id });
|
||||
return RedirectToAction(nameof(Rates), new { storeId = CurrentStore.Id });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{storeId}/checkout")]
|
||||
public IActionResult CheckoutExperience()
|
||||
{
|
||||
var storeBlob = StoreData.GetStoreBlob();
|
||||
var storeBlob = CurrentStore.GetStoreBlob();
|
||||
var vm = new CheckoutExperienceViewModel();
|
||||
SetCryptoCurrencies(vm, StoreData);
|
||||
SetCryptoCurrencies(vm, CurrentStore);
|
||||
vm.CustomCSS = storeBlob.CustomCSS?.AbsoluteUri;
|
||||
vm.CustomLogo = storeBlob.CustomLogo?.AbsoluteUri;
|
||||
vm.HtmlTitle = storeBlob.HtmlTitle;
|
||||
|
@ -402,14 +402,14 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
}
|
||||
bool needUpdate = false;
|
||||
var blob = StoreData.GetStoreBlob();
|
||||
var blob = CurrentStore.GetStoreBlob();
|
||||
var defaultPaymentMethodId = model.DefaultPaymentMethod == null ? null : PaymentMethodId.Parse(model.DefaultPaymentMethod);
|
||||
if (StoreData.GetDefaultPaymentId(_NetworkProvider) != defaultPaymentMethodId)
|
||||
if (CurrentStore.GetDefaultPaymentId(_NetworkProvider) != defaultPaymentMethodId)
|
||||
{
|
||||
needUpdate = true;
|
||||
StoreData.SetDefaultPaymentId(defaultPaymentMethodId);
|
||||
CurrentStore.SetDefaultPaymentId(defaultPaymentMethodId);
|
||||
}
|
||||
SetCryptoCurrencies(model, StoreData);
|
||||
SetCryptoCurrencies(model, CurrentStore);
|
||||
model.SetLanguages(_LangService, model.DefaultLang);
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
|
@ -425,19 +425,19 @@ namespace BTCPayServer.Controllers
|
|||
blob.LightningMaxValue = lightningMaxValue;
|
||||
blob.LightningAmountInSatoshi = model.LightningAmountInSatoshi;
|
||||
blob.RedirectAutomatically = model.RedirectAutomatically;
|
||||
if (StoreData.SetStoreBlob(blob))
|
||||
if (CurrentStore.SetStoreBlob(blob))
|
||||
{
|
||||
needUpdate = true;
|
||||
}
|
||||
if (needUpdate)
|
||||
{
|
||||
await _Repo.UpdateStore(StoreData);
|
||||
await _Repo.UpdateStore(CurrentStore);
|
||||
StatusMessage = "Store successfully updated";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(CheckoutExperience), new
|
||||
{
|
||||
storeId = StoreData.Id
|
||||
storeId = CurrentStore.Id
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -529,23 +529,23 @@ namespace BTCPayServer.Controllers
|
|||
public async Task<IActionResult> UpdateStore(StoreViewModel model, string command = null)
|
||||
{
|
||||
bool needUpdate = false;
|
||||
if (StoreData.SpeedPolicy != model.SpeedPolicy)
|
||||
if (CurrentStore.SpeedPolicy != model.SpeedPolicy)
|
||||
{
|
||||
needUpdate = true;
|
||||
StoreData.SpeedPolicy = model.SpeedPolicy;
|
||||
CurrentStore.SpeedPolicy = model.SpeedPolicy;
|
||||
}
|
||||
if (StoreData.StoreName != model.StoreName)
|
||||
if (CurrentStore.StoreName != model.StoreName)
|
||||
{
|
||||
needUpdate = true;
|
||||
StoreData.StoreName = model.StoreName;
|
||||
CurrentStore.StoreName = model.StoreName;
|
||||
}
|
||||
if (StoreData.StoreWebsite != model.StoreWebsite)
|
||||
if (CurrentStore.StoreWebsite != model.StoreWebsite)
|
||||
{
|
||||
needUpdate = true;
|
||||
StoreData.StoreWebsite = model.StoreWebsite;
|
||||
CurrentStore.StoreWebsite = model.StoreWebsite;
|
||||
}
|
||||
|
||||
var blob = StoreData.GetStoreBlob();
|
||||
var blob = CurrentStore.GetStoreBlob();
|
||||
blob.AnyoneCanInvoice = model.AnyoneCanCreateInvoice;
|
||||
blob.NetworkFeeMode = model.NetworkFeeMode;
|
||||
blob.MonitoringExpiration = model.MonitoringExpiration;
|
||||
|
@ -553,20 +553,20 @@ namespace BTCPayServer.Controllers
|
|||
blob.LightningDescriptionTemplate = model.LightningDescriptionTemplate ?? string.Empty;
|
||||
blob.PaymentTolerance = model.PaymentTolerance;
|
||||
|
||||
if (StoreData.SetStoreBlob(blob))
|
||||
if (CurrentStore.SetStoreBlob(blob))
|
||||
{
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
if (needUpdate)
|
||||
{
|
||||
await _Repo.UpdateStore(StoreData);
|
||||
await _Repo.UpdateStore(CurrentStore);
|
||||
StatusMessage = "Store successfully updated";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(UpdateStore), new
|
||||
{
|
||||
storeId = StoreData.Id
|
||||
storeId = CurrentStore.Id
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -588,7 +588,7 @@ namespace BTCPayServer.Controllers
|
|||
[Route("{storeId}/delete")]
|
||||
public async Task<IActionResult> DeleteStorePost(string storeId)
|
||||
{
|
||||
await _Repo.DeleteStore(StoreData.Id);
|
||||
await _Repo.DeleteStore(CurrentStore.Id);
|
||||
StatusMessage = "Success: Store successfully deleted";
|
||||
return RedirectToAction(nameof(UserStoresController.ListStores), "UserStores");
|
||||
}
|
||||
|
@ -614,7 +614,7 @@ namespace BTCPayServer.Controllers
|
|||
public async Task<IActionResult> ListTokens()
|
||||
{
|
||||
var model = new TokensViewModel();
|
||||
var tokens = await _TokenRepository.GetTokensByStoreIdAsync(StoreData.Id);
|
||||
var tokens = await _TokenRepository.GetTokensByStoreIdAsync(CurrentStore.Id);
|
||||
model.StatusMessage = StatusMessage;
|
||||
model.StoreNotConfigured = StoreNotConfigured;
|
||||
model.Tokens = tokens.Select(t => new TokenViewModel()
|
||||
|
@ -624,7 +624,7 @@ namespace BTCPayServer.Controllers
|
|||
Id = t.Value
|
||||
}).ToArray();
|
||||
|
||||
model.ApiKey = (await _TokenRepository.GetLegacyAPIKeys(StoreData.Id)).FirstOrDefault();
|
||||
model.ApiKey = (await _TokenRepository.GetLegacyAPIKeys(CurrentStore.Id)).FirstOrDefault();
|
||||
if (model.ApiKey == null)
|
||||
model.EncodedApiKey = "*API Key*";
|
||||
else
|
||||
|
@ -637,7 +637,7 @@ namespace BTCPayServer.Controllers
|
|||
public async Task<IActionResult> RevokeToken(string tokenId)
|
||||
{
|
||||
var token = await _TokenRepository.GetToken(tokenId);
|
||||
if (token == null || token.StoreId != StoreData.Id)
|
||||
if (token == null || token.StoreId != CurrentStore.Id)
|
||||
return NotFound();
|
||||
return View("Confirm", new ConfirmModel()
|
||||
{
|
||||
|
@ -653,7 +653,7 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
var token = await _TokenRepository.GetToken(tokenId);
|
||||
if (token == null ||
|
||||
token.StoreId != StoreData.Id ||
|
||||
token.StoreId != CurrentStore.Id ||
|
||||
!await _TokenRepository.DeleteToken(tokenId))
|
||||
StatusMessage = "Failure to revoke this token";
|
||||
else
|
||||
|
@ -666,7 +666,7 @@ namespace BTCPayServer.Controllers
|
|||
public async Task<IActionResult> ShowToken(string tokenId)
|
||||
{
|
||||
var token = await _TokenRepository.GetToken(tokenId);
|
||||
if (token == null || token.StoreId != StoreData.Id)
|
||||
if (token == null || token.StoreId != CurrentStore.Id)
|
||||
return NotFound();
|
||||
return View(token);
|
||||
}
|
||||
|
@ -674,7 +674,6 @@ namespace BTCPayServer.Controllers
|
|||
[HttpPost]
|
||||
[Route("/api-tokens")]
|
||||
[Route("{storeId}/Tokens/Create")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> CreateToken(CreateTokenViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
|
@ -684,23 +683,17 @@ namespace BTCPayServer.Controllers
|
|||
model.Label = model.Label ?? String.Empty;
|
||||
var userId = GetUserId();
|
||||
if (userId == null)
|
||||
return Challenge(Policies.CookieAuthentication);
|
||||
return Challenge(AuthenticationSchemes.Cookie);
|
||||
|
||||
var store = StoreData;
|
||||
var storeId = StoreData?.Id;
|
||||
var store = CurrentStore;
|
||||
var storeId = CurrentStore?.Id;
|
||||
if (storeId == null)
|
||||
{
|
||||
storeId = model.StoreId;
|
||||
store = await _Repo.FindStore(storeId, userId);
|
||||
if (store == null)
|
||||
return Challenge(Policies.CookieAuthentication);
|
||||
return Challenge(AuthenticationSchemes.Cookie);
|
||||
}
|
||||
|
||||
if (!store.HasClaim(Policies.CanModifyStoreSettings.Key))
|
||||
{
|
||||
return Challenge(Policies.CookieAuthentication);
|
||||
}
|
||||
|
||||
var tokenRequest = new TokenRequest()
|
||||
{
|
||||
Label = model.Label,
|
||||
|
@ -737,20 +730,12 @@ namespace BTCPayServer.Controllers
|
|||
[HttpGet]
|
||||
[Route("/api-tokens")]
|
||||
[Route("{storeId}/Tokens/Create")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> CreateToken()
|
||||
{
|
||||
var userId = GetUserId();
|
||||
if (string.IsNullOrWhiteSpace(userId))
|
||||
return Challenge(Policies.CookieAuthentication);
|
||||
var storeId = StoreData?.Id;
|
||||
if (StoreData != null)
|
||||
{
|
||||
if (!StoreData.HasClaim(Policies.CanModifyStoreSettings.Key))
|
||||
{
|
||||
return Challenge(Policies.CookieAuthentication);
|
||||
}
|
||||
}
|
||||
return Challenge(AuthenticationSchemes.Cookie);
|
||||
var storeId = CurrentStore?.Id;
|
||||
var model = new CreateTokenViewModel();
|
||||
ViewBag.HidePublicKey = storeId == null;
|
||||
ViewBag.ShowStores = storeId == null;
|
||||
|
@ -759,7 +744,7 @@ namespace BTCPayServer.Controllers
|
|||
if (storeId == null)
|
||||
{
|
||||
var stores = await _Repo.GetStoresByUserId(userId);
|
||||
model.Stores = new SelectList(stores.Where(s => s.HasClaim(Policies.CanModifyStoreSettings.Key)), nameof(StoreData.Id), nameof(StoreData.StoreName), storeId);
|
||||
model.Stores = new SelectList(stores.Where(s => s.Role == StoreRoles.Owner), nameof(CurrentStore.Id), nameof(CurrentStore.StoreName), storeId);
|
||||
if (model.Stores.Count() == 0)
|
||||
{
|
||||
StatusMessage = "Error: You need to be owner of at least one store before pairing";
|
||||
|
@ -776,7 +761,7 @@ namespace BTCPayServer.Controllers
|
|||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
await _TokenRepository.GenerateLegacyAPIKey(StoreData.Id);
|
||||
await _TokenRepository.GenerateLegacyAPIKey(CurrentStore.Id);
|
||||
StatusMessage = "API Key re-generated";
|
||||
return RedirectToAction(nameof(ListTokens));
|
||||
}
|
||||
|
@ -788,7 +773,7 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
var userId = GetUserId();
|
||||
if (userId == null)
|
||||
return Challenge(Policies.CookieAuthentication);
|
||||
return Challenge(AuthenticationSchemes.Cookie);
|
||||
if (pairingCode == null)
|
||||
return NotFound();
|
||||
var pairing = await _TokenRepository.GetPairingAsync(pairingCode);
|
||||
|
@ -805,8 +790,8 @@ namespace BTCPayServer.Controllers
|
|||
Id = pairing.Id,
|
||||
Label = pairing.Label,
|
||||
SIN = pairing.SIN ?? "Server-Initiated Pairing",
|
||||
SelectedStore = selectedStore ?? stores.FirstOrDefault()?.Id,
|
||||
Stores = stores.Where(u => u.HasClaim(Policies.CanModifyStoreSettings.Key)).Select(s => new PairingModel.StoreViewModel()
|
||||
StoreId = selectedStore ?? stores.FirstOrDefault()?.Id,
|
||||
Stores = stores.Where(u => u.Role == StoreRoles.Owner).Select(s => new PairingModel.StoreViewModel()
|
||||
{
|
||||
Id = s.Id,
|
||||
Name = string.IsNullOrEmpty(s.StoreName) ? s.Id : s.StoreName
|
||||
|
@ -817,24 +802,15 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
[HttpPost]
|
||||
[Route("/api-access-request")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> Pair(string pairingCode, string selectedStore)
|
||||
public async Task<IActionResult> Pair(string pairingCode, string storeId)
|
||||
{
|
||||
if (pairingCode == null)
|
||||
return NotFound();
|
||||
var userId = GetUserId();
|
||||
if (userId == null)
|
||||
return Challenge(Policies.CookieAuthentication);
|
||||
var store = await _Repo.FindStore(selectedStore, userId);
|
||||
var store = CurrentStore;
|
||||
var pairing = await _TokenRepository.GetPairingAsync(pairingCode);
|
||||
if (store == null || pairing == null)
|
||||
return NotFound();
|
||||
|
||||
if (!store.HasClaim(Policies.CanModifyStoreSettings.Key))
|
||||
{
|
||||
return Challenge(Policies.CookieAuthentication);
|
||||
}
|
||||
|
||||
var pairingResult = await _TokenRepository.PairWithStoreAsync(pairingCode, store.Id);
|
||||
if (pairingResult == PairingResult.Complete || pairingResult == PairingResult.Partial)
|
||||
{
|
||||
|
@ -863,7 +839,7 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
private string GetUserId()
|
||||
{
|
||||
if (User.Identity.AuthenticationType != Policies.CookieAuthentication)
|
||||
if (User.Identity.AuthenticationType != AuthenticationSchemes.Cookie)
|
||||
return null;
|
||||
return _UserManager.GetUserId(User);
|
||||
}
|
||||
|
@ -877,7 +853,7 @@ namespace BTCPayServer.Controllers
|
|||
[Route("{storeId}/paybutton")]
|
||||
public IActionResult PayButton()
|
||||
{
|
||||
var store = StoreData;
|
||||
var store = CurrentStore;
|
||||
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
if (!storeBlob.AnyoneCanInvoice)
|
||||
|
@ -906,17 +882,17 @@ namespace BTCPayServer.Controllers
|
|||
[Route("{storeId}/paybutton")]
|
||||
public async Task<IActionResult> PayButton(bool enableStore)
|
||||
{
|
||||
var blob = StoreData.GetStoreBlob();
|
||||
var blob = CurrentStore.GetStoreBlob();
|
||||
blob.AnyoneCanInvoice = enableStore;
|
||||
if (StoreData.SetStoreBlob(blob))
|
||||
if (CurrentStore.SetStoreBlob(blob))
|
||||
{
|
||||
await _Repo.UpdateStore(StoreData);
|
||||
await _Repo.UpdateStore(CurrentStore);
|
||||
StatusMessage = "Store successfully updated";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(PayButton), new
|
||||
{
|
||||
storeId = StoreData.Id
|
||||
storeId = CurrentStore.Id
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ using NBXplorer.DerivationStrategy;
|
|||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Route("stores")]
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[AutoValidateAntiforgeryToken]
|
||||
public partial class UserStoresController : Controller
|
||||
{
|
||||
|
@ -91,7 +91,7 @@ namespace BTCPayServer.Controllers
|
|||
Id = store.Id,
|
||||
Name = store.StoreName,
|
||||
WebSite = store.StoreWebsite,
|
||||
IsOwner = store.HasClaim(Policies.CanModifyStoreSettings.Key)
|
||||
IsOwner = store.Role == StoreRoles.Owner
|
||||
});
|
||||
}
|
||||
return View(result);
|
||||
|
|
|
@ -89,7 +89,7 @@ namespace BTCPayServer.Controllers
|
|||
case "ledger":
|
||||
return ViewWalletSendLedger(psbt);
|
||||
case "update":
|
||||
var derivationSchemeSettings = await GetDerivationSchemeSettings(walletId);
|
||||
var derivationSchemeSettings = GetDerivationSchemeSettings(walletId);
|
||||
psbt = await UpdatePSBT(derivationSchemeSettings, psbt, network);
|
||||
if (psbt == null)
|
||||
{
|
||||
|
@ -140,7 +140,7 @@ namespace BTCPayServer.Controllers
|
|||
vm.SigningKey = signingKey;
|
||||
vm.SigningKeyPath = signingKeyPath;
|
||||
|
||||
var derivationSchemeSettings = await GetDerivationSchemeSettings(walletId);
|
||||
var derivationSchemeSettings = GetDerivationSchemeSettings(walletId);
|
||||
if (derivationSchemeSettings == null)
|
||||
return NotFound();
|
||||
try
|
||||
|
@ -263,7 +263,7 @@ namespace BTCPayServer.Controllers
|
|||
try
|
||||
{
|
||||
psbt = PSBT.Parse(vm.PSBT, network.NBitcoinNetwork);
|
||||
var derivationSchemeSettings = await GetDerivationSchemeSettings(walletId);
|
||||
var derivationSchemeSettings = GetDerivationSchemeSettings(walletId);
|
||||
if (derivationSchemeSettings == null)
|
||||
return NotFound();
|
||||
await FetchTransactionDetails(derivationSchemeSettings, vm, network);
|
||||
|
@ -295,7 +295,7 @@ namespace BTCPayServer.Controllers
|
|||
vm.GlobalError = "Error while broadcasting: " + ex.Message;
|
||||
return View(vm);
|
||||
}
|
||||
return await RedirectToWalletTransaction(walletId, transaction);
|
||||
return RedirectToWalletTransaction(walletId, transaction);
|
||||
}
|
||||
else if (command == "analyze-psbt")
|
||||
{
|
||||
|
|
|
@ -34,7 +34,7 @@ using static BTCPayServer.Controllers.StoresController;
|
|||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Route("wallets")]
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings.Key, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[AutoValidateAntiforgeryToken]
|
||||
public partial class WalletsController : Controller
|
||||
{
|
||||
|
@ -46,7 +46,7 @@ namespace BTCPayServer.Controllers
|
|||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly JsonSerializerSettings _serializerSettings;
|
||||
private readonly NBXplorerDashboard _dashboard;
|
||||
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly IFeeProviderFactory _feeRateProvider;
|
||||
private readonly BTCPayWalletProvider _walletProvider;
|
||||
public RateFetcher RateFetcher { get; }
|
||||
|
@ -62,6 +62,7 @@ namespace BTCPayServer.Controllers
|
|||
MvcNewtonsoftJsonOptions mvcJsonOptions,
|
||||
NBXplorerDashboard dashboard,
|
||||
RateFetcher rateProvider,
|
||||
IAuthorizationService authorizationService,
|
||||
ExplorerClientProvider explorerProvider,
|
||||
IFeeProviderFactory feeRateProvider,
|
||||
BTCPayWalletProvider walletProvider)
|
||||
|
@ -70,6 +71,7 @@ namespace BTCPayServer.Controllers
|
|||
Repository = repo;
|
||||
WalletRepository = walletRepository;
|
||||
RateFetcher = rateProvider;
|
||||
_authorizationService = authorizationService;
|
||||
NetworkProvider = networkProvider;
|
||||
_userManager = userManager;
|
||||
_serializerSettings = mvcJsonOptions.SerializerSettings;
|
||||
|
@ -119,7 +121,7 @@ namespace BTCPayServer.Controllers
|
|||
catch { }
|
||||
/////////
|
||||
|
||||
DerivationSchemeSettings paymentMethod = await GetDerivationSchemeSettings(walletId);
|
||||
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId);
|
||||
if (paymentMethod == null)
|
||||
return NotFound();
|
||||
|
||||
|
@ -185,8 +187,14 @@ namespace BTCPayServer.Controllers
|
|||
return RedirectToAction(nameof(WalletTransactions), new { walletId = walletId.ToString() });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> ListWallets()
|
||||
{
|
||||
if (GetUserId() == null)
|
||||
{
|
||||
return Challenge(AuthenticationSchemes.Cookie);
|
||||
}
|
||||
var wallets = new ListWalletsViewModel();
|
||||
var stores = await Repository.GetStoresByUserId(GetUserId());
|
||||
|
||||
|
@ -209,7 +217,8 @@ namespace BTCPayServer.Controllers
|
|||
ListWalletsViewModel.WalletViewModel walletVm = new ListWalletsViewModel.WalletViewModel();
|
||||
wallets.Wallets.Add(walletVm);
|
||||
walletVm.Balance = await wallet.Balance + " " + wallet.Wallet.Network.CryptoCode;
|
||||
if (!wallet.Store.HasClaim(Policies.CanModifyStoreSettings.Key))
|
||||
walletVm.IsOwner = wallet.Store.Role == StoreRoles.Owner;
|
||||
if (!walletVm.IsOwner)
|
||||
{
|
||||
walletVm.Balance = "";
|
||||
}
|
||||
|
@ -217,7 +226,6 @@ namespace BTCPayServer.Controllers
|
|||
walletVm.StoreId = wallet.Store.Id;
|
||||
walletVm.Id = new WalletId(wallet.Store.Id, wallet.Network.CryptoCode);
|
||||
walletVm.StoreName = wallet.Store.StoreName;
|
||||
walletVm.IsOwner = wallet.Store.HasClaim(Policies.CanModifyStoreSettings.Key);
|
||||
}
|
||||
|
||||
return View(wallets);
|
||||
|
@ -229,7 +237,7 @@ namespace BTCPayServer.Controllers
|
|||
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId, string labelFilter = null)
|
||||
{
|
||||
DerivationSchemeSettings paymentMethod = await GetDerivationSchemeSettings(walletId);
|
||||
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId);
|
||||
if (paymentMethod == null)
|
||||
return NotFound();
|
||||
|
||||
|
@ -279,7 +287,7 @@ namespace BTCPayServer.Controllers
|
|||
if (walletId?.StoreId == null)
|
||||
return NotFound();
|
||||
var store = await Repository.FindStore(walletId.StoreId, GetUserId());
|
||||
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId, store);
|
||||
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId);
|
||||
if (paymentMethod == null)
|
||||
return NotFound();
|
||||
var network = this.NetworkProvider.GetNetwork<BTCPayNetwork>(walletId?.CryptoCode);
|
||||
|
@ -422,7 +430,7 @@ namespace BTCPayServer.Controllers
|
|||
if (!ModelState.IsValid)
|
||||
return View(vm);
|
||||
|
||||
DerivationSchemeSettings derivationScheme = await GetDerivationSchemeSettings(walletId);
|
||||
DerivationSchemeSettings derivationScheme = GetDerivationSchemeSettings(walletId);
|
||||
|
||||
CreatePSBTResponse psbt = null;
|
||||
try
|
||||
|
@ -511,7 +519,7 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
|
||||
ExtKey signingKey = null;
|
||||
var settings = (await GetDerivationSchemeSettings(walletId));
|
||||
var settings = GetDerivationSchemeSettings(walletId);
|
||||
var signingKeySettings = settings.GetSigningAccountKeySettings();
|
||||
if (signingKeySettings.RootFingerprint is null)
|
||||
signingKeySettings.RootFingerprint = extKey.GetPublicKey().GetHDFingerPrint();
|
||||
|
@ -557,13 +565,13 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
}
|
||||
|
||||
private async Task<IActionResult> RedirectToWalletTransaction(WalletId walletId, Transaction transaction)
|
||||
private IActionResult RedirectToWalletTransaction(WalletId walletId, Transaction transaction)
|
||||
{
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
||||
if (transaction != null)
|
||||
{
|
||||
var wallet = _walletProvider.GetWallet(network);
|
||||
var derivationSettings = await GetDerivationSchemeSettings(walletId);
|
||||
var derivationSettings = GetDerivationSchemeSettings(walletId);
|
||||
wallet.InvalidateCache(derivationSettings.AccountDerivation);
|
||||
StatusMessage = $"Transaction broadcasted successfully ({transaction.GetHash().ToString()})";
|
||||
}
|
||||
|
@ -578,13 +586,13 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
if (walletId?.StoreId == null)
|
||||
return NotFound();
|
||||
DerivationSchemeSettings paymentMethod = await GetDerivationSchemeSettings(walletId);
|
||||
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId);
|
||||
if (paymentMethod == null)
|
||||
return NotFound();
|
||||
|
||||
var vm = new RescanWalletModel();
|
||||
vm.IsFullySync = _dashboard.IsFullySynched(walletId.CryptoCode, out var unused);
|
||||
vm.IsServerAdmin = User.Claims.Any(c => c.Type == Policies.CanModifyServerSettings.Key && c.Value == "true");
|
||||
vm.IsServerAdmin = (await _authorizationService.AuthorizeAsync(User, Policies.CanModifyServerSettings.Key)).Succeeded;
|
||||
vm.IsSupportedByCurrency = _dashboard.Get(walletId.CryptoCode)?.Status?.BitcoinStatus?.Capabilities?.CanScanTxoutSet == true;
|
||||
var explorer = ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode);
|
||||
var scanProgress = await explorer.GetScanUTXOSetInformationAsync(paymentMethod.AccountDerivation);
|
||||
|
@ -614,14 +622,14 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
[HttpPost]
|
||||
[Route("{walletId}/rescan")]
|
||||
[Authorize(Policy = Policies.CanModifyServerSettings.Key, AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(Policy = Policies.CanModifyServerSettings.Key, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
public async Task<IActionResult> WalletRescan(
|
||||
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId, RescanWalletModel vm)
|
||||
{
|
||||
if (walletId?.StoreId == null)
|
||||
return NotFound();
|
||||
DerivationSchemeSettings paymentMethod = await GetDerivationSchemeSettings(walletId);
|
||||
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId);
|
||||
if (paymentMethod == null)
|
||||
return NotFound();
|
||||
var explorer = ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode);
|
||||
|
@ -649,24 +657,23 @@ namespace BTCPayServer.Controllers
|
|||
return null;
|
||||
}
|
||||
|
||||
private DerivationSchemeSettings GetDerivationSchemeSettings(WalletId walletId, StoreData store)
|
||||
public StoreData CurrentStore
|
||||
{
|
||||
if (store == null || !store.HasClaim(Policies.CanModifyStoreSettings.Key))
|
||||
return null;
|
||||
get
|
||||
{
|
||||
return HttpContext.GetStoreData();
|
||||
}
|
||||
}
|
||||
|
||||
var paymentMethod = store
|
||||
private DerivationSchemeSettings GetDerivationSchemeSettings(WalletId walletId)
|
||||
{
|
||||
var paymentMethod = CurrentStore
|
||||
.GetSupportedPaymentMethods(NetworkProvider)
|
||||
.OfType<DerivationSchemeSettings>()
|
||||
.FirstOrDefault(p => p.PaymentId.PaymentType == Payments.PaymentTypes.BTCLike && p.PaymentId.CryptoCode == walletId.CryptoCode);
|
||||
return paymentMethod;
|
||||
}
|
||||
|
||||
private async Task<DerivationSchemeSettings> GetDerivationSchemeSettings(WalletId walletId)
|
||||
{
|
||||
var store = (await Repository.FindStore(walletId.StoreId, GetUserId()));
|
||||
return GetDerivationSchemeSettings(walletId, store);
|
||||
}
|
||||
|
||||
private static async Task<string> GetBalanceString(BTCPayWallet wallet, DerivationStrategyBase derivationStrategy)
|
||||
{
|
||||
using (CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
|
||||
|
@ -703,12 +710,11 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
if (!HttpContext.WebSockets.IsWebSocketRequest)
|
||||
return NotFound();
|
||||
|
||||
var storeData = CurrentStore;
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
||||
if (network == null)
|
||||
throw new FormatException("Invalid value for crypto code");
|
||||
var storeData = (await Repository.FindStore(walletId.StoreId, GetUserId()));
|
||||
var derivationSettings = GetDerivationSchemeSettings(walletId, storeData);
|
||||
var derivationSettings = GetDerivationSchemeSettings(walletId);
|
||||
|
||||
var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||
|
||||
|
@ -813,7 +819,7 @@ namespace BTCPayServer.Controllers
|
|||
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId)
|
||||
{
|
||||
var derivationSchemeSettings = await GetDerivationSchemeSettings(walletId);
|
||||
var derivationSchemeSettings = GetDerivationSchemeSettings(walletId);
|
||||
if (derivationSchemeSettings == null)
|
||||
return NotFound();
|
||||
var store = (await Repository.FindStore(walletId.StoreId, GetUserId()));
|
||||
|
@ -842,7 +848,7 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
if (!ModelState.IsValid)
|
||||
return View(vm);
|
||||
var derivationScheme = await GetDerivationSchemeSettings(walletId);
|
||||
var derivationScheme = GetDerivationSchemeSettings(walletId);
|
||||
if (derivationScheme == null)
|
||||
return NotFound();
|
||||
derivationScheme.Label = vm.Label;
|
||||
|
|
|
@ -16,29 +16,6 @@ namespace BTCPayServer.Data
|
|||
{
|
||||
public static class StoreDataExtensions
|
||||
{
|
||||
public static bool HasClaim(this StoreData storeData, string claim)
|
||||
{
|
||||
return storeData.GetClaims().Any(c => c.Type == claim && c.Value == storeData.Id);
|
||||
}
|
||||
public static Claim[] GetClaims(this StoreData storeData)
|
||||
{
|
||||
List<Claim> claims = new List<Claim>();
|
||||
claims.AddRange(storeData.AdditionalClaims);
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
var role = storeData.Role;
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
if (role == StoreRoles.Owner)
|
||||
{
|
||||
claims.Add(new Claim(Policies.CanModifyStoreSettings.Key, storeData.Id));
|
||||
}
|
||||
|
||||
if (role == StoreRoles.Owner || role == StoreRoles.Guest || storeData.GetStoreBlob().AnyoneCanInvoice)
|
||||
{
|
||||
claims.Add(new Claim(Policies.CanCreateInvoice.Key, storeData.Id));
|
||||
}
|
||||
return claims.ToArray();
|
||||
}
|
||||
|
||||
#pragma warning disable CS0618
|
||||
public static PaymentMethodId GetDefaultPaymentId(this StoreData storeData, BTCPayNetworkProvider networks)
|
||||
{
|
||||
|
|
|
@ -326,12 +326,7 @@ namespace BTCPayServer
|
|||
|
||||
public static string GetSIN(this ClaimsPrincipal principal)
|
||||
{
|
||||
return principal.Claims.Where(c => c.Type == Claims.SIN).Select(c => c.Value).FirstOrDefault();
|
||||
}
|
||||
|
||||
public static string GetStoreId(this ClaimsPrincipal principal)
|
||||
{
|
||||
return principal.Claims.Where(c => c.Type == Claims.OwnStore).Select(c => c.Value).FirstOrDefault();
|
||||
return principal.Claims.Where(c => c.Type == Security.Bitpay.BitpayClaims.SIN).Select(c => c.Value).FirstOrDefault();
|
||||
}
|
||||
|
||||
public static void SetIsBitpayAPI(this HttpContext ctx, bool value)
|
||||
|
|
|
@ -54,6 +54,8 @@ using Microsoft.AspNetCore.Hosting;
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using BTCPayServer.Security.Bitpay;
|
||||
|
||||
namespace BTCPayServer.Hosting
|
||||
{
|
||||
|
@ -176,7 +178,6 @@ namespace BTCPayServer.Hosting
|
|||
return htmlSanitizer;
|
||||
});
|
||||
|
||||
services.AddTransient<IClaimsTransformation, ClaimTransformer>();
|
||||
services.TryAddSingleton<LightningConfigurationProvider>();
|
||||
services.TryAddSingleton<LanguageService>();
|
||||
services.TryAddSingleton<NBXplorerDashboard>();
|
||||
|
@ -224,7 +225,9 @@ namespace BTCPayServer.Hosting
|
|||
services.AddSingleton<IHostedService, TorServicesHostedService>();
|
||||
services.AddSingleton<IHostedService, PaymentRequestStreamer>();
|
||||
services.AddSingleton<IBackgroundJobClient, BackgroundJobClient>();
|
||||
services.AddTransient<IConfigureOptions<MvcOptions>, BTCPayClaimsFilter>();
|
||||
services.AddScoped<IAuthorizationHandler, CookieAuthorizationHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, OpenIdAuthorizationHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, BitpayAuthorizationHandler>();
|
||||
|
||||
services.TryAddSingleton<ExplorerClientProvider>();
|
||||
services.TryAddSingleton<Bitpay>(o =>
|
||||
|
@ -246,8 +249,8 @@ namespace BTCPayServer.Hosting
|
|||
services.AddSingleton<EmailSenderFactory>();
|
||||
// bundling
|
||||
|
||||
services.AddAuthorization(o => o.AddBTCPayPolicies().AddBTCPayRESTApiPolicies());
|
||||
services.AddBtcPayServerAuthenticationSchemes(configuration);
|
||||
services.AddAuthorization(o => o.AddBTCPayPolicies());
|
||||
|
||||
services.AddSingleton<IBundleProvider, ResourceBundleProvider>();
|
||||
services.AddTransient<BundleOptions>(provider =>
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace BTCPayServer.Models.StoreViewModels
|
|||
|
||||
[Display(Name = "Pair to")]
|
||||
[Required]
|
||||
public string SelectedStore
|
||||
public string StoreId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace BTCPayServer.Security
|
|||
{
|
||||
public static AuthenticationBuilder AddBitpayAuthentication(this AuthenticationBuilder builder)
|
||||
{
|
||||
builder.AddScheme<BitpayAuthenticationOptions, BitpayAuthenticationHandler>(Policies.BitpayAuthentication, o => { });
|
||||
builder.AddScheme<BitpayAuthenticationOptions, BitpayAuthenticationHandler>(AuthenticationSchemes.Bitpay, o => { });
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
|
15
BTCPayServer/Security/AuthenticationSchemes.cs
Normal file
15
BTCPayServer/Security/AuthenticationSchemes.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using OpenIddict.Validation;
|
||||
|
||||
namespace BTCPayServer.Security
|
||||
{
|
||||
public class AuthenticationSchemes
|
||||
{
|
||||
public const string Cookie = "Identity.Application";
|
||||
public const string Bitpay = "Bitpay";
|
||||
public const string OpenId = OpenIddictValidationDefaults.AuthenticationScheme;
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace BTCPayServer.Security
|
||||
{
|
||||
public class BTCPayClaimsFilter : IAsyncAuthorizationFilter, IConfigureOptions<MvcOptions>
|
||||
{
|
||||
UserManager<ApplicationUser> _userManager;
|
||||
StoreRepository _StoreRepository;
|
||||
public BTCPayClaimsFilter(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
StoreRepository storeRepository)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_StoreRepository = storeRepository;
|
||||
}
|
||||
|
||||
void IConfigureOptions<MvcOptions>.Configure(MvcOptions options)
|
||||
{
|
||||
options.Filters.Add(typeof(BTCPayClaimsFilter));
|
||||
}
|
||||
|
||||
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||
{
|
||||
if (context.HttpContext.User?.Identity?.AuthenticationType != Policies.CookieAuthentication)
|
||||
return;
|
||||
var principal = context.HttpContext.User;
|
||||
var identity = ((ClaimsIdentity)principal.Identity);
|
||||
if (principal.IsInRole(Roles.ServerAdmin))
|
||||
{
|
||||
identity.AddClaim(new Claim(Policies.CanModifyServerSettings.Key, "true"));
|
||||
}
|
||||
|
||||
if (context.RouteData.Values.TryGetValue("storeId", out var storeId))
|
||||
{
|
||||
var userid = _userManager.GetUserId(principal);
|
||||
|
||||
if (!string.IsNullOrEmpty(userid))
|
||||
{
|
||||
var store = await _StoreRepository.FindStore((string)storeId, userid);
|
||||
if (store == null)
|
||||
{
|
||||
context.Result = new ChallengeResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
context.HttpContext.SetStoreData(store);
|
||||
identity.AddClaims(store.GetClaims());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,63 +41,39 @@ namespace BTCPayServer.Security.Bitpay
|
|||
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
List<Claim> claims = new List<Claim>();
|
||||
if (!Context.Request.HttpContext.TryGetBitpayAuth(out var bitpayAuth))
|
||||
return AuthenticateResult.NoResult();
|
||||
string storeId = null;
|
||||
bool anonymous = true;
|
||||
bool? success = null;
|
||||
if (!string.IsNullOrEmpty(bitpayAuth.Signature) && !string.IsNullOrEmpty(bitpayAuth.Id))
|
||||
{
|
||||
var result = await CheckBitId(Context.Request.HttpContext, bitpayAuth.Signature, bitpayAuth.Id, claims);
|
||||
storeId = result.StoreId;
|
||||
success = result.SuccessAuth;
|
||||
anonymous = false;
|
||||
var sin = await CheckBitId(Context.Request.HttpContext, bitpayAuth.Signature, bitpayAuth.Id);
|
||||
if (sin == null)
|
||||
return AuthenticateResult.Fail("BitId authentication failed");
|
||||
return Success(BitpayClaims.SIN, sin, BitpayAuthenticationTypes.SinAuthentication);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(bitpayAuth.Authorization))
|
||||
{
|
||||
storeId = await CheckLegacyAPIKey(Context.Request.HttpContext, bitpayAuth.Authorization);
|
||||
success = storeId != null;
|
||||
anonymous = false;
|
||||
var storeId = await GetStoreIdFromAuth(Context.Request.HttpContext, bitpayAuth.Authorization);
|
||||
if (storeId == null)
|
||||
return AuthenticateResult.Fail("ApiKey authentication failed");
|
||||
return Success(BitpayClaims.ApiKeyStoreId, storeId, BitpayAuthenticationTypes.ApiKeyAuthentication);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Context.Request.HttpContext.Request.Query.TryGetValue("storeId", out var storeIdStringValues))
|
||||
{
|
||||
storeId = storeIdStringValues.FirstOrDefault() ?? string.Empty;
|
||||
success = true;
|
||||
anonymous = true;
|
||||
}
|
||||
return Success(null, null, BitpayAuthenticationTypes.Anonymous);
|
||||
}
|
||||
|
||||
if (success is true)
|
||||
{
|
||||
if (storeId != null)
|
||||
{
|
||||
claims.Add(new Claim(Policies.CanCreateInvoice.Key, storeId));
|
||||
var store = await _StoreRepository.FindStore(storeId);
|
||||
if (store == null ||
|
||||
(anonymous && !store.GetStoreBlob().AnyoneCanInvoice))
|
||||
{
|
||||
return AuthenticateResult.Fail("Invalid credentials");
|
||||
}
|
||||
store.AdditionalClaims.AddRange(claims);
|
||||
Context.Request.HttpContext.SetStoreData(store);
|
||||
}
|
||||
return AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(new ClaimsIdentity(claims, Policies.BitpayAuthentication)), Policies.BitpayAuthentication));
|
||||
}
|
||||
else if (success is false)
|
||||
{
|
||||
return AuthenticateResult.Fail("Invalid credentials");
|
||||
}
|
||||
return AuthenticateResult.NoResult();
|
||||
}
|
||||
|
||||
private async Task<(string StoreId, bool SuccessAuth)> CheckBitId(HttpContext httpContext, string sig, string id, List<Claim> claims)
|
||||
private AuthenticateResult Success(string claimType, string claimValue, string authenticationType)
|
||||
{
|
||||
List<Claim> claims = new List<Claim>();
|
||||
if (claimType != null)
|
||||
claims.Add(new Claim(claimType, claimValue));
|
||||
return AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(new ClaimsIdentity(claims, authenticationType)), authenticationType));
|
||||
}
|
||||
|
||||
private async Task<string> CheckBitId(HttpContext httpContext, string sig, string id)
|
||||
{
|
||||
httpContext.Request.EnableBuffering();
|
||||
|
||||
string storeId = null;
|
||||
string body = string.Empty;
|
||||
if (httpContext.Request.ContentLength != 0 && httpContext.Request.Body != null)
|
||||
{
|
||||
|
@ -114,44 +90,14 @@ namespace BTCPayServer.Security.Bitpay
|
|||
var key = new PubKey(id);
|
||||
if (BitIdExtensions.CheckBitIDSignature(key, sig, url, body))
|
||||
{
|
||||
var sin = key.GetBitIDSIN();
|
||||
claims.Add(new Claim(Claims.SIN, sin));
|
||||
|
||||
string token = null;
|
||||
if (httpContext.Request.Query.TryGetValue("token", out var tokenValues))
|
||||
{
|
||||
token = tokenValues[0];
|
||||
}
|
||||
|
||||
if (token == null && !String.IsNullOrEmpty(body) && httpContext.Request.Method == "POST")
|
||||
{
|
||||
try
|
||||
{
|
||||
token = JObject.Parse(body)?.Property("token", StringComparison.OrdinalIgnoreCase)?.Value?.Value<string>();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (token != null)
|
||||
{
|
||||
var bitToken = (await _TokenRepository.GetTokens(sin)).FirstOrDefault();
|
||||
if (bitToken == null)
|
||||
{
|
||||
return (null, false);
|
||||
}
|
||||
storeId = bitToken.StoreId;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return (storeId, false);
|
||||
return key.GetBitIDSIN();
|
||||
}
|
||||
}
|
||||
catch (FormatException) { }
|
||||
return (storeId, true);
|
||||
catch { }
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<string> CheckLegacyAPIKey(HttpContext httpContext, string auth)
|
||||
private async Task<string> GetStoreIdFromAuth(HttpContext httpContext, string auth)
|
||||
{
|
||||
var splitted = auth.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (splitted.Length != 2 || !splitted[0].Equals("Basic", StringComparison.OrdinalIgnoreCase))
|
||||
|
|
14
BTCPayServer/Security/Bitpay/BitpayAuthenticationTypes.cs
Normal file
14
BTCPayServer/Security/Bitpay/BitpayAuthenticationTypes.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Security.Bitpay
|
||||
{
|
||||
public class BitpayAuthenticationTypes
|
||||
{
|
||||
public const string ApiKeyAuthentication = "Bitpay.APIKey";
|
||||
public const string SinAuthentication = "Bitpay.SIN";
|
||||
public const string Anonymous = "Bitpay.Anonymous";
|
||||
}
|
||||
}
|
73
BTCPayServer/Security/Bitpay/BitpayAuthorizationHandler.cs
Normal file
73
BTCPayServer/Security/Bitpay/BitpayAuthorizationHandler.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using BTCPayServer.Authentication;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Security.Bitpay;
|
||||
|
||||
namespace BTCPayServer.Security.Bitpay
|
||||
{
|
||||
public class BitpayAuthorizationHandler : AuthorizationHandler<PolicyRequirement>
|
||||
{
|
||||
private readonly HttpContext _HttpContext;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly TokenRepository _tokenRepository;
|
||||
|
||||
public BitpayAuthorizationHandler(IHttpContextAccessor httpContextAccessor,
|
||||
StoreRepository storeRepository,
|
||||
TokenRepository tokenRepository)
|
||||
{
|
||||
_HttpContext = httpContextAccessor.HttpContext;
|
||||
_storeRepository = storeRepository;
|
||||
_tokenRepository = tokenRepository;
|
||||
}
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement)
|
||||
{
|
||||
string storeId = null;
|
||||
if (context.User.Identity.AuthenticationType == BitpayAuthenticationTypes.ApiKeyAuthentication)
|
||||
{
|
||||
storeId = context.User.Claims.Where(c => c.Type == BitpayClaims.ApiKeyStoreId).Select(c => c.Value).First();
|
||||
}
|
||||
else if (context.User.Identity.AuthenticationType == BitpayAuthenticationTypes.SinAuthentication)
|
||||
{
|
||||
var sin = context.User.Claims.Where(c => c.Type == BitpayClaims.SIN).Select(c => c.Value).First();
|
||||
var bitToken = (await _tokenRepository.GetTokens(sin)).FirstOrDefault();
|
||||
storeId = bitToken?.StoreId;
|
||||
}
|
||||
else if (context.User.Identity.AuthenticationType == BitpayAuthenticationTypes.Anonymous)
|
||||
{
|
||||
storeId = _HttpContext.GetImplicitStoreId();
|
||||
}
|
||||
if (storeId == null)
|
||||
return;
|
||||
var store = await _storeRepository.FindStore(storeId);
|
||||
if (store == null)
|
||||
return;
|
||||
var isAnonymous = context.User.Identity.AuthenticationType == BitpayAuthenticationTypes.Anonymous;
|
||||
var anyoneCanInvoice = store.GetStoreBlob().AnyoneCanInvoice;
|
||||
switch (requirement.Policy)
|
||||
{
|
||||
case Policies.CanCreateInvoice.Key:
|
||||
if (!isAnonymous || (isAnonymous && anyoneCanInvoice))
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
_HttpContext.SetStoreData(store);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case Policies.CanGetRates.Key:
|
||||
context.Succeed(requirement);
|
||||
_HttpContext.SetStoreData(store);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
BTCPayServer/Security/Bitpay/BitpayClaims.cs
Normal file
13
BTCPayServer/Security/Bitpay/BitpayClaims.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Security.Bitpay
|
||||
{
|
||||
public class BitpayClaims
|
||||
{
|
||||
public const string SIN = "Bitpay.SIN";
|
||||
public const string ApiKeyStoreId = "Bitpay.ApiKeyStoreId";
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using BTCPayServer.Authentication;
|
||||
|
||||
namespace BTCPayServer.Security
|
||||
{
|
||||
public class ClaimTransformer : IClaimsTransformation
|
||||
{
|
||||
private readonly HttpContext _HttpContext;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
|
||||
public ClaimTransformer(IHttpContextAccessor httpContextAccessor,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
StoreRepository storeRepository)
|
||||
{
|
||||
_HttpContext = httpContextAccessor.HttpContext;
|
||||
_userManager = userManager;
|
||||
_storeRepository = storeRepository;
|
||||
}
|
||||
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
|
||||
{
|
||||
var routeData = _HttpContext.GetRouteData();
|
||||
if (routeData == null)
|
||||
return principal;
|
||||
var identity = ((ClaimsIdentity)principal.Identity);
|
||||
// A ClaimTransform can be called several time, we prevent dups by removing all the
|
||||
// claims this transform might add.
|
||||
var claims = new[] { RestAPIPolicies.CanEditStore };
|
||||
foreach (var claim in identity.Claims.Where(c => claims.Contains(c.Type)).ToList())
|
||||
{
|
||||
identity.RemoveClaim(claim);
|
||||
}
|
||||
|
||||
if (!routeData.Values.TryGetValue("storeId", out var storeId))
|
||||
{
|
||||
return principal;
|
||||
}
|
||||
var userid = _userManager.GetUserId(principal);
|
||||
if (!string.IsNullOrEmpty(userid))
|
||||
{
|
||||
var store = await _storeRepository.FindStore((string)storeId, userid);
|
||||
if (store != null)
|
||||
{
|
||||
_HttpContext.SetStoreData(store);
|
||||
foreach (var claim in store.GetClaims())
|
||||
{
|
||||
if (claim.Type.Equals(Policies.CanModifyStoreSettings.Key, System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
identity.AddClaim(new Claim(RestAPIPolicies.CanEditStore, store.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return principal;
|
||||
}
|
||||
}
|
||||
}
|
81
BTCPayServer/Security/CookieAuthorizationHandler.cs
Normal file
81
BTCPayServer/Security/CookieAuthorizationHandler.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using BTCPayServer.Authentication;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace BTCPayServer.Security
|
||||
{
|
||||
public class CookieAuthorizationHandler : AuthorizationHandler<PolicyRequirement>
|
||||
{
|
||||
private readonly HttpContext _HttpContext;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
|
||||
public CookieAuthorizationHandler(IHttpContextAccessor httpContextAccessor,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
StoreRepository storeRepository)
|
||||
{
|
||||
_HttpContext = httpContextAccessor.HttpContext;
|
||||
_userManager = userManager;
|
||||
_storeRepository = storeRepository;
|
||||
}
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement)
|
||||
{
|
||||
if (context.User.Identity.AuthenticationType != AuthenticationSchemes.Cookie)
|
||||
return;
|
||||
|
||||
var isAdmin = context.User.IsInRole(Roles.ServerAdmin);
|
||||
switch (requirement.Policy)
|
||||
{
|
||||
case Policies.CanModifyServerSettings.Key:
|
||||
if (isAdmin)
|
||||
context.Succeed(requirement);
|
||||
return;
|
||||
}
|
||||
|
||||
string storeId = _HttpContext.GetImplicitStoreId();
|
||||
if (storeId == null)
|
||||
return;
|
||||
|
||||
var userid = _userManager.GetUserId(context.User);
|
||||
if (string.IsNullOrEmpty(userid))
|
||||
return;
|
||||
|
||||
|
||||
var store = await _storeRepository.FindStore((string)storeId, userid);
|
||||
if (store == null)
|
||||
return;
|
||||
bool success = false;
|
||||
switch (requirement.Policy)
|
||||
{
|
||||
case Policies.CanModifyStoreSettings.Key:
|
||||
if (store.Role == StoreRoles.Owner || isAdmin)
|
||||
success = true;
|
||||
break;
|
||||
case Policies.CanCreateInvoice.Key:
|
||||
if (store.Role == StoreRoles.Owner ||
|
||||
store.Role == StoreRoles.Guest ||
|
||||
isAdmin ||
|
||||
store.GetStoreBlob().AnyoneCanInvoice)
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
_HttpContext.SetStoreData(store);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
92
BTCPayServer/Security/OpenIdAuthorizationHandler.cs
Normal file
92
BTCPayServer/Security/OpenIdAuthorizationHandler.cs
Normal file
|
@ -0,0 +1,92 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using BTCPayServer.Authentication;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using static BTCPayServer.Authentication.RestAPIPolicies;
|
||||
using OpenIddict.Abstractions;
|
||||
|
||||
namespace BTCPayServer.Security
|
||||
{
|
||||
public class OpenIdAuthorizationHandler : AuthorizationHandler<PolicyRequirement>
|
||||
{
|
||||
private readonly HttpContext _HttpContext;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
|
||||
public OpenIdAuthorizationHandler(IHttpContextAccessor httpContextAccessor,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
StoreRepository storeRepository)
|
||||
{
|
||||
_HttpContext = httpContextAccessor.HttpContext;
|
||||
_userManager = userManager;
|
||||
_storeRepository = storeRepository;
|
||||
}
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement)
|
||||
{
|
||||
if (context.User.Identity.AuthenticationType != "AuthenticationTypes.Federation")
|
||||
return;
|
||||
|
||||
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:
|
||||
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);
|
||||
break;
|
||||
case Policies.CanModifyServerSettings.Key:
|
||||
success = context.User.HasClaim("role", Roles.ServerAdmin);
|
||||
break;
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,26 +2,35 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace BTCPayServer.Security
|
||||
{
|
||||
public static class Policies
|
||||
{
|
||||
|
||||
public const string BitpayAuthentication = "Bitpay.Auth";
|
||||
public const string CookieAuthentication = "Identity.Application";
|
||||
public static AuthorizationOptions AddBTCPayPolicies(this AuthorizationOptions options)
|
||||
{
|
||||
AddClaim(options, CanModifyStoreSettings.Key);
|
||||
AddClaim(options, CanModifyServerSettings.Key);
|
||||
AddClaim(options, CanCreateInvoice.Key);
|
||||
options.AddPolicy(CanModifyStoreSettings.Key);
|
||||
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;
|
||||
}
|
||||
|
||||
private static void AddClaim(AuthorizationOptions options, string key)
|
||||
public static void AddPolicy(this AuthorizationOptions options, string policy)
|
||||
{
|
||||
options.AddPolicy(key, o => o.RequireClaim(key));
|
||||
options.AddPolicy(policy, o => o.AddRequirements(new PolicyRequirement(policy)));
|
||||
}
|
||||
|
||||
public class CanModifyServerSettings
|
||||
|
@ -36,6 +45,10 @@ namespace BTCPayServer.Security
|
|||
{
|
||||
public const string Key = "btcpay.store.cancreateinvoice";
|
||||
}
|
||||
|
||||
|
||||
public class CanGetRates
|
||||
{
|
||||
public const string Key = "btcpay.store.cangetrates";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
19
BTCPayServer/Security/PolicyRequirement.cs
Normal file
19
BTCPayServer/Security/PolicyRequirement.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace BTCPayServer.Security
|
||||
{
|
||||
public class PolicyRequirement : IAuthorizationRequirement
|
||||
{
|
||||
public PolicyRequirement(string policy)
|
||||
{
|
||||
if (policy == null)
|
||||
throw new ArgumentNullException(nameof(policy));
|
||||
Policy = policy;
|
||||
}
|
||||
public string Policy { get; }
|
||||
}
|
||||
}
|
64
BTCPayServer/Security/SecurityExtensions.cs
Normal file
64
BTCPayServer/Security/SecurityExtensions.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using OpenIddict.Abstractions;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace BTCPayServer.Security
|
||||
{
|
||||
public static class SecurityExtensions
|
||||
{
|
||||
public static bool HasScopes(this AuthorizationHandlerContext context, params string[] scopes)
|
||||
{
|
||||
return scopes.All(s => context.User.HasClaim(OpenIddictConstants.Claims.Scope, s));
|
||||
}
|
||||
public static string GetImplicitStoreId(this HttpContext httpContext)
|
||||
{
|
||||
// 1. Check in the routeData
|
||||
var routeData = httpContext.GetRouteData();
|
||||
string storeId = null;
|
||||
if (routeData != null)
|
||||
{
|
||||
if (routeData.Values.TryGetValue("storeId", out var v))
|
||||
storeId = v as string;
|
||||
}
|
||||
|
||||
if (storeId == null)
|
||||
{
|
||||
if (httpContext.Request.Query.TryGetValue("storeId", out var sv))
|
||||
{
|
||||
storeId = sv.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Check in forms
|
||||
if (storeId == null)
|
||||
{
|
||||
if (httpContext.Request.HasFormContentType &&
|
||||
httpContext.Request.Form != null &&
|
||||
httpContext.Request.Form.TryGetValue("storeId", out var sv))
|
||||
{
|
||||
storeId = sv.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Checks in walletId
|
||||
if (storeId == null && routeData != null)
|
||||
{
|
||||
if (routeData.Values.TryGetValue("walletId", out var walletId) &&
|
||||
WalletId.TryParse((string)walletId, out var w))
|
||||
{
|
||||
storeId = w.StoreId;
|
||||
}
|
||||
}
|
||||
|
||||
return storeId;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,9 +25,9 @@ namespace BTCPayServer.Services.Altcoins.Monero.UI
|
|||
{
|
||||
[Route("stores/{storeId}/monerolike")]
|
||||
[OnlyIfSupportAttribute("XMR")]
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings.Key, AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(Policy = Policies.CanModifyServerSettings.Key, AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings.Key, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Authorize(Policy = Policies.CanModifyServerSettings.Key, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
public class MoneroLikeStoreController : Controller
|
||||
{
|
||||
private readonly MoneroLikeConfiguration _MoneroLikeConfiguration;
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Services
|
||||
{
|
||||
public class Claims
|
||||
{
|
||||
public const string SIN = "BITID_SIN";
|
||||
|
||||
public const string OwnStore = "BTCPAY_OWN_STORE";
|
||||
}
|
||||
}
|
|
@ -48,9 +48,7 @@ namespace BTCPayServer.Services.Stores
|
|||
}).ToArrayAsync())
|
||||
.Select(us =>
|
||||
{
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
us.Store.Role = us.Role;
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
return us.Store;
|
||||
}).FirstOrDefault();
|
||||
}
|
||||
|
@ -90,9 +88,7 @@ namespace BTCPayServer.Services.Stores
|
|||
.ToArrayAsync())
|
||||
.Select(u =>
|
||||
{
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
u.StoreData.Role = u.Role;
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
return u.StoreData;
|
||||
}).ToArray();
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
<div class="row mb-2">
|
||||
<div class="col-lg-12 text-center">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-lg btn-primary" name="consent" id="consent-yes" type="submit" value="Yes">Authorize app</button>
|
||||
<button class="btn btn-primary" name="consent" id="consent-yes" type="submit" value="Yes">Authorize app</button>
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
|
@ -57,7 +57,7 @@
|
|||
<button class="dropdown-item" name="consent" id="consent-yes-temporary" type="submit" value="YesTemporary">Authorize app until session ends</button>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-lg btn-secondary" id="consent-no" name="consent" type="submit" value="No">Cancel</button>
|
||||
<button class="btn btn-secondary" id="consent-no" name="consent" type="submit" value="No">Cancel</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
{
|
||||
<input type="hidden" asp-for="StoreId" />}
|
||||
<div class="form-group">
|
||||
<input type="submit" value="Request pairing" class="btn btn-primary" />
|
||||
<input id="RequestPairing" type="submit" value="Request pairing" class="btn btn-primary" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<a asp-action="CreateToken" class="btn btn-primary" role="button"><span class="fa fa-plus"></span> Create a new token</a>
|
||||
<a id="CreateNewToken" asp-action="CreateToken" class="btn btn-primary" role="button"><span class="fa fa-plus"></span> Create a new token</a>
|
||||
<table class="table table-sm table-responsive-md">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
|
@ -33,12 +33,12 @@
|
|||
<div class="col-md-4">
|
||||
<form asp-action="Pair" method="post">
|
||||
<div class="form-group">
|
||||
<label asp-for="SelectedStore"></label>
|
||||
<select asp-for="SelectedStore" asp-items="@(new SelectList(Model.Stores,"Id","Name"))" class="form-control"></select>
|
||||
<span asp-validation-for="SelectedStore" class="text-danger"></span>
|
||||
<label asp-for="StoreId"></label>
|
||||
<select asp-for="StoreId" asp-items="@(new SelectList(Model.Stores,"Id","Name"))" class="form-control"></select>
|
||||
<span asp-validation-for="StoreId" class="text-danger"></span>
|
||||
</div>
|
||||
<input type="hidden" name="pairingCode" value="@Model.Id" />
|
||||
<button type="submit" class="btn btn-secondary" title="Approve this pairing demand">Approve</button>
|
||||
<button id="ApprovePairing" type="submit" class="btn btn-secondary" title="Approve this pairing demand">Approve</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-4"></div>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="nav flex-column nav-pills">
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Transactions)" asp-action="WalletTransactions" id="WalletTransactions">Transactions</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Send)" asp-action="WalletSend" id="WalletSend">Send</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Rescan)" asp-action="WalletRescan">Rescan</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Rescan)" asp-action="WalletRescan" id="WalletRescan">Rescan</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.PSBT)" asp-action="WalletPSBT">PSBT</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Settings)" asp-action="WalletSettings" id="WalletSettings">Settings</a>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Reference in a new issue