Move Bitpay authentication class in BTCPayServer.Security

This commit is contained in:
nicolas.dorier 2019-10-19 00:54:20 +09:00
parent 037fcf93f5
commit eac4c91820
No known key found for this signature in database
GPG key ID: 6618763EF09186FE
23 changed files with 10 additions and 362 deletions

View file

@ -12,7 +12,6 @@ using Xunit;
using Xunit.Abstractions;
using System.Net.Http;
using System.Net.Http.Headers;
using BTCPayServer.Authentication;
using BTCPayServer.Data;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

View file

@ -15,7 +15,6 @@ using System.IO;
using Newtonsoft.Json.Linq;
using BTCPayServer.Controllers;
using Microsoft.AspNetCore.Mvc;
using BTCPayServer.Authentication;
using System.Diagnostics;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore;
@ -60,6 +59,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using NBXplorer.DerivationStrategy;
using BTCPayServer.U2F.Models;
using BTCPayServer.Security.Bitpay;
namespace BTCPayServer.Tests
{

View file

@ -1,45 +0,0 @@
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
using NBitpayClient;
namespace BTCPayServer.Authentication
{
public class BitTokenEntity
{
public string Value
{
get; set;
}
public string StoreId
{
get; set;
}
public string Label
{
get; set;
}
public DateTimeOffset PairingTime
{
get; set;
}
public string SIN
{
get;
set;
}
public BitTokenEntity Clone(Facade facade)
{
return new BitTokenEntity()
{
Label = Label,
StoreId = StoreId,
PairingTime = PairingTime,
SIN = SIN,
Value = Value
};
}
}
}

View file

@ -1,45 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BTCPayServer.Authentication
{
public class PairingCodeEntity
{
public string Id
{
get;
set;
}
public string Label
{
get;
set;
}
public string SIN
{
get;
set;
}
public DateTimeOffset CreatedTime
{
get;
set;
}
public DateTimeOffset Expiration
{
get;
set;
}
public string TokenValue
{
get;
set;
}
public bool IsExpired()
{
return DateTimeOffset.UtcNow > Expiration;
}
}
}

View file

@ -1,247 +0,0 @@
using BTCPayServer.Data;
using DBriize;
using NBitcoin;
using NBitcoin.DataEncoders;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using System.Linq;
namespace BTCPayServer.Authentication
{
public enum PairingResult
{
Partial,
Complete,
ReusedKey,
Expired
}
public class TokenRepository
{
ApplicationDbContextFactory _Factory;
public TokenRepository(ApplicationDbContextFactory dbFactory)
{
if (dbFactory == null)
throw new ArgumentNullException(nameof(dbFactory));
_Factory = dbFactory;
}
public async Task<BitTokenEntity[]> GetTokens(string sin)
{
if (sin == null)
return Array.Empty<BitTokenEntity>();
using (var ctx = _Factory.CreateContext())
{
return (await ctx.PairedSINData.Where(p => p.SIN == sin)
.ToArrayAsync())
.Select(p => CreateTokenEntity(p))
.ToArray();
}
}
public async Task<String> GetStoreIdFromAPIKey(string apiKey)
{
using (var ctx = _Factory.CreateContext())
{
return await ctx.ApiKeys.Where(o => o.Id == apiKey).Select(o => o.StoreId).FirstOrDefaultAsync();
}
}
public async Task GenerateLegacyAPIKey(string storeId)
{
// It is legacy support and Bitpay generate string of unknown format, trying to replicate them
// as good as possible. The string below got generated for me.
var chars = "ERo0vkBMOYhyU0ZHvirCplbLDIGWPdi1ok77VnW7QdE";
var rand = new Random(Math.Abs(RandomUtils.GetInt32()));
var generated = new char[chars.Length];
for (int i = 0; i < generated.Length; i++)
{
generated[i] = chars[rand.Next(0, generated.Length)];
}
using (var ctx = _Factory.CreateContext())
{
var existing = await ctx.ApiKeys.Where(o => o.StoreId == storeId).FirstOrDefaultAsync();
if (existing != null)
{
ctx.ApiKeys.Remove(existing);
}
ctx.ApiKeys.Add(new APIKeyData() { Id = new string(generated), StoreId = storeId });
await ctx.SaveChangesAsync().ConfigureAwait(false);
}
}
public async Task<string[]> GetLegacyAPIKeys(string storeId)
{
using (var ctx = _Factory.CreateContext())
{
return await ctx.ApiKeys.Where(o => o.StoreId == storeId).Select(c => c.Id).ToArrayAsync();
}
}
private BitTokenEntity CreateTokenEntity(PairedSINData data)
{
return new BitTokenEntity()
{
Label = data.Label,
Value = data.Id,
SIN = data.SIN,
PairingTime = data.PairingTime,
StoreId = data.StoreDataId
};
}
public async Task<string> CreatePairingCodeAsync()
{
string pairingCodeId = null;
while (true)
{
pairingCodeId = Encoders.Base58.EncodeData(RandomUtils.GetBytes(6));
if (pairingCodeId.Length == 7) // woocommerce plugin check for exactly 7 digits
break;
}
using (var ctx = _Factory.CreateContext())
{
var now = DateTime.UtcNow;
var expiration = DateTime.UtcNow + TimeSpan.FromMinutes(15);
ctx.PairingCodes.Add(new PairingCodeData()
{
Id = pairingCodeId,
DateCreated = now,
Expiration = expiration,
TokenValue = Encoders.Base58.EncodeData(RandomUtils.GetBytes(32))
});
await ctx.SaveChangesAsync();
}
return pairingCodeId;
}
public async Task<PairingCodeEntity> UpdatePairingCode(PairingCodeEntity pairingCodeEntity)
{
using (var ctx = _Factory.CreateContext())
{
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeEntity.Id);
pairingCode.Label = pairingCodeEntity.Label;
await ctx.SaveChangesAsync();
return CreatePairingCodeEntity(pairingCode);
}
}
public async Task<PairingResult> PairWithStoreAsync(string pairingCodeId, string storeId)
{
using (var ctx = _Factory.CreateContext())
{
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeId);
if (pairingCode == null || pairingCode.Expiration < DateTimeOffset.UtcNow)
return PairingResult.Expired;
pairingCode.StoreDataId = storeId;
var result = await ActivateIfComplete(ctx, pairingCode);
await ctx.SaveChangesAsync();
return result;
}
}
public async Task<PairingResult> PairWithSINAsync(string pairingCodeId, string sin)
{
using (var ctx = _Factory.CreateContext())
{
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeId);
if (pairingCode == null || pairingCode.Expiration < DateTimeOffset.UtcNow)
return PairingResult.Expired;
pairingCode.SIN = sin;
var result = await ActivateIfComplete(ctx, pairingCode);
await ctx.SaveChangesAsync();
return result;
}
}
private async Task<PairingResult> ActivateIfComplete(ApplicationDbContext ctx, PairingCodeData pairingCode)
{
if (!string.IsNullOrEmpty(pairingCode.SIN) && !string.IsNullOrEmpty(pairingCode.StoreDataId))
{
ctx.PairingCodes.Remove(pairingCode);
// Can have concurrency issues... but no harm can be done
var alreadyUsed = await ctx.PairedSINData.Where(p => p.SIN == pairingCode.SIN && p.StoreDataId != pairingCode.StoreDataId).AnyAsync();
if (alreadyUsed)
return PairingResult.ReusedKey;
await ctx.PairedSINData.AddAsync(new PairedSINData()
{
Id = pairingCode.TokenValue,
PairingTime = DateTime.UtcNow,
Label = pairingCode.Label,
StoreDataId = pairingCode.StoreDataId,
SIN = pairingCode.SIN
});
return PairingResult.Complete;
}
return PairingResult.Partial;
}
public async Task<BitTokenEntity[]> GetTokensByStoreIdAsync(string storeId)
{
using (var ctx = _Factory.CreateContext())
{
return (await ctx.PairedSINData.Where(p => p.StoreDataId == storeId).ToListAsync())
.Select(c => CreateTokenEntity(c))
.ToArray();
}
}
public async Task<PairingCodeEntity> GetPairingAsync(string pairingCode)
{
using (var ctx = _Factory.CreateContext())
{
return CreatePairingCodeEntity(await ctx.PairingCodes.FindAsync(pairingCode));
}
}
private PairingCodeEntity CreatePairingCodeEntity(PairingCodeData data)
{
if (data == null)
return null;
return new PairingCodeEntity()
{
Id = data.Id,
Label = data.Label,
Expiration = data.Expiration,
CreatedTime = data.DateCreated,
TokenValue = data.TokenValue,
SIN = data.SIN
};
}
public async Task<bool> DeleteToken(string tokenId)
{
using (var ctx = _Factory.CreateContext())
{
var token = await ctx.PairedSINData.FindAsync(tokenId);
if (token == null)
return false;
ctx.PairedSINData.Remove(token);
await ctx.SaveChangesAsync();
return true;
}
}
public async Task<BitTokenEntity> GetToken(string tokenId)
{
using (var ctx = _Factory.CreateContext())
{
var token = await ctx.PairedSINData.FindAsync(tokenId);
if (token == null)
return null;
return CreateTokenEntity(token);
}
}
}
}

View file

@ -64,7 +64,7 @@
<PackageReference Include="TwentyTwenty.Storage" Version="2.11.2" />
<PackageReference Include="TwentyTwenty.Storage.Amazon" Version="2.11.2" />
<PackageReference Include="TwentyTwenty.Storage.Azure" Version="2.11.2" />
<PackageReference Include="TwentyTwenty.Storage.Google" Version="2.11.2" Condition="'$(TargetFramework)' == 'netcoreapp2.1'" />
<PackageReference Include="TwentyTwenty.Storage.Google" Version="2.11.2" Condition="'$(TargetFramework)' == 'netcoreapp2.1'" />
<PackageReference Include="TwentyTwenty.Storage.Local" Version="2.11.2" />
<PackageReference Include="U2F.Core" Version="1.0.4" />
<PackageReference Include="YamlDotNet" Version="5.2.1" />
@ -129,6 +129,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Authentication\" />
<Folder Include="wwwroot\vendor\clipboard.js\" />
<Folder Include="wwwroot\vendor\highlightjs\" />
<Folder Include="wwwroot\vendor\summernote" />

View file

@ -1,7 +1,7 @@
using BTCPayServer.Authentication;
using BTCPayServer.Filters;
using BTCPayServer.Filters;
using BTCPayServer.Models;
using BTCPayServer.Security;
using BTCPayServer.Security.Bitpay;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;

View file

@ -11,7 +11,6 @@ using Microsoft.Extensions.Logging;
using BTCPayServer.Models;
using BTCPayServer.Models.ManageViewModels;
using BTCPayServer.Services;
using BTCPayServer.Authentication;
using Microsoft.AspNetCore.Hosting;
using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Wallets;
@ -35,7 +34,6 @@ namespace BTCPayServer.Controllers
private readonly EmailSenderFactory _EmailSenderFactory;
private readonly ILogger _logger;
private readonly UrlEncoder _urlEncoder;
TokenRepository _TokenRepository;
IWebHostEnvironment _Env;
private readonly U2FService _u2FService;
private readonly BTCPayServerEnvironment _btcPayServerEnvironment;
@ -49,7 +47,6 @@ namespace BTCPayServer.Controllers
EmailSenderFactory emailSenderFactory,
ILogger<ManageController> logger,
UrlEncoder urlEncoder,
TokenRepository tokenRepository,
BTCPayWalletProvider walletProvider,
StoreRepository storeRepository,
IWebHostEnvironment env,
@ -61,7 +58,6 @@ namespace BTCPayServer.Controllers
_EmailSenderFactory = emailSenderFactory;
_logger = logger;
_urlEncoder = urlEncoder;
_TokenRepository = tokenRepository;
_Env = env;
_u2FService = u2FService;
_btcPayServerEnvironment = btcPayServerEnvironment;

View file

@ -11,11 +11,11 @@ using BTCPayServer.Services.Stores;
using BTCPayServer.Rating;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Authorization;
using BTCPayServer.Authentication;
using Microsoft.AspNetCore.Cors;
using System.Threading;
using BTCPayServer.Data;
using BTCPayServer.Security;
using BTCPayServer.Security.Bitpay;
namespace BTCPayServer.Controllers
{

View file

@ -1,5 +1,4 @@
using System.Threading.Tasks;
using BTCPayServer.Authentication;
using BTCPayServer.Data;
using BTCPayServer.Models;
using BTCPayServer.Security;

View file

@ -5,7 +5,6 @@ using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Authentication;
using BTCPayServer.Configuration;
using BTCPayServer.Data;
using BTCPayServer.HostedServices;
@ -17,6 +16,7 @@ using BTCPayServer.Payments.Changelly;
using BTCPayServer.Payments.Lightning;
using BTCPayServer.Rating;
using BTCPayServer.Security;
using BTCPayServer.Security.Bitpay;
using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;

View file

@ -4,7 +4,6 @@ using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using BTCPayServer.Authentication;
using BTCPayServer.Payments;
using BTCPayServer.Security;
using BTCPayServer.Services.Rates;

View file

@ -1,5 +1,4 @@
using BTCPayServer.Authentication;
using BTCPayServer.Configuration;
using BTCPayServer.Configuration;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

View file

@ -25,7 +25,6 @@ using BTCPayServer.Controllers;
using BTCPayServer.Services.Mails;
using System.Threading;
using BTCPayServer.Services.Wallets;
using BTCPayServer.Authentication;
using BTCPayServer.Logging;
using BTCPayServer.HostedServices;
using BTCPayServer.PaymentRequest;

View file

@ -7,7 +7,6 @@ using System.Text;
using System.Linq;
using System.Threading.Tasks;
using System.IO;
using BTCPayServer.Authentication;
using BTCPayServer.Logging;
using Newtonsoft.Json;
using BTCPayServer.Models;

View file

@ -24,7 +24,6 @@ using BTCPayServer.Security;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using OpenIddict.EntityFrameworkCore.Models;
using System.Net;
using BTCPayServer.Authentication;
using BTCPayServer.Security.OpenId;
using BTCPayServer.PaymentRequest;
using BTCPayServer.Services.Apps;

View file

@ -6,10 +6,10 @@ using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using BTCPayServer.Authentication;
using NBitcoin.DataEncoders;
using Microsoft.AspNetCore.Http;
using System.IO;
using BTCPayServer.Security.Bitpay;
namespace BTCPayServer.Models
{

View file

@ -7,7 +7,6 @@ using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using BTCPayServer.Authentication;
using BTCPayServer.Services;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Http;

View file

@ -9,7 +9,6 @@ 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;

View file

@ -9,7 +9,6 @@ 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

View file

@ -9,7 +9,6 @@ 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.Security.OpenId.RestAPIPolicies;
using OpenIddict.Abstractions;

View file

@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Authentication;
using Microsoft.AspNetCore.Authorization;
namespace BTCPayServer.Security

View file

@ -1,4 +1,4 @@
@model BTCPayServer.Authentication.BitTokenEntity
@model BTCPayServer.Security.Bitpay.BitTokenEntity
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData.SetActivePageAndTitle(StoreNavPages.Tokens, "Access Tokens");