Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb8d10a0b9 | ||
|
|
b19665152d | ||
|
|
561c676758 | ||
|
|
6699e73ed6 | ||
|
|
c5dfbf336e | ||
|
|
e74a996424 | ||
|
|
96c3747ad9 | ||
|
|
c7fe58200f | ||
|
|
8e3051e61b | ||
|
|
d84d4bdf7f | ||
|
|
003c261909 | ||
|
|
4dc82b81c5 | ||
|
|
d36ead30a7 | ||
|
|
4cb2a23900 | ||
|
|
b3faa84cf3 | ||
|
|
b413816797 | ||
|
|
048b59c261 | ||
|
|
7de6647e65 | ||
|
|
f4a837f36f | ||
|
|
140d2c206a | ||
|
|
50c1651cba | ||
|
|
a497084b34 | ||
|
|
051a27fd86 | ||
|
|
124982bd94 | ||
|
|
97f682115c | ||
|
|
39bd13bb25 | ||
|
|
2ef1e353e7 | ||
|
|
8334969f93 | ||
|
|
21ecf986f1 | ||
|
|
cb6fa66300 | ||
|
|
32f21e5306 | ||
|
|
5d0b771a36 | ||
|
|
435ebf8909 | ||
|
|
323f7e8481 | ||
|
|
2d7911bc95 | ||
|
|
ce7a70bb99 | ||
|
|
9b4fb02241 | ||
|
|
aa68a90224 | ||
|
|
236f416fc6 | ||
|
|
36fbd57209 | ||
|
|
28c2bac049 | ||
|
|
2c6ace2a95 | ||
|
|
a9d5a2fadb | ||
|
|
215d311113 | ||
|
|
e598cc10d2 | ||
|
|
25383e196b | ||
|
|
d5c84fa491 | ||
|
|
05b64186fb | ||
|
|
a75c275c10 | ||
|
|
1ca2119138 | ||
|
|
0138d9712b | ||
|
|
19db0dff46 | ||
|
|
f21a4ac701 | ||
|
|
0295dd1e87 | ||
|
|
1293f03182 | ||
|
|
d56356d2f3 | ||
|
|
ded2313463 | ||
|
|
e7c48e2db7 | ||
|
|
ddfc647300 | ||
|
|
5902da4212 | ||
|
|
774007e5c7 | ||
|
|
c47efa0b43 | ||
|
|
c8fee0e18f | ||
|
|
c3b73c7e4d | ||
|
|
0e7a7a5d70 | ||
|
|
08a3329eff | ||
|
|
5b29c62fc6 | ||
|
|
8ad099bcce | ||
|
|
2fba3da43e | ||
|
|
9649294bbf | ||
|
|
c555df3054 | ||
|
|
78ca88b030 | ||
|
|
7ffaa89f55 |
@ -80,12 +80,26 @@ jobs:
|
||||
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 --os linux --arch arm --variant v7
|
||||
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 --os linux --arch arm64 --variant v8
|
||||
sudo docker manifest push $DOCKERHUB_REPO:$LATEST_TAG -p
|
||||
|
||||
trigger_docs_build:
|
||||
machine:
|
||||
enabled: true
|
||||
image: circleci/classic:201808-01
|
||||
steps:
|
||||
- run:
|
||||
command: |
|
||||
curl -X POST -H "Authorization: token $GH_PAT" -H "Accept: application/vnd.github.everest-preview+json" -H "Content-Type: application/json" https://api.github.com/repos/btcpayserver/btcpayserver-doc/dispatches --data '{"event_type": "build_docs"}'
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
publish:
|
||||
jobs:
|
||||
- trigger_docs_build:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
# only act on version tags
|
||||
tags:
|
||||
only: /v[0-9]+(\.[0-9]+)*/
|
||||
- publish_docker_linuxamd64:
|
||||
filters:
|
||||
# ignore any commit on any branch by default
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
@inject BTCTransmuter.Extension.Tor.Services.TorServices TorServices
|
||||
@if (TorServices.TransmuterTorService != null)
|
||||
{
|
||||
<li class="nav-item mr-3">
|
||||
<a href="@TorServices.TransmuterTorService.OnionHost">Onion</a>
|
||||
<li class="nav-item">
|
||||
<a href="@TorServices.TransmuterTorService.OnionHost" class="nav-link">Onion</a>
|
||||
</li>
|
||||
}
|
||||
|
||||
@ -7,12 +7,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.1.0" />
|
||||
<PackageReference Include="McMaster.NETCore.Plugins.Mvc" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.1" />
|
||||
<PackageReference Include="NetCore.AutoRegisterDi" Version="1.1.0" />
|
||||
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.3.0" />
|
||||
<PackageReference Include="McMaster.NETCore.Plugins.Mvc" Version="1.3.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.4" />
|
||||
<PackageReference Include="NetCore.AutoRegisterDi" Version="2.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.20" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.1.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BtcTransmuter.Data\BtcTransmuter.Data.csproj" />
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
using System;
|
||||
|
||||
namespace BtcTransmuter
|
||||
{
|
||||
public interface IBtcTransmuterOptions
|
||||
@ -7,5 +9,7 @@ namespace BtcTransmuter
|
||||
string DataProtectionDir { get; set; }
|
||||
DatabaseType DatabaseType { get; set; }
|
||||
bool UseDatabaseColumnEncryption { get; set; }
|
||||
bool DisableInternalAuth { get; set; }
|
||||
Uri BTCPayAuthServer { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BtcTransmuter.Abstractions.Extensions
|
||||
{
|
||||
public static class ControllerExtensions
|
||||
{
|
||||
|
||||
public static bool IsApi(this Controller controller)
|
||||
{
|
||||
return controller.HttpContext.IsApi();
|
||||
}
|
||||
|
||||
public static bool IsApi(this HttpContext context)
|
||||
{
|
||||
if (context.Items.TryGetValue("API", out var val))
|
||||
{
|
||||
return val is true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void SetIsApi(this HttpContext context, bool val)
|
||||
{
|
||||
context.Items.TryAdd("API", val);
|
||||
}
|
||||
public static IActionResult ViewOrJson<T>(this Controller controller, string viewName, T payload)
|
||||
{
|
||||
if (controller.IsApi())
|
||||
{
|
||||
return controller.Json(payload);
|
||||
}
|
||||
|
||||
return controller.View(viewName, payload);
|
||||
}
|
||||
public static IActionResult ViewOrJson<T>(this Controller controller, T payload)
|
||||
{
|
||||
if (controller.IsApi())
|
||||
{
|
||||
return controller.Json(payload);
|
||||
}
|
||||
|
||||
return controller.View(payload);
|
||||
}
|
||||
public static IActionResult ViewOrBadRequest<T>(this Controller controller, T payload, bool usePayloadInBadRequest = false)
|
||||
{
|
||||
if (controller.IsApi())
|
||||
{
|
||||
return usePayloadInBadRequest ? controller.BadRequest(payload) : controller.BadRequest(controller.ModelState);
|
||||
}
|
||||
|
||||
return controller.View(payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,16 +10,5 @@ namespace BtcTransmuter.Abstractions.Extensions
|
||||
{
|
||||
return new HashSet<T>(source, comparer);
|
||||
}
|
||||
|
||||
public static void AddOrReplace<TKey, TValue>(
|
||||
this IDictionary<TKey, TValue> dico,
|
||||
TKey key,
|
||||
TValue value)
|
||||
{
|
||||
if (dico.ContainsKey(key))
|
||||
dico[key] = value;
|
||||
else
|
||||
dico.Add(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
BtcTransmuter.Abstractions/Extensions/RequestExtensions.cs
Normal file
16
BtcTransmuter.Abstractions/Extensions/RequestExtensions.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace BtcTransmuter.Abstractions.Extensions
|
||||
{
|
||||
public static class RequestExtensions
|
||||
{
|
||||
public static string GetAbsoluteRoot(this HttpRequest request)
|
||||
{
|
||||
return string.Concat(
|
||||
request.Scheme,
|
||||
"://",
|
||||
request.Host.ToUriComponent(),
|
||||
request.PathBase.ToUriComponent());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -35,7 +35,7 @@ namespace BtcTransmuter.Abstractions.ExternalServices
|
||||
{
|
||||
return result.Error;
|
||||
}
|
||||
|
||||
|
||||
return View(await BuildViewModel(result.Data));
|
||||
}
|
||||
|
||||
|
||||
@ -62,7 +62,7 @@ namespace BtcTransmuter.Abstractions.Helpers
|
||||
|
||||
return (e.Compile().DynamicInvoke(data.Values.ToArray()) ?? "").ToString();
|
||||
}
|
||||
catch (Exception exception)
|
||||
catch (Exception)
|
||||
{
|
||||
return processed;
|
||||
}
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using BtcTransmuter.Data.Entities;
|
||||
using BtcTransmuter.Data.Entities.U2F;
|
||||
|
||||
namespace BtcTransmuter.Abstractions.U2F
|
||||
{
|
||||
public interface IU2FService
|
||||
{
|
||||
Task<List<U2FDevice>> GetDevices(string userId);
|
||||
Task RemoveDevice(string id, string userId);
|
||||
Task<bool> HasDevices(string userId);
|
||||
ServerRegisterResponse StartDeviceRegistration(string userId, string appId);
|
||||
Task<bool> CompleteRegistration(string userId, string deviceResponse, string name);
|
||||
Task<bool> AuthenticateUser(string userId, string deviceResponse);
|
||||
Task<List<ServerChallenge>> GenerateDeviceChallenges(string userId, string appId);
|
||||
}
|
||||
}
|
||||
@ -8,11 +8,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.4" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
namespace BtcTransmuter.Data.Entities.U2F
|
||||
{
|
||||
public class ServerChallenge
|
||||
{
|
||||
public string challenge { get; set; }
|
||||
public string version { get; set; }
|
||||
public string appId { get; set; }
|
||||
public string keyHandle { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
namespace BtcTransmuter.Data.Entities.U2F
|
||||
{
|
||||
public class ServerRegisterResponse
|
||||
{
|
||||
public string AppId { get; set; }
|
||||
public string Challenge { get; set; }
|
||||
public string Version { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace BtcTransmuter.Data.Entities.U2F
|
||||
{
|
||||
public class U2FDeviceAuthenticationRequest
|
||||
{
|
||||
public string KeyHandle { get; set; }
|
||||
|
||||
[Required] public string Challenge { get; set; }
|
||||
|
||||
[Required] [StringLength(200)] public string AppId { get; set; }
|
||||
|
||||
[Required] [StringLength(50)] public string Version { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace BtcTransmuter.Data.Entities
|
||||
{
|
||||
public class U2FDevice
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required] public byte[] KeyHandle { get; set; }
|
||||
|
||||
[Required] public byte[] PublicKey { get; set; }
|
||||
|
||||
[Required] public byte[] AttestationCert { get; set; }
|
||||
|
||||
[Required] public int Counter { get; set; }
|
||||
|
||||
public string UserId { get; set; }
|
||||
public User User { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using BtcTransmuter.Data.Encryption;
|
||||
using BtcTransmuter.Data.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace BtcTransmuter.Data.Entities
|
||||
{
|
||||
public class User : IdentityUser
|
||||
public class User : IdentityUser, IHasJsonData
|
||||
{
|
||||
public List<Recipe> Recipes { get; set; }
|
||||
public List<U2FDevice> U2FDevices { get; set; }
|
||||
|
||||
[Encrypted] public string DataJson { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public class UserBlob
|
||||
{
|
||||
public bool BasicAuth { get; set; }
|
||||
public BTCPayAuthDetails BTCPayAuthDetails { get; set; } = new BTCPayAuthDetails();
|
||||
}
|
||||
|
||||
public class BTCPayAuthDetails
|
||||
{
|
||||
public string UserId { get; set; }
|
||||
public string AccessToken { get; set; }
|
||||
}
|
||||
}
|
||||
@ -9,9 +9,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="Moq" Version="4.13.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="Moq" Version="4.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -4,13 +4,13 @@
|
||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BtcTransmuter.Abstractions\BtcTransmuter.Abstractions.csproj"/>
|
||||
<ProjectReference Include="..\BtcTransmuter.Extension.DynamicServices\BtcTransmuter.Extension.DynamicServices.csproj"/>
|
||||
<ProjectReference Include="..\BtcTransmuter.Abstractions\BtcTransmuter.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\BtcTransmuter.Extension.DynamicServices\BtcTransmuter.Extension.DynamicServices.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.1"/>
|
||||
<PackageReference Include="NBitcoin" Version="5.0.13"/>
|
||||
<PackageReference Include="NBitpayClient" Version="1.0.0.37"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.4" />
|
||||
<PackageReference Include="NBitcoin" Version="5.0.40" />
|
||||
<PackageReference Include="NBitpayClient" Version="1.0.0.38" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@ -52,7 +52,7 @@ namespace BtcTransmuter.Extension.BtcPayServer.ExternalServices.BtcPayServer
|
||||
var client = ConstructClient();
|
||||
return client != null && await client.TestAccessAsync(Facade.Merchant);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -9,9 +9,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="Moq" Version="4.13.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="Moq" Version="4.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BtcTransmuter.Abstractions\BtcTransmuter.Abstractions.csproj"/>
|
||||
<ProjectReference Include="..\BtcTransmuter.Abstractions\BtcTransmuter.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.1"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@ -9,9 +9,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="Moq" Version="4.13.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="Moq" Version="4.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -5,11 +5,11 @@
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BtcTransmuter.Abstractions\BtcTransmuter.Abstractions.csproj"/>
|
||||
<ProjectReference Include="..\BtcTransmuter.Extension.DynamicServices\BtcTransmuter.Extension.DynamicServices.csproj"/>
|
||||
<ProjectReference Include="..\BtcTransmuter.Abstractions\BtcTransmuter.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\BtcTransmuter.Extension.DynamicServices\BtcTransmuter.Extension.DynamicServices.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MailKit" Version="2.4.1"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.1"/>
|
||||
<PackageReference Include="MailKit" Version="2.6.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@ -51,7 +51,6 @@ namespace BtcTransmuter.Extension.Email.ExternalServices.Smtp
|
||||
var error = await SendTestEmail(smtpService, viewModel.TestEmail);
|
||||
if (string.IsNullOrEmpty(error))
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.TestEmail), "Email sent successfully, confirm that you received it");
|
||||
viewModel.TestEmail = string.Empty;
|
||||
}
|
||||
else
|
||||
@ -99,4 +98,4 @@ namespace BtcTransmuter.Extension.Email.ExternalServices.Smtp
|
||||
[Display(Name = "Send test email from and to this address to check if your settings are valid")]
|
||||
public string TestEmail { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,9 +9,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="Moq" Version="4.13.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="Moq" Version="4.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -2,6 +2,7 @@ using System;
|
||||
using BtcTransmuter.Data.Entities;
|
||||
using BtcTransmuter.Data.Models;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BtcTransmuter.Extension.Exchange.ExternalServices.Exchange;
|
||||
using BtcTransmuter.Tests.Base;
|
||||
using Xunit;
|
||||
@ -12,9 +13,9 @@ namespace BtcTransmuter.Extension.Exchange.Tests
|
||||
public class ExchangeServiceTests:BaseExternalServiceTest<ExchangeService,ExchangeExternalServiceData >
|
||||
{
|
||||
[Fact]
|
||||
public void ExchangeService_GetAvailableExchanges()
|
||||
public async Task ExchangeService_GetAvailableExchanges()
|
||||
{
|
||||
Assert.True(ExchangeService.GetAvailableExchanges().Any());
|
||||
Assert.True((await ExchangeService.GetAvailableExchanges()).Any());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -49,7 +50,7 @@ namespace BtcTransmuter.Extension.Exchange.Tests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExchangeService_CanConstructClient()
|
||||
public async Task ExchangeService_CanConstructClient()
|
||||
{
|
||||
var InvalidData = new ExchangeExternalServiceData()
|
||||
{
|
||||
@ -64,7 +65,7 @@ namespace BtcTransmuter.Extension.Exchange.Tests
|
||||
externalServiceData.Set(InvalidData);
|
||||
|
||||
var exchangeService = GetExternalService(externalServiceData);
|
||||
Assert.ThrowsAny<Exception>(() => exchangeService.ConstructClient());
|
||||
await Assert.ThrowsAnyAsync<Exception>(async () => await exchangeService.ConstructClient());
|
||||
|
||||
|
||||
var validData = new ExchangeExternalServiceData()
|
||||
|
||||
@ -36,7 +36,7 @@ namespace BtcTransmuter.Extension.Exchange.Actions.GetExchangeBalance
|
||||
var serviceData =
|
||||
await _externalServiceManager.GetExternalServiceData(externalServiceId, GetUserId());
|
||||
var exchangeService = new ExchangeService(serviceData);
|
||||
var symbols = await exchangeService.ConstructClient().GetCurrenciesAsync();
|
||||
var symbols = await (await exchangeService.ConstructClient()).GetCurrenciesAsync();
|
||||
|
||||
return symbols.Keys.ToArray();
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ namespace BtcTransmuter.Extension.Exchange.Actions.GetExchangeBalance
|
||||
|
||||
var externalService = await recipeAction.GetExternalService();
|
||||
var exchangeService = new ExchangeService(externalService);
|
||||
var client = exchangeService.ConstructClient();
|
||||
var client = await exchangeService.ConstructClient();
|
||||
|
||||
var result = await client.GetAmountsAsync();
|
||||
|
||||
|
||||
@ -55,7 +55,7 @@ namespace BtcTransmuter.Extension.Exchange.Actions.GetExchangeRate
|
||||
var serviceData =
|
||||
await _externalServiceManager.GetExternalServiceData(viewModel.ExternalServiceId, GetUserId());
|
||||
var exchangeService = new ExchangeService(serviceData);
|
||||
var symbols = (await exchangeService.ConstructClient().GetMarketSymbolsAsync()).ToArray();
|
||||
var symbols = (await (await exchangeService.ConstructClient()).GetMarketSymbolsAsync()).ToArray();
|
||||
if (symbols.Contains(viewModel.MarketSymbol))
|
||||
{
|
||||
mainModel.ExternalServiceId = viewModel.ExternalServiceId;
|
||||
|
||||
@ -26,7 +26,7 @@ namespace BtcTransmuter.Extension.Exchange.Actions.GetExchangeRate
|
||||
{
|
||||
var externalService = await recipeAction.GetExternalService();
|
||||
var exchangeService = new ExchangeService(externalService);
|
||||
var client = exchangeService.ConstructClient();
|
||||
var client = await exchangeService.ConstructClient();
|
||||
|
||||
var result = await client.GetTickerAsync(actionData.MarketSymbol);
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ namespace BtcTransmuter.Extension.Exchange.Actions.PlaceOrder
|
||||
var serviceData =
|
||||
await _externalServiceManager.GetExternalServiceData(externalServiceId, GetUserId());
|
||||
var exchangeService = new ExchangeService(serviceData);
|
||||
return (await exchangeService.ConstructClient().GetMarketSymbolsAsync()).ToArray();
|
||||
return (await (await exchangeService.ConstructClient()).GetMarketSymbolsAsync()).ToArray();
|
||||
}
|
||||
|
||||
protected override async Task<PlaceOrderViewModel> BuildViewModel(RecipeAction from)
|
||||
@ -79,7 +79,7 @@ namespace BtcTransmuter.Extension.Exchange.Actions.PlaceOrder
|
||||
var serviceData =
|
||||
await _externalServiceManager.GetExternalServiceData(viewModel.ExternalServiceId, GetUserId());
|
||||
var exchangeService = new ExchangeService(serviceData);
|
||||
var symbols = (await exchangeService.ConstructClient().GetMarketSymbolsAsync()).ToArray();
|
||||
var symbols = (await (await exchangeService.ConstructClient()).GetMarketSymbolsAsync()).ToArray();
|
||||
if (symbols.Contains(viewModel.MarketSymbol))
|
||||
{
|
||||
mainModel.ExternalServiceId = viewModel.ExternalServiceId;
|
||||
|
||||
@ -26,7 +26,7 @@ namespace BtcTransmuter.Extension.Exchange.Actions.PlaceOrder
|
||||
{
|
||||
var externalService = await recipeAction.GetExternalService();
|
||||
var exchangeService = new ExchangeService(externalService);
|
||||
var client = exchangeService.ConstructClient();
|
||||
var client = await exchangeService.ConstructClient();
|
||||
var orderRequest = new ExchangeOrderRequest()
|
||||
{
|
||||
MarketSymbol = actionData.MarketSymbol,
|
||||
@ -43,7 +43,7 @@ namespace BtcTransmuter.Extension.Exchange.Actions.PlaceOrder
|
||||
{
|
||||
var result = await client.PlaceOrderAsync(orderRequest);
|
||||
System.Threading.Thread.Sleep(500);
|
||||
result = await client.GetOrderDetailsAsync(result.OrderId);
|
||||
result = await client.GetOrderDetailsAsync(result.OrderId, orderRequest.MarketSymbol);
|
||||
return new TypedActionHandlerResult<ExchangeOrderResult>()
|
||||
{
|
||||
Executed = true,
|
||||
@ -63,4 +63,4 @@ namespace BtcTransmuter.Extension.Exchange.Actions.PlaceOrder
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,9 +4,6 @@
|
||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="0.6.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BtcTransmuter.Abstractions\BtcTransmuter.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\BtcTransmuter.Extension.DynamicServices\BtcTransmuter.Extension.DynamicServices.csproj" />
|
||||
@ -14,4 +11,7 @@
|
||||
<ItemGroup>
|
||||
<UpToDateCheckInput Remove="Views\SendEmail\EditData.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Custom.DigitalRuby.ExchangeSharp" Version="0.9.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@ -25,10 +25,10 @@ namespace BtcTransmuter.Extension.Exchange.ExternalServices.Exchange
|
||||
|
||||
protected override string ExternalServiceType => ExchangeService.ExchangeServiceType;
|
||||
|
||||
protected override Task<EditExchangeExternalServiceDataViewModel> BuildViewModel(ExternalServiceData data)
|
||||
protected override async Task<EditExchangeExternalServiceDataViewModel> BuildViewModel(ExternalServiceData data)
|
||||
{
|
||||
return Task.FromResult(new EditExchangeExternalServiceDataViewModel(new ExchangeService(data).GetData(),
|
||||
ExchangeService.GetAvailableExchanges()));
|
||||
return new EditExchangeExternalServiceDataViewModel(new ExchangeService(data).GetData(),
|
||||
await ExchangeService.GetAvailableExchanges());
|
||||
}
|
||||
|
||||
protected override async
|
||||
@ -41,7 +41,7 @@ namespace BtcTransmuter.Extension.Exchange.ExternalServices.Exchange
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return (null,
|
||||
new EditExchangeExternalServiceDataViewModel(viewModel, ExchangeService.GetAvailableExchanges()));
|
||||
new EditExchangeExternalServiceDataViewModel(viewModel, await ExchangeService.GetAvailableExchanges()));
|
||||
}
|
||||
|
||||
//current External Service data
|
||||
@ -54,7 +54,7 @@ namespace BtcTransmuter.Extension.Exchange.ExternalServices.Exchange
|
||||
"Could not connect with current settings. Transmuter tests against fetching your balance amount from the exchange so you would need to enable that option if available");
|
||||
|
||||
return (null,
|
||||
new EditExchangeExternalServiceDataViewModel(viewModel, ExchangeService.GetAvailableExchanges()));
|
||||
new EditExchangeExternalServiceDataViewModel(viewModel, await ExchangeService.GetAvailableExchanges()));
|
||||
}
|
||||
|
||||
return (externalServiceData, null);
|
||||
|
||||
@ -25,17 +25,17 @@ namespace BtcTransmuter.Extension.Exchange.ExternalServices.Exchange
|
||||
{
|
||||
}
|
||||
|
||||
public static IExchangeAPI[] GetAvailableExchanges()
|
||||
public static async Task<IExchangeAPI[]> GetAvailableExchanges()
|
||||
{
|
||||
return ExchangeAPI.GetExchangeAPIs();
|
||||
return await ExchangeAPI.GetExchangeAPIsAsync();
|
||||
}
|
||||
|
||||
|
||||
public ExchangeAPI ConstructClient()
|
||||
public async Task<ExchangeAPI> ConstructClient()
|
||||
{
|
||||
var data = GetData();
|
||||
|
||||
var result = ExchangeAPI.GetExchangeAPI(data.ExchangeName);
|
||||
var result = await ExchangeAPI.GetExchangeAPIAsync(data.ExchangeName);
|
||||
if (result is ExchangeAPI api)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(data.OverrideUrl))
|
||||
@ -52,7 +52,7 @@ namespace BtcTransmuter.Extension.Exchange.ExternalServices.Exchange
|
||||
|
||||
public async Task<bool> TestAccess()
|
||||
{
|
||||
var client = ConstructClient();
|
||||
var client = await ConstructClient();
|
||||
if (client == null)
|
||||
{
|
||||
return false;
|
||||
|
||||
@ -52,7 +52,7 @@ namespace BtcTransmuter.Extension.NBXplorer.HostedServices
|
||||
await Task.WhenAll(exchangeExternalServices.Select(async data =>
|
||||
{
|
||||
var exchangeService = new ExchangeService(data);
|
||||
var client = exchangeService.ConstructClient();
|
||||
var client = await exchangeService.ConstructClient();
|
||||
var amounts = await client.GetAmountsAsync();
|
||||
foreach (var keyValuePair in amounts)
|
||||
{
|
||||
|
||||
@ -37,7 +37,7 @@ namespace BtcTransmuter.Extension.Exchange.Triggers.CheckExchangeBalance
|
||||
var serviceData =
|
||||
await _externalServiceManager.GetExternalServiceData(externalServiceId, GetUserId());
|
||||
var exchangeService = new ExchangeService(serviceData);
|
||||
var symbols = await exchangeService.ConstructClient().GetCurrenciesAsync();
|
||||
var symbols = await (await exchangeService.ConstructClient()).GetCurrenciesAsync();
|
||||
|
||||
return symbols.Keys.ToArray();
|
||||
}
|
||||
|
||||
@ -10,9 +10,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MaxKagamine.Moq.HttpClient" Version="1.1.0" />
|
||||
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="Moq" Version="4.13.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="Moq" Version="4.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.1.8" />
|
||||
<PackageReference Include="NBitcoin" Version="5.0.13" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.1.15" />
|
||||
<PackageReference Include="NBitcoin" Version="5.0.40" />
|
||||
<ProjectReference Include="..\BtcTransmuter.Abstractions\BtcTransmuter.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\BtcTransmuter.Extension.DynamicServices\BtcTransmuter.Extension.DynamicServices.csproj" />
|
||||
<ProjectReference Include="..\BtcTransmuter.Extension.NBXplorer\BtcTransmuter.Extension.NBXplorer.csproj" />
|
||||
|
||||
@ -11,9 +11,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MaxKagamine.Moq.HttpClient" Version="1.1.0" />
|
||||
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="Moq" Version="4.13.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="Moq" Version="4.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
<ProjectReference Include="..\BtcTransmuter.Extension.DynamicServices\BtcTransmuter.Extension.DynamicServices.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NBXplorer.Client" Version="3.0.2" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="3.0.15" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<UpToDateCheckInput Remove="Views\NBXplorerCreatePSBT\EditData.cshtml" />
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BtcTransmuter.Extension.NBXplorer.Models;
|
||||
@ -34,28 +33,30 @@ namespace BtcTransmuter.Extension.NBXplorer.Controllers
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("{cryptoCode}/{mnemonic?}")]
|
||||
public IActionResult GetWallet(string cryptoCode, string mnemonic)
|
||||
[HttpPost("{cryptoCode}")]
|
||||
public IActionResult GetWallet(string cryptoCode, [FromForm] string mnemonic)
|
||||
{
|
||||
if (string.IsNullOrEmpty(mnemonic))
|
||||
{
|
||||
return RedirectToAction("GetWallet", new
|
||||
{
|
||||
cryptoCode = cryptoCode,
|
||||
mnemonic = new Mnemonic(Wordlist.English).ToString()
|
||||
});
|
||||
}
|
||||
return ActionResult(cryptoCode, string.IsNullOrEmpty(mnemonic) ? null : new Mnemonic(mnemonic));
|
||||
}
|
||||
|
||||
[HttpGet("{cryptoCode}")]
|
||||
public IActionResult GetWallet(string cryptoCode)
|
||||
{
|
||||
return ActionResult(cryptoCode);
|
||||
}
|
||||
|
||||
private IActionResult ActionResult(string cryptoCode, Mnemonic mnemonic = null)
|
||||
{
|
||||
var network = _nbXplorerClientProvider.GetClient(cryptoCode).Network;
|
||||
|
||||
var addressTypes = new Dictionary<ScriptPubKeyType, GetWalletViewModel.GetWalletViewModelAddressType>();
|
||||
var mnemonicSeed = new Mnemonic(mnemonic);
|
||||
var mnemonicSeed = mnemonic ?? new Mnemonic(Wordlist.English);
|
||||
var extKey = mnemonicSeed.DeriveExtKey();
|
||||
var wif = extKey.GetWif(network.NBitcoinNetwork);
|
||||
var privateKey = extKey.PrivateKey;
|
||||
var secret = privateKey.GetBitcoinSecret(network.NBitcoinNetwork);
|
||||
|
||||
|
||||
|
||||
if (network.NBitcoinNetwork.Consensus.SupportSegwit)
|
||||
{
|
||||
var segwitExtPubkey = extKey
|
||||
@ -70,24 +71,26 @@ namespace BtcTransmuter.Extension.NBXplorer.Controllers
|
||||
var segwit = _derivationSchemeParser.Parse(network.DerivationStrategyFactory, $"{segwitExtPubkey}");
|
||||
var p2sh = _derivationSchemeParser.Parse(network.DerivationStrategyFactory, $"{p2shExtPubKey}-[p2sh]");
|
||||
|
||||
|
||||
|
||||
addressTypes.Add(ScriptPubKeyType.Segwit, new GetWalletViewModel.GetWalletViewModelAddressType()
|
||||
{
|
||||
Description = "BTCPay / BTCTransmuter compatible xpub for segwit addresses",
|
||||
DerivationScheme = segwit.ToString(),
|
||||
Addresses = GenerateAddresses(segwit, network),
|
||||
RootKeyPath = NBXplorerPublicWallet.GetDerivationKeyPath(ScriptPubKeyType.Segwit, 0, network).ToString()
|
||||
RootKeyPath ="m/" + NBXplorerPublicWallet.GetDerivationKeyPath(ScriptPubKeyType.Segwit, 0, network)
|
||||
.ToString()
|
||||
});
|
||||
|
||||
|
||||
addressTypes.Add(ScriptPubKeyType.SegwitP2SH, new GetWalletViewModel.GetWalletViewModelAddressType()
|
||||
{
|
||||
Description = "BTCPay / BTCTransmuter compatible xpub for p2sh addresses",
|
||||
DerivationScheme = p2sh.ToString(),
|
||||
Addresses = GenerateAddresses(p2sh, network),
|
||||
RootKeyPath = NBXplorerPublicWallet.GetDerivationKeyPath(ScriptPubKeyType.SegwitP2SH, 0, network).ToString()
|
||||
RootKeyPath = "m/" + NBXplorerPublicWallet.GetDerivationKeyPath(ScriptPubKeyType.SegwitP2SH, 0, network)
|
||||
.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var legacyExtPubkey = extKey
|
||||
.Derive(NBXplorerPublicWallet.GetDerivationKeyPath(ScriptPubKeyType.Legacy, 0, network)).Neuter()
|
||||
.ToString(network.NBitcoinNetwork);
|
||||
@ -99,18 +102,19 @@ namespace BtcTransmuter.Extension.NBXplorer.Controllers
|
||||
Description = "BTCPay / BTCTransmuter compatible xpub for legacy addresses",
|
||||
DerivationScheme = legacy.ToString(),
|
||||
Addresses = GenerateAddresses(legacy, network),
|
||||
RootKeyPath = NBXplorerPublicWallet.GetDerivationKeyPath(ScriptPubKeyType.Legacy, 0, network).ToString()
|
||||
RootKeyPath = "m/" +NBXplorerPublicWallet.GetDerivationKeyPath(ScriptPubKeyType.Legacy, 0, network).ToString()
|
||||
});
|
||||
|
||||
return View(new GetWalletViewModel()
|
||||
{
|
||||
Mnemonic = mnemonic,
|
||||
Mnemonic = mnemonicSeed.ToString(),
|
||||
Network = network,
|
||||
CryptoCode = cryptoCode,
|
||||
CryptoCodes = _nbXplorerOptions.Cryptos,
|
||||
PrivateKey = privateKey,
|
||||
WIF = wif,
|
||||
ExtPubKey = extKey.Neuter().ToString(network.NBitcoinNetwork),
|
||||
Fingerprint = extKey.Neuter().PubKey.GetHDFingerPrint().ToString(),
|
||||
AddressTypes = addressTypes,
|
||||
Address = secret.GetAddress(ScriptPubKeyType.Legacy),
|
||||
SegwitAddress = network.NBitcoinNetwork.Consensus.SupportSegwit
|
||||
@ -118,7 +122,8 @@ namespace BtcTransmuter.Extension.NBXplorer.Controllers
|
||||
: null,
|
||||
P2SHAddress = network.NBitcoinNetwork.Consensus.SupportSegwit
|
||||
? secret.GetAddress(ScriptPubKeyType.SegwitP2SH)
|
||||
: null
|
||||
: null,
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -147,8 +152,8 @@ namespace BtcTransmuter.Extension.NBXplorer.Controllers
|
||||
public Key PrivateKey { get; set; }
|
||||
public BitcoinExtKey WIF { get; set; }
|
||||
public string ExtPubKey { get; set; }
|
||||
|
||||
public Dictionary<ScriptPubKeyType,GetWalletViewModelAddressType> AddressTypes { get; set; }
|
||||
public string Fingerprint { get; set; }
|
||||
public Dictionary<ScriptPubKeyType, GetWalletViewModelAddressType> AddressTypes { get; set; }
|
||||
public BitcoinAddress SegwitAddress { get; set; }
|
||||
public BitcoinAddress Address { get; set; }
|
||||
public BitcoinAddress P2SHAddress { get; set; }
|
||||
@ -158,7 +163,7 @@ namespace BtcTransmuter.Extension.NBXplorer.Controllers
|
||||
public string Description { get; set; }
|
||||
public string DerivationScheme { get; set; }
|
||||
public string RootKeyPath { get; set; }
|
||||
public Dictionary<string , string> Addresses { get; set; }
|
||||
public Dictionary<string, string> Addresses { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ namespace BtcTransmuter.Extension.NBXplorer.Services
|
||||
return txBuilder.BuildTransaction(true);
|
||||
}
|
||||
|
||||
public async Task<TransactionBuilder> AddTxOutsToTransaction(TransactionBuilder transactionBuilder,
|
||||
private Task<TransactionBuilder> AddTxOutsToTransaction(TransactionBuilder transactionBuilder,
|
||||
IEnumerable<(Money amount, IDestination destination, bool subtractFee)> outgoing)
|
||||
{
|
||||
foreach (var tuple in outgoing)
|
||||
@ -119,7 +119,7 @@ namespace BtcTransmuter.Extension.NBXplorer.Services
|
||||
break;
|
||||
}
|
||||
|
||||
return transactionBuilder;
|
||||
return Task.FromResult(transactionBuilder);
|
||||
}
|
||||
|
||||
public static ExtKey GetKeyFromDetails(PrivateKeyDetails privateKeyDetails, Network network)
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
{
|
||||
if (User.IsInRole("Admin"))
|
||||
{
|
||||
<li class="nav-item mr-3">
|
||||
<a asp-controller="NBXplorerStatus" asp-action="GetSummaries">NBXplorer Status</a>
|
||||
<li class="nav-item">
|
||||
<a asp-controller="NBXplorerStatus" asp-action="GetSummaries" class="nav-link">NBXplorer Status</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,12 @@
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label class="control-label">Mnemonic Seed</label>
|
||||
<input readonly class="form-control" value="@Model.Mnemonic"/>
|
||||
<form class="input-group" method="post" asp-action="GetWallet" asp-route-cryptoCode="@Model.CryptoCode">
|
||||
<input asp-for="Mnemonic" class="form-control"/>
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-secondary">Load</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">Master Private Key</label>
|
||||
@ -34,13 +39,17 @@
|
||||
<label class="control-label">Master Extended Public key</label>
|
||||
<input readonly class="form-control" value="@Model.ExtPubKey"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">Root Fingerprint</label>
|
||||
<input readonly class="form-control" value="@Model.Fingerprint"/>
|
||||
</div>
|
||||
<div class="card">
|
||||
<nav>
|
||||
<div class="nav nav-tabs nav-fill" id="nav-tab" role="tablist">
|
||||
@for (int i = 0; i < Model.AddressTypes.Count; i++)
|
||||
{
|
||||
var item = Model.AddressTypes.ElementAt(i);
|
||||
<a class="nav-item nav-link @(i == 0? "active": "")" href="#tab-hd-@item.Key" id="nav-hd-@item.Key" data-toggle="tab" h role="tab">@item.Key</a>
|
||||
@for (int i = 0; i < Model.AddressTypes.Count; i++)
|
||||
{
|
||||
var item = Model.AddressTypes.ElementAt(i);
|
||||
<a class="nav-item nav-link @(i == 0 ? "active" : "")" href="#tab-hd-@item.Key" id="nav-hd-@item.Key" data-toggle="tab" h role="tab">@item.Key</a>
|
||||
}
|
||||
|
||||
<a class="nav-item nav-link" href="#tab-singular" id="nav-singular" data-toggle="tab" role="tab">Singular Addresses</a>
|
||||
@ -50,11 +59,15 @@
|
||||
@for (int i = 0; i < Model.AddressTypes.Count; i++)
|
||||
{
|
||||
var item = Model.AddressTypes.ElementAt(i);
|
||||
<div class="tab-pane p-2 @(i == 0? "show active": "")" id="tab-hd-@item.Key" role="tabpanel" id="tab-hd-@item.Key">
|
||||
<div class="tab-pane p-2 @(i == 0 ? "show active" : "")" id="tab-hd-@item.Key" role="tabpanel" id="tab-hd-@item.Key">
|
||||
<div class="form-group">
|
||||
<label class="control-label">@item.Value.Description</label>
|
||||
<input readonly class="form-control" value="@item.Value.DerivationScheme"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">Account Key Path</label>
|
||||
<input readonly class="form-control" value="@item.Value.RootKeyPath"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<table class="table table-sm table-responsive-md">
|
||||
<thead>
|
||||
@ -67,14 +80,15 @@
|
||||
@foreach (var sample in item.Value.Addresses)
|
||||
{
|
||||
<tr>
|
||||
<td><span class="text-muted ">@item.Value.RootKeyPath/</span>@sample.Key</td>
|
||||
<td>
|
||||
<span class="text-muted ">@item.Value.RootKeyPath/</span>@sample.Key</td>
|
||||
<td>@sample.Value</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="tab-pane p-2" id="tab-singular" role="tabpanel">
|
||||
|
||||
@ -11,9 +11,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MaxKagamine.Moq.HttpClient" Version="1.1.0" />
|
||||
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="Moq" Version="4.13.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="Moq" Version="4.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -24,14 +24,14 @@ namespace BtcTransmuter.Extension.Operators.Actions.ChooseNumericValue
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task<ChooseNumericValueViewModel> BuildViewModel(RecipeAction from)
|
||||
protected override Task<ChooseNumericValueViewModel> BuildViewModel(RecipeAction from)
|
||||
{
|
||||
var fromData = from.Get<ChooseNumericValueData>();
|
||||
return new ChooseNumericValueViewModel
|
||||
return Task.FromResult(new ChooseNumericValueViewModel
|
||||
{
|
||||
RecipeId = @from.RecipeId,
|
||||
Items = fromData.Items
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
protected override async Task<(RecipeAction ToSave, ChooseNumericValueViewModel showViewModel)> BuildModel(
|
||||
|
||||
@ -18,7 +18,7 @@ namespace BtcTransmuter.Extension.Operators.Actions.ChooseNumericValue
|
||||
public override string ControllerName => "ChooseNumericValue";
|
||||
|
||||
|
||||
protected override async Task<TypedActionHandlerResult<string>> Execute(Dictionary<string, object> data, RecipeAction recipeAction,
|
||||
protected override Task<TypedActionHandlerResult<string>> Execute(Dictionary<string, object> data, RecipeAction recipeAction,
|
||||
ChooseNumericValueData actionData)
|
||||
{
|
||||
ChooseNumericValueData.ChooseNumericValueDataItem selectedItem = null;
|
||||
@ -53,12 +53,12 @@ namespace BtcTransmuter.Extension.Operators.Actions.ChooseNumericValue
|
||||
}
|
||||
|
||||
});
|
||||
return new TypedActionHandlerResult<string>()
|
||||
return Task.FromResult(new TypedActionHandlerResult<string>()
|
||||
{
|
||||
TypedData = selectedItem.ValueToChoose,
|
||||
Executed = true,
|
||||
Result = $"chose {selectedItem.ValueToChoose}"
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -24,14 +24,14 @@ namespace BtcTransmuter.Extension.Operators.Actions.Condition
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task<ConditionViewModel> BuildViewModel(RecipeAction from)
|
||||
protected override Task<ConditionViewModel> BuildViewModel(RecipeAction from)
|
||||
{
|
||||
var fromData = from.Get<ConditionData>();
|
||||
return new ConditionViewModel
|
||||
return Task.FromResult(new ConditionViewModel
|
||||
{
|
||||
RecipeId = @from.RecipeId,
|
||||
Condition = fromData.Condition
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
protected override async Task<(RecipeAction ToSave, ConditionViewModel showViewModel)> BuildModel(
|
||||
|
||||
@ -18,18 +18,17 @@ namespace BtcTransmuter.Extension.Operators.Actions.Condition
|
||||
public override string ControllerName => "Condition";
|
||||
|
||||
|
||||
protected override async Task<TypedActionHandlerResult<string>> Execute(Dictionary<string, object> data, RecipeAction recipeAction,
|
||||
protected override Task<TypedActionHandlerResult<string>> Execute(Dictionary<string, object> data, RecipeAction recipeAction,
|
||||
ConditionData actionData)
|
||||
{
|
||||
var condition = InterpolateString(actionData.Condition, data);
|
||||
|
||||
|
||||
return new TypedActionHandlerResult<string>()
|
||||
return Task.FromResult(new TypedActionHandlerResult<string>()
|
||||
{
|
||||
TypedData = condition,
|
||||
Executed = condition.Equals("true", StringComparison.InvariantCultureIgnoreCase),
|
||||
Result = $"Data value was: {condition}"
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@ -10,5 +10,6 @@
|
||||
<ProjectReference Include="..\BtcTransmuter.Extension.Email\BtcTransmuter.Extension.Email.csproj" />
|
||||
<ProjectReference Include="..\BtcTransmuter.Extension.Exchange\BtcTransmuter.Extension.Exchange.csproj" />
|
||||
<ProjectReference Include="..\BtcTransmuter.Extension.NBXplorer\BtcTransmuter.Extension.NBXplorer.csproj" />
|
||||
<ProjectReference Include="..\BtcTransmuter.Extension.Timer\BtcTransmuter.Extension.Timer.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
202
BtcTransmuter.Extension.Presets/DCAController.cs
Normal file
202
BtcTransmuter.Extension.Presets/DCAController.cs
Normal file
@ -0,0 +1,202 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BtcTransmuter.Abstractions.Extensions;
|
||||
using BtcTransmuter.Abstractions.ExternalServices;
|
||||
using BtcTransmuter.Abstractions.Recipes;
|
||||
using BtcTransmuter.Data.Entities;
|
||||
using BtcTransmuter.Data.Models;
|
||||
using BtcTransmuter.Extension.Exchange.Actions.PlaceOrder;
|
||||
using BtcTransmuter.Extension.Exchange.ExternalServices.Exchange;
|
||||
using BtcTransmuter.Extension.Timer.Triggers.Timer;
|
||||
using ExchangeSharp;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BtcTransmuter.Extension.Presets
|
||||
{
|
||||
[Route("presets-plugin/presets/dca")]
|
||||
[Authorize]
|
||||
public class DCAController : Controller, ITransmuterPreset
|
||||
{
|
||||
private readonly IExternalServiceManager _externalServiceManager;
|
||||
private readonly UserManager<User> _userManager;
|
||||
private readonly IRecipeManager _recipeManager;
|
||||
public string Id { get; } = "DCA";
|
||||
public string Name { get; } = "Dollar Cost Average";
|
||||
public string Description { get; } = "Schedule daily purchases of Bitcoin!";
|
||||
|
||||
public DCAController(
|
||||
IExternalServiceManager externalServiceManager,
|
||||
UserManager<User> userManager,
|
||||
IRecipeManager recipeManager)
|
||||
{
|
||||
_externalServiceManager = externalServiceManager;
|
||||
_userManager = userManager;
|
||||
_recipeManager = recipeManager;
|
||||
}
|
||||
|
||||
public (string ControllerName, string ActionName) GetLink()
|
||||
{
|
||||
return (Id, nameof(Create));
|
||||
}
|
||||
|
||||
[HttpGet("create")]
|
||||
public async Task<IActionResult> Create()
|
||||
{
|
||||
var services = await GetServices();
|
||||
|
||||
return View(new CreateDCAViewModel()
|
||||
{
|
||||
ExchangeServices = new SelectList(services, nameof(ExternalServiceData.Id), nameof(ExternalServiceData.Name))
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ExternalServiceData>> GetServices()
|
||||
{
|
||||
var services = await _externalServiceManager.GetExternalServicesData(new ExternalServicesDataQuery()
|
||||
{
|
||||
UserId = _userManager.GetUserId(User),
|
||||
Type = new[] {Exchange.ExternalServices.Exchange.ExchangeService.ExchangeServiceType}
|
||||
});
|
||||
var exchangeServices = services.Where(data => data.Type == Exchange.ExternalServices.Exchange.ExchangeService.ExchangeServiceType);
|
||||
|
||||
return exchangeServices;
|
||||
}
|
||||
|
||||
[HttpPost("create")]
|
||||
public async Task<IActionResult> Create(CreateDCAViewModel viewModel)
|
||||
{
|
||||
var services = await GetServices();
|
||||
|
||||
viewModel.ExchangeServices = new SelectList(services, nameof(ExternalServiceData.Id),
|
||||
nameof(ExternalServiceData.Name));
|
||||
if (viewModel.FiatAmount <= 0)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.FiatAmount), "Amount needs to be more than 0.");
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var serviceData =
|
||||
await _externalServiceManager.GetExternalServiceData(viewModel.SelectedExchangeServiceId, GetUserId());
|
||||
var exchangeService = new ExchangeService(serviceData);
|
||||
var symbols = (await (await exchangeService.ConstructClient()).GetMarketSymbolsAsync()).ToArray();
|
||||
if (!symbols.Contains(viewModel.MarketSymbol))
|
||||
{
|
||||
viewModel.AddModelError(nameof(viewModel.MarketSymbol), $"The market symbols you entered is invalid. Please choose from the following: {string.Join(",", symbols)}", ModelState);
|
||||
}
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
return await SetItUp(viewModel);
|
||||
}
|
||||
|
||||
protected string GetUserId()
|
||||
{
|
||||
return _userManager.GetUserId(User);
|
||||
}
|
||||
|
||||
private async Task<IActionResult> SetItUp(CreateDCAViewModel vm)
|
||||
{
|
||||
var presetName = $"Generated_DCA";
|
||||
|
||||
var recipe = new Recipe()
|
||||
{
|
||||
Name = presetName,
|
||||
Description = "Generated from a preset",
|
||||
UserId = _userManager.GetUserId(User),
|
||||
Enabled = false
|
||||
};
|
||||
await _recipeManager.AddOrUpdateRecipe(recipe);
|
||||
|
||||
var recipeTrigger = new RecipeTrigger()
|
||||
{
|
||||
TriggerId = new TimerTrigger().Id,
|
||||
RecipeId = recipe.Id
|
||||
};
|
||||
|
||||
recipeTrigger.Set(new TimerTriggerParameters()
|
||||
{
|
||||
StartOn = vm.StartOn,
|
||||
TriggerEvery = vm.TriggerEvery,
|
||||
TriggerEveryAmount = vm.TriggerEveryAmount
|
||||
|
||||
});
|
||||
await _recipeManager.AddOrUpdateRecipeTrigger(recipeTrigger);
|
||||
|
||||
var recipeActionGroup = new RecipeActionGroup()
|
||||
{
|
||||
RecipeId = recipe.Id
|
||||
};
|
||||
|
||||
await _recipeManager.AddRecipeActionGroup(recipeActionGroup);
|
||||
|
||||
var tradeAction = new RecipeAction()
|
||||
{
|
||||
RecipeId = recipe.Id,
|
||||
RecipeActionGroupId = recipeActionGroup.Id,
|
||||
ActionId = new PlaceOrderDataActionHandler().ActionId,
|
||||
ExternalServiceId = vm.SelectedExchangeServiceId,
|
||||
Order = 0,
|
||||
DataJson = JsonConvert.SerializeObject(new PlaceOrderData()
|
||||
{
|
||||
Amount = vm.FiatAmount.ToString(CultureInfo.InvariantCulture),
|
||||
IsBuy = vm.IsBuy,
|
||||
MarketSymbol = vm.MarketSymbol,
|
||||
OrderType = OrderType.Market
|
||||
})
|
||||
};
|
||||
|
||||
await _recipeManager.AddOrUpdateRecipeAction(tradeAction);
|
||||
|
||||
|
||||
return RedirectToAction("EditRecipe", "Recipes", new
|
||||
{
|
||||
id = recipe.Id,
|
||||
statusMessage =
|
||||
"Preset generated. Recipe is currently disabled for now. Please verify details are correct before enabling!"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateDCAViewModel
|
||||
{
|
||||
public SelectList ExchangeServices { get; set; }
|
||||
|
||||
[Display(Name = "Existing Exchange Store")]
|
||||
[Required]
|
||||
public string SelectedExchangeServiceId { get; set; }
|
||||
|
||||
[Display(Name = "The trading pair on the exchange")]
|
||||
[Required]
|
||||
public string MarketSymbol { get; set; }
|
||||
|
||||
[Display(Name = "Is it a buy market order?")]
|
||||
[Required]
|
||||
public bool IsBuy { get; set; } = true;
|
||||
|
||||
[Display(Name = "How much do you want to buy?")]
|
||||
[Required]
|
||||
public decimal FiatAmount { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Trigger every")]
|
||||
public int TriggerEveryAmount { get; set; } = 1;
|
||||
|
||||
[Required]
|
||||
public TimerTriggerParameters.TimerResetEvery TriggerEvery { get; set; } =
|
||||
TimerTriggerParameters.TimerResetEvery.Day;
|
||||
[Display(Name = "Start from")]
|
||||
public DateTime? StartOn { get; set; }
|
||||
}
|
||||
}
|
||||
@ -113,7 +113,7 @@ namespace BtcTransmuter.Extension.Presets
|
||||
var serviceData =
|
||||
await _externalServiceManager.GetExternalServiceData(condition.ExchangeServiceId, GetUserId());
|
||||
var exchangeService = new ExchangeService(serviceData);
|
||||
var symbols = (await exchangeService.ConstructClient().GetMarketSymbolsAsync()).ToArray();
|
||||
var symbols = (await (await exchangeService.ConstructClient()).GetMarketSymbolsAsync()).ToArray();
|
||||
if (!symbols.Contains(condition.MarketSymbol))
|
||||
{
|
||||
viewModel.AddModelError(
|
||||
|
||||
@ -14,6 +14,7 @@ namespace BtcTransmuter.Extension.Presets
|
||||
serviceCollection.AddTransient<ITransmuterPreset, PaymentForwarderController>();
|
||||
serviceCollection.AddTransient<ITransmuterPreset, BTCPayEmailReceiptsController>();
|
||||
serviceCollection.AddTransient<ITransmuterPreset, FiatExchangeConversionController>();
|
||||
serviceCollection.AddTransient<ITransmuterPreset, DCAController>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,12 +17,12 @@ namespace BtcTransmuter.Extension.Presets
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> ChoosePreset()
|
||||
public Task<IActionResult> ChoosePreset()
|
||||
{
|
||||
return View(new ChoosePresetViewModel()
|
||||
return Task.FromResult<IActionResult>(View(new ChoosePresetViewModel()
|
||||
{
|
||||
Presets = _transmuterPresets
|
||||
});
|
||||
}));
|
||||
}
|
||||
public class ChoosePresetViewModel
|
||||
{
|
||||
|
||||
126
BtcTransmuter.Extension.Presets/Views/DCA/Create.cshtml
Normal file
126
BtcTransmuter.Extension.Presets/Views/DCA/Create.cshtml
Normal file
@ -0,0 +1,126 @@
|
||||
@using System.Globalization
|
||||
@using BtcTransmuter.Extension.Exchange.ExternalServices.Exchange
|
||||
@using BtcTransmuter.Extension.Timer.Triggers.Timer
|
||||
@model BtcTransmuter.Extension.Presets.CreateDCAViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Create automated Dollar Cost Averaging";
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
|
||||
|
||||
<form method="post">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
|
||||
<div class="card mb-2">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Choose your Exchange</h5>
|
||||
<div class="form-group">
|
||||
@if (!Model.ExchangeServices.Any())
|
||||
{
|
||||
<div class="list-group-item ">
|
||||
<span class="text-danger">
|
||||
There are no Exchange services connected to your Transmuter account.
|
||||
|
||||
<a asp-controller="ExternalServices" asp-action="CreateExternalService" asp-route-selectedType="@ExchangeService.ExchangeServiceType">Please create one first.</a>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<select asp-for="SelectedExchangeServiceId" asp-items="Model.ExchangeServices" class="form-control"></select>
|
||||
|
||||
<a asp-controller="ExternalServices" asp-action="CreateExternalService" asp-route-selectedType="@ExchangeService.ExchangeServiceType">Create</a>
|
||||
}
|
||||
<span asp-validation-for="SelectedExchangeServiceId" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-2">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">When do you want to execute the trade?</h5>
|
||||
<div class="form-group">
|
||||
<label asp-for="StartOn" class="control-label"></label>
|
||||
<div class="input-group">
|
||||
<input type="datetime-local" asp-for="StartOn"
|
||||
value="@(Model.StartOn?.ToString("u", CultureInfo.InvariantCulture))"
|
||||
class="form-control flatdtpicker" placeholder="Start from"/>
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-secondary input-group-clear" type="button" title="Clear">
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<span asp-validation-for="StartOn" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="TriggerEveryAmount" class="control-label"></label>
|
||||
<div class="input-group">
|
||||
<input type="number" asp-for="TriggerEveryAmount" placeholder="Amount" class="form-control">
|
||||
<select class="custom-select" asp-for="TriggerEvery" asp-items="@Html.GetEnumSelectList(typeof(TimerTriggerParameters.TimerResetEvery))">
|
||||
</select>
|
||||
</div>
|
||||
<span asp-validation-for="TriggerEveryAmount" class="text-danger"></span>
|
||||
<span asp-validation-for="TriggerEvery" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-2">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Set up your trading conditions</h5>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="MarketSymbol" class="control-label"></label>
|
||||
<input asp-for="MarketSymbol" class="form-control autocomplete" data-datasrc="availableMarketSymbols" placeholder="Start typing to see a list of available trading pairs"/>
|
||||
<span asp-validation-for="MarketSymbol" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="IsBuy" class="control-label"></label>
|
||||
<input type="checkbox" asp-for="IsBuy" class="form-check"/>
|
||||
<span asp-validation-for="IsBuy" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="FiatAmount" class="control-label"></label>
|
||||
<input type="number" step="any" asp-for="FiatAmount" class="form-control"/>
|
||||
<span asp-validation-for="FiatAmount" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
<a asp-action="GetServices" asp-controller="ExternalServices" class="btn btn-secondary">Back to recipe</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
var actionUrlMapping = @Json.Serialize(Model.ExchangeServices.ToDictionary(item => item.Value, item => @Url.Action("GetAvailableMarketSymbols", "PlaceOrder", new {ExternalServiceId = item.Value})));;
|
||||
var availableMarketSymbols = [];
|
||||
$(document).ready(function(){
|
||||
$("#SelectedExchangeServiceId").on("input", populateAvailableMarketSymbols);
|
||||
function populateAvailableMarketSymbols(){
|
||||
var value = $("#SelectedExchangeServiceId").val();
|
||||
if(!value){
|
||||
availableMarketSymbols = [];
|
||||
}else{
|
||||
$.ajax({
|
||||
url: actionUrlMapping[value],
|
||||
success: function(response){
|
||||
availableMarketSymbols = response;
|
||||
},
|
||||
error: function(){
|
||||
availableMarketSymbols = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
populateAvailableMarketSymbols();
|
||||
})
|
||||
</script>
|
||||
@ -1,6 +1,6 @@
|
||||
@if (User.Claims.Any())
|
||||
{
|
||||
<li class="nav-item mr-3">
|
||||
<a asp-controller="Presets" asp-action="ChoosePreset">Presets</a>
|
||||
<li class="nav-item">
|
||||
<a asp-controller="Presets" asp-action="ChoosePreset" class="nav-link">Presets</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,9 +10,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MaxKagamine.Moq.HttpClient" Version="1.1.0" />
|
||||
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="Moq" Version="4.13.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="Moq" Version="4.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -10,9 +10,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="Moq" Version="4.13.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="Moq" Version="4.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -11,9 +11,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MaxKagamine.Moq.HttpClient" Version="1.1.0" />
|
||||
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="Moq" Version="4.13.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="Moq" Version="4.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -8,6 +8,6 @@
|
||||
<ProjectReference Include="..\BtcTransmuter.Abstractions\BtcTransmuter.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@ -26,6 +26,7 @@ namespace BtcTransmuter.Extension.Webhook.Triggers.ReceiveWebRequest
|
||||
|
||||
public static readonly List<string> AllowedMethods = new List<string>()
|
||||
{
|
||||
"",
|
||||
HttpMethod.Get.ToString(),
|
||||
HttpMethod.Put.ToString(),
|
||||
HttpMethod.Head.ToString(),
|
||||
@ -74,6 +75,7 @@ namespace BtcTransmuter.Extension.Webhook.Triggers.ReceiveWebRequest
|
||||
}
|
||||
|
||||
[Route("trigger/{relativeUrl?}")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> Trigger(string relativeUrl)
|
||||
{
|
||||
string body = null;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using System.Net.Http;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BtcTransmuter.Extension.Webhook.Triggers.ReceiveWebRequest
|
||||
{
|
||||
@ -8,7 +8,7 @@ namespace BtcTransmuter.Extension.Webhook.Triggers.ReceiveWebRequest
|
||||
public string RelativeUrl { get; set; } = "";
|
||||
public string Body { get; set; }= "";
|
||||
|
||||
public dynamic BodyJson { get; set; }
|
||||
public JObject BodyJson { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@ -22,7 +22,7 @@ namespace BtcTransmuter.Extension.Webhook.Triggers.ReceiveWebRequest
|
||||
ReceiveWebRequestTriggerData triggerData,
|
||||
ReceiveWebRequestTriggerParameters parameters)
|
||||
{
|
||||
if (triggerData.Method != parameters.Method)
|
||||
if (!string.IsNullOrEmpty(parameters.Method) && triggerData.Method != parameters.Method)
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
@ -6,13 +6,19 @@
|
||||
}
|
||||
|
||||
<div>
|
||||
Receive a <kbd>@data.Method</kbd> HTTP request at
|
||||
Receive a
|
||||
@if (!string.IsNullOrEmpty(data.Method))
|
||||
{
|
||||
<kbd>@data.Method</kbd>
|
||||
}
|
||||
HTTP request at
|
||||
<kbd>
|
||||
@Url.Action("Trigger", "ReceiveWebRequest", new
|
||||
{
|
||||
relativeUrl = data.RelativeUrl
|
||||
}, Context.Request.Scheme)</kbd>
|
||||
@if (!string.IsNullOrEmpty(@data.Body))
|
||||
}, Context.Request.Scheme)
|
||||
</kbd>
|
||||
@if (!string.IsNullOrEmpty(data.Body))
|
||||
{
|
||||
<span> with a body <kbd>@Enum.GetName(typeof(ReceiveWebRequestTriggerParameters.FieldComparer), data.BodyComparer) </kbd>@data.Body</span>
|
||||
}
|
||||
|
||||
@ -26,8 +26,7 @@ namespace BtcTransmuter.Data
|
||||
public DbSet<RecipeInvocation> RecipeInvocations { get; set; }
|
||||
public DbSet<RecipeTrigger> RecipeTriggers { get; set; }
|
||||
public DbSet<RecipeAction> RecipeActions { get; set; }
|
||||
public DbSet<Settings> Settings { get; set; }
|
||||
public DbSet<U2FDevice> U2FDevices { get; set; }
|
||||
public DbSet<Settings> Settings { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
@ -79,4 +78,4 @@ namespace BtcTransmuter.Data
|
||||
return new ApplicationDbContext(optionsBuilder.Options, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,10 +5,7 @@
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BtcTransmuter.Abstractions\BtcTransmuter.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\BtcTransmuter.Data\BtcTransmuter.Data.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="U2F.Core" Version="2.0.1" />
|
||||
<ProjectReference Include="..\BtcTransmuter.Abstractions\BtcTransmuter.Abstractions.csproj"/>
|
||||
<ProjectReference Include="..\BtcTransmuter.Data\BtcTransmuter.Data.csproj"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
505
BtcTransmuter.Services/Migrations/20200402084229_AddUserBlob.Designer.cs
generated
Normal file
505
BtcTransmuter.Services/Migrations/20200402084229_AddUserBlob.Designer.cs
generated
Normal file
@ -0,0 +1,505 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using BtcTransmuter.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace BtcTransmuter.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20200402084229_AddUserBlob")]
|
||||
partial class AddUserBlob
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.1");
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.ExternalServiceData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DataJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("ExternalServices");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.Recipe", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Recipes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.RecipeAction", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ActionId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DataJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ExternalServiceId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Order")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("RecipeActionGroupId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RecipeId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ExternalServiceId");
|
||||
|
||||
b.HasIndex("RecipeActionGroupId");
|
||||
|
||||
b.HasIndex("RecipeId");
|
||||
|
||||
b.ToTable("RecipeActions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.RecipeActionGroup", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RecipeId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RecipeId");
|
||||
|
||||
b.ToTable("RecipeActionGroups");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.RecipeInvocation", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ActionResult")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RecipeAction")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RecipeId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("Timestamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TriggerDataJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RecipeId");
|
||||
|
||||
b.ToTable("RecipeInvocations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.RecipeTrigger", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DataJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ExternalServiceId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RecipeId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TriggerId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ExternalServiceId");
|
||||
|
||||
b.HasIndex("RecipeId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("RecipeTriggers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.Settings", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DataJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Key")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.User", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DataJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.ExternalServiceData", b =>
|
||||
{
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.Recipe", b =>
|
||||
{
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User", null)
|
||||
.WithMany("Recipes")
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.RecipeAction", b =>
|
||||
{
|
||||
b.HasOne("BtcTransmuter.Data.Entities.ExternalServiceData", "ExternalService")
|
||||
.WithMany("RecipeActions")
|
||||
.HasForeignKey("ExternalServiceId");
|
||||
|
||||
b.HasOne("BtcTransmuter.Data.Entities.RecipeActionGroup", "RecipeActionGroup")
|
||||
.WithMany("RecipeActions")
|
||||
.HasForeignKey("RecipeActionGroupId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("BtcTransmuter.Data.Entities.Recipe", "Recipe")
|
||||
.WithMany("RecipeActions")
|
||||
.HasForeignKey("RecipeId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.RecipeActionGroup", b =>
|
||||
{
|
||||
b.HasOne("BtcTransmuter.Data.Entities.Recipe", "Recipe")
|
||||
.WithMany("RecipeActionGroups")
|
||||
.HasForeignKey("RecipeId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.RecipeInvocation", b =>
|
||||
{
|
||||
b.HasOne("BtcTransmuter.Data.Entities.Recipe", "Recipe")
|
||||
.WithMany("RecipeInvocations")
|
||||
.HasForeignKey("RecipeId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.RecipeTrigger", b =>
|
||||
{
|
||||
b.HasOne("BtcTransmuter.Data.Entities.ExternalServiceData", "ExternalService")
|
||||
.WithMany("RecipeTriggers")
|
||||
.HasForeignKey("ExternalServiceId");
|
||||
|
||||
b.HasOne("BtcTransmuter.Data.Entities.Recipe", "Recipe")
|
||||
.WithOne("RecipeTrigger")
|
||||
.HasForeignKey("BtcTransmuter.Data.Entities.RecipeTrigger", "RecipeId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace BtcTransmuter.Data.Migrations
|
||||
{
|
||||
public partial class AddUserBlob : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "DataJson",
|
||||
table: "AspNetUsers",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DataJson",
|
||||
table: "AspNetUsers");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,20 +14,25 @@ namespace BtcTransmuter.Data.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.4-servicing-10062");
|
||||
.HasAnnotation("ProductVersion", "3.1.1");
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.ExternalServiceData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DataJson");
|
||||
b.Property<string>("DataJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name");
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Type");
|
||||
b.Property<string>("Type")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId");
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -39,15 +44,20 @@ namespace BtcTransmuter.Data.Migrations
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.Recipe", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description");
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("Enabled");
|
||||
b.Property<bool>("Enabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name");
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId");
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -59,19 +69,26 @@ namespace BtcTransmuter.Data.Migrations
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.RecipeAction", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ActionId");
|
||||
b.Property<string>("ActionId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DataJson");
|
||||
b.Property<string>("DataJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ExternalServiceId");
|
||||
b.Property<string>("ExternalServiceId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Order");
|
||||
b.Property<int>("Order")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("RecipeActionGroupId");
|
||||
b.Property<string>("RecipeActionGroupId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RecipeId");
|
||||
b.Property<string>("RecipeId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -87,9 +104,11 @@ namespace BtcTransmuter.Data.Migrations
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.RecipeActionGroup", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RecipeId");
|
||||
b.Property<string>("RecipeId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -101,17 +120,23 @@ namespace BtcTransmuter.Data.Migrations
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.RecipeInvocation", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ActionResult");
|
||||
b.Property<string>("ActionResult")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RecipeAction");
|
||||
b.Property<string>("RecipeAction")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RecipeId");
|
||||
b.Property<string>("RecipeId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("Timestamp");
|
||||
b.Property<DateTime>("Timestamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TriggerDataJson");
|
||||
b.Property<string>("TriggerDataJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -123,15 +148,20 @@ namespace BtcTransmuter.Data.Migrations
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.RecipeTrigger", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DataJson");
|
||||
b.Property<string>("DataJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ExternalServiceId");
|
||||
b.Property<string>("ExternalServiceId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RecipeId");
|
||||
b.Property<string>("RecipeId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TriggerId");
|
||||
b.Property<string>("TriggerId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -146,11 +176,14 @@ namespace BtcTransmuter.Data.Migrations
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.Settings", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DataJson");
|
||||
b.Property<string>("DataJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Key");
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -163,39 +196,56 @@ namespace BtcTransmuter.Data.Migrations
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.User", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DataJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
@ -213,15 +263,18 @@ namespace BtcTransmuter.Data.Migrations
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
@ -236,14 +289,18 @@ namespace BtcTransmuter.Data.Migrations
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -255,14 +312,18 @@ namespace BtcTransmuter.Data.Migrations
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
@ -273,14 +334,18 @@ namespace BtcTransmuter.Data.Migrations
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider");
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderKey");
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
@ -291,9 +356,11 @@ namespace BtcTransmuter.Data.Migrations
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
@ -304,13 +371,17 @@ namespace BtcTransmuter.Data.Migrations
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LoginProvider");
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name");
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value");
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
@ -326,7 +397,7 @@ namespace BtcTransmuter.Data.Migrations
|
||||
|
||||
modelBuilder.Entity("BtcTransmuter.Data.Entities.Recipe", b =>
|
||||
{
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User")
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User", null)
|
||||
.WithMany("Recipes")
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
@ -378,47 +449,53 @@ namespace BtcTransmuter.Data.Migrations
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User")
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User")
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User")
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User")
|
||||
b.HasOne("BtcTransmuter.Data.Entities.User", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
|
||||
@ -70,34 +70,30 @@ namespace BtcTransmuter.Services
|
||||
|
||||
public async Task<IEnumerable<RecipeInvocation>> GetRecipeInvocations(RecipeInvocationsQuery query)
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
await using var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
var queryable = context.RecipeInvocations
|
||||
.Include(invocation => invocation.Recipe)
|
||||
.ThenInclude(recipe => recipe.RecipeTrigger)
|
||||
.AsEnumerable();
|
||||
if (query.OrderBy != null)
|
||||
{
|
||||
using (var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
|
||||
switch (query.OrderBy.Field)
|
||||
{
|
||||
var queryable = context.RecipeInvocations
|
||||
.Include(invocation => invocation.Recipe)
|
||||
.ThenInclude(recipe => recipe.RecipeTrigger)
|
||||
.AsEnumerable();
|
||||
if (query.OrderBy != null)
|
||||
{
|
||||
switch (query.OrderBy.Field)
|
||||
{
|
||||
case RecipeInvocationsQuery.RecipeInvocationsQueryOrderBy.Timestamp:
|
||||
queryable = query.OrderBy.Direction == OrderDirection.Ascending
|
||||
? queryable.OrderBy(invocation => invocation.Timestamp)
|
||||
: queryable.OrderByDescending(invocation => invocation.Timestamp);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
return queryable
|
||||
.Where(invocation =>
|
||||
invocation.RecipeId.Equals(query.RecipeId, StringComparison.InvariantCultureIgnoreCase))
|
||||
.Skip(query.Skip).Take(query.Take).ToList();
|
||||
case RecipeInvocationsQuery.RecipeInvocationsQueryOrderBy.Timestamp:
|
||||
queryable = query.OrderBy.Direction == OrderDirection.Ascending
|
||||
? queryable.OrderBy(invocation => invocation.Timestamp)
|
||||
: queryable.OrderByDescending(invocation => invocation.Timestamp);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
return queryable
|
||||
.Where(invocation =>
|
||||
invocation.RecipeId.Equals(query.RecipeId, StringComparison.InvariantCultureIgnoreCase))
|
||||
.Skip(query.Skip).Take(query.Take).ToList();
|
||||
}
|
||||
|
||||
public async Task AddOrUpdateRecipe(Recipe recipe)
|
||||
@ -150,6 +146,8 @@ namespace BtcTransmuter.Services
|
||||
|
||||
public async Task AddOrUpdateRecipeAction(RecipeAction action)
|
||||
{
|
||||
var oldES = action.ExternalService;
|
||||
action.ExternalService = null;
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
using (var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
|
||||
@ -160,10 +158,12 @@ namespace BtcTransmuter.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
context.Entry(action).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
action.ExternalService = oldES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ using BtcTransmuter.Abstractions.ExternalServices;
|
||||
using BtcTransmuter.Abstractions.Recipes;
|
||||
using BtcTransmuter.Abstractions.Settings;
|
||||
using BtcTransmuter.Abstractions.Triggers;
|
||||
using BtcTransmuter.Abstractions.U2F;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BtcTransmuter.Services
|
||||
@ -17,7 +16,6 @@ namespace BtcTransmuter.Services
|
||||
collection.AddSingleton<IActionDispatcher, ActionDispatcher>();
|
||||
collection.AddSingleton<ITriggerDispatcher, TriggerDispatcher>();
|
||||
collection.AddSingleton<ISettingsManager, SettingsManager>();
|
||||
collection.AddSingleton<IU2FService, U2FService>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,273 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BtcTransmuter.Abstractions.Extensions;
|
||||
using BtcTransmuter.Abstractions.U2F;
|
||||
using BtcTransmuter.Data;
|
||||
using BtcTransmuter.Data.Entities;
|
||||
using BtcTransmuter.Data.Entities.U2F;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using U2F.Core.Exceptions;
|
||||
using U2F.Core.Models;
|
||||
using U2F.Core.Utils;
|
||||
|
||||
namespace BtcTransmuter.Services
|
||||
{
|
||||
public class U2FService : IU2FService
|
||||
{
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
|
||||
public U2FService(IServiceScopeFactory serviceScopeFactory)
|
||||
{
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
}
|
||||
|
||||
private ConcurrentDictionary<string, List<U2FDeviceAuthenticationRequest>> UserAuthenticationRequests
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
= new ConcurrentDictionary<string, List<U2FDeviceAuthenticationRequest>>();
|
||||
|
||||
public async Task<List<U2FDevice>> GetDevices(string userId)
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
using (var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
|
||||
{
|
||||
return await context.U2FDevices
|
||||
.Where(device => device.UserId == userId)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RemoveDevice(string id, string userId)
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
using (var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
|
||||
{
|
||||
var device = await context.U2FDevices.FindAsync(id);
|
||||
if (device == null || !device.UserId.Equals(userId, StringComparison.InvariantCulture))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.U2FDevices.Remove(device);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> HasDevices(string userId)
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
using (var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
|
||||
{
|
||||
return await context.U2FDevices.Where(fDevice => fDevice.UserId == userId).AnyAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ServerRegisterResponse StartDeviceRegistration(string userId, string appId)
|
||||
{
|
||||
var startedRegistration = StartDeviceRegistrationCore(appId);
|
||||
|
||||
UserAuthenticationRequests.AddOrReplace(userId, new List<U2FDeviceAuthenticationRequest>()
|
||||
{
|
||||
new U2FDeviceAuthenticationRequest()
|
||||
{
|
||||
AppId = startedRegistration.AppId,
|
||||
Challenge = startedRegistration.Challenge,
|
||||
Version = global::U2F.Core.Crypto.U2F.U2FVersion,
|
||||
}
|
||||
});
|
||||
|
||||
return new ServerRegisterResponse
|
||||
{
|
||||
AppId = startedRegistration.AppId,
|
||||
Challenge = startedRegistration.Challenge,
|
||||
Version = startedRegistration.Version
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<bool> CompleteRegistration(string userId, string deviceResponse, string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(deviceResponse))
|
||||
return false;
|
||||
|
||||
if (!UserAuthenticationRequests.ContainsKey(userId) || !UserAuthenticationRequests[userId].Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var registerResponse = RegisterResponse.FromJson<RegisterResponse>(deviceResponse);
|
||||
|
||||
//There is only 1 request when registering device
|
||||
var authenticationRequest = UserAuthenticationRequests[userId].First();
|
||||
|
||||
var startedRegistration =
|
||||
new StartedRegistration(authenticationRequest.Challenge, authenticationRequest.AppId);
|
||||
var registration = FinishRegistrationCore(startedRegistration, registerResponse);
|
||||
|
||||
UserAuthenticationRequests.AddOrReplace(userId, new List<U2FDeviceAuthenticationRequest>());
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
using (var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
|
||||
{
|
||||
var duplicate = context.U2FDevices.Any(device =>
|
||||
device.UserId == userId &&
|
||||
device.KeyHandle.Equals(registration.KeyHandle) &&
|
||||
device.PublicKey.Equals(registration.PublicKey));
|
||||
|
||||
if (duplicate)
|
||||
{
|
||||
throw new U2fException("The U2F Device has already been registered with this user");
|
||||
}
|
||||
|
||||
await context.U2FDevices.AddAsync(new U2FDevice()
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
AttestationCert = registration.AttestationCert,
|
||||
Counter = Convert.ToInt32(registration.Counter),
|
||||
Name = name,
|
||||
KeyHandle = registration.KeyHandle,
|
||||
PublicKey = registration.PublicKey,
|
||||
UserId = userId
|
||||
});
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> AuthenticateUser(string userId, string deviceResponse)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(userId) || string.IsNullOrWhiteSpace(deviceResponse))
|
||||
return false;
|
||||
|
||||
var authenticateResponse =
|
||||
AuthenticateResponse.FromJson<AuthenticateResponse>(deviceResponse);
|
||||
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
using (var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
|
||||
{
|
||||
var keyHandle = authenticateResponse.KeyHandle.Base64StringToByteArray();
|
||||
var device = await context.U2FDevices.Where(fDevice =>
|
||||
fDevice.UserId == userId &&
|
||||
fDevice.KeyHandle == keyHandle).SingleOrDefaultAsync();
|
||||
|
||||
if (device == null)
|
||||
return false;
|
||||
|
||||
// User will have a authentication request for each device they have registered so get the one that matches the device key handle
|
||||
|
||||
var authenticationRequest =
|
||||
UserAuthenticationRequests[userId].First(f =>
|
||||
f.KeyHandle.Equals(authenticateResponse.KeyHandle, StringComparison.InvariantCulture));
|
||||
|
||||
var registration = new DeviceRegistration(device.KeyHandle, device.PublicKey,
|
||||
device.AttestationCert, Convert.ToUInt32(device.Counter));
|
||||
|
||||
var authentication = new StartedAuthentication(authenticationRequest.Challenge,
|
||||
authenticationRequest.AppId, authenticationRequest.KeyHandle);
|
||||
|
||||
|
||||
var challengeAuthenticationRequestMatch = UserAuthenticationRequests[userId].First(f =>
|
||||
f.Challenge.Equals(authenticateResponse.GetClientData().Challenge,
|
||||
StringComparison.InvariantCulture));
|
||||
|
||||
if (authentication.Challenge != challengeAuthenticationRequestMatch.Challenge)
|
||||
{
|
||||
authentication = new StartedAuthentication(challengeAuthenticationRequestMatch.Challenge,
|
||||
authenticationRequest.AppId, authenticationRequest.KeyHandle);
|
||||
}
|
||||
|
||||
FinishAuthenticationCore(authentication, authenticateResponse, registration);
|
||||
|
||||
UserAuthenticationRequests.AddOrReplace(userId, new List<U2FDeviceAuthenticationRequest>());
|
||||
|
||||
device.Counter = Convert.ToInt32(registration.Counter);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<List<ServerChallenge>> GenerateDeviceChallenges(string userId, string appId)
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
using (var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
|
||||
{
|
||||
var devices = await context.U2FDevices.Where(fDevice => fDevice.UserId == userId)
|
||||
.ToListAsync();
|
||||
|
||||
if (devices.Count == 0)
|
||||
return null;
|
||||
|
||||
var requests = new List<U2FDeviceAuthenticationRequest>();
|
||||
|
||||
|
||||
var serverChallenges = new List<ServerChallenge>();
|
||||
foreach (var registeredDevice in devices)
|
||||
{
|
||||
var challenge = StartAuthenticationCore(appId, registeredDevice);
|
||||
serverChallenges.Add(new ServerChallenge()
|
||||
{
|
||||
challenge = challenge.Challenge,
|
||||
appId = challenge.AppId,
|
||||
version = challenge.Version,
|
||||
keyHandle = challenge.KeyHandle
|
||||
});
|
||||
|
||||
requests.Add(
|
||||
new U2FDeviceAuthenticationRequest()
|
||||
{
|
||||
AppId = appId,
|
||||
Challenge = challenge.Challenge,
|
||||
KeyHandle = registeredDevice.KeyHandle.ByteArrayToBase64String(),
|
||||
Version = global::U2F.Core.Crypto.U2F.U2FVersion
|
||||
});
|
||||
}
|
||||
|
||||
UserAuthenticationRequests.AddOrReplace(userId, requests);
|
||||
return serverChallenges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual StartedRegistration StartDeviceRegistrationCore(string appId)
|
||||
{
|
||||
return global::U2F.Core.Crypto.U2F.StartRegistration(appId);
|
||||
}
|
||||
|
||||
protected virtual DeviceRegistration FinishRegistrationCore(StartedRegistration startedRegistration,
|
||||
RegisterResponse registerResponse)
|
||||
{
|
||||
return global::U2F.Core.Crypto.U2F.FinishRegistration(startedRegistration, registerResponse);
|
||||
}
|
||||
|
||||
protected virtual StartedAuthentication StartAuthenticationCore(string appId, U2FDevice registeredDevice)
|
||||
{
|
||||
return global::U2F.Core.Crypto.U2F.StartAuthentication(appId,
|
||||
new DeviceRegistration(registeredDevice.KeyHandle, registeredDevice.PublicKey,
|
||||
registeredDevice.AttestationCert, (uint) registeredDevice.Counter));
|
||||
}
|
||||
|
||||
protected virtual void FinishAuthenticationCore(StartedAuthentication authentication,
|
||||
AuthenticateResponse authenticateResponse, DeviceRegistration registration)
|
||||
{
|
||||
global::U2F.Core.Crypto.U2F.FinishAuthentication(authentication, authenticateResponse, registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,9 +9,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="Moq" Version="4.13.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="Moq" Version="4.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="Moq" Version="4.13.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
<PackageReference Include="Moq" Version="4.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -9,7 +9,7 @@ namespace BtcTransmuter.Tests
|
||||
public class InterpolationTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task CanInterpolate()
|
||||
public void CanInterpolate()
|
||||
{
|
||||
Assert.Equal("hello world",InterpolationHelper.InterpolateString("{{Data1 +\" \" + Data2}}", new Dictionary<string, object>()
|
||||
{
|
||||
|
||||
@ -15,8 +15,7 @@ namespace BtcTransmuter.Areas.Identity
|
||||
{
|
||||
public void Configure(IWebHostBuilder builder)
|
||||
{
|
||||
builder.ConfigureServices((context, services) => {
|
||||
});
|
||||
builder.ConfigureServices((context, services) => { services.AddScoped<BTCPayAuthService>(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@
|
||||
}
|
||||
|
||||
@inject ISettingsManager SettingsManager
|
||||
@inject BtcTransmuterOptions Options;
|
||||
@{
|
||||
var settings = await SettingsManager.GetSettings<SystemSettings>(nameof(SystemSettings));
|
||||
}
|
||||
@ -17,37 +18,45 @@
|
||||
<div class="col-md-4">
|
||||
<section>
|
||||
<form id="account" method="post">
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Input.Email"></label>
|
||||
<input asp-for="Input.Email" class="form-control" />
|
||||
<input asp-for="Input.Email" class="form-control"/>
|
||||
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Input.Password"></label>
|
||||
<input asp-for="Input.Password" class="form-control" />
|
||||
<input asp-for="Input.Password" class="form-control"/>
|
||||
<span asp-validation-for="Input.Password" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label asp-for="Input.RememberMe">
|
||||
<input asp-for="Input.RememberMe" />
|
||||
<input asp-for="Input.RememberMe"/>
|
||||
@Html.DisplayNameFor(m => m.Input.RememberMe)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">Log in</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Log in
|
||||
@if (Options.DisableInternalAuth)
|
||||
{
|
||||
<span> with BTCPay Server account</span>
|
||||
}
|
||||
else if (Options.BTCPayAuthServer != null)
|
||||
{
|
||||
<span> (local account or BTCPay Server account)</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
@if (!settings.DisableRegistration)
|
||||
@if (!settings.DisableRegistration && !Options.DisableInternalAuth)
|
||||
{
|
||||
|
||||
<div class="form-group">
|
||||
@* <p> *@
|
||||
@* <a id="forgot-password" asp-page="./ForgotPassword">Forgot your password?</a> *@
|
||||
@* </p> *@
|
||||
|
||||
|
||||
<p>
|
||||
<a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a>
|
||||
</p>
|
||||
@ -56,37 +65,8 @@
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
@* <div class="col-md-6 col-md-offset-2"> *@
|
||||
@* <section> *@
|
||||
@* <h4>Use another service to log in.</h4> *@
|
||||
@* <hr /> *@
|
||||
@* @{ *@
|
||||
@* if ((Model.ExternalLogins?.Count ?? 0) == 0) *@
|
||||
@* { *@
|
||||
@* <div> *@
|
||||
@* <p> *@
|
||||
@* Coming soon: Log in with Btcpayserver account *@
|
||||
@* </p> *@
|
||||
@* </div> *@
|
||||
@* } *@
|
||||
@* else *@
|
||||
@* { *@
|
||||
@* <form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal"> *@
|
||||
@* <div> *@
|
||||
@* <p> *@
|
||||
@* @foreach (var provider in Model.ExternalLogins) *@
|
||||
@* { *@
|
||||
@* <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button> *@
|
||||
@* } *@
|
||||
@* </p> *@
|
||||
@* </div> *@
|
||||
@* </form> *@
|
||||
@* } *@
|
||||
@* } *@
|
||||
@* </section> *@
|
||||
@* </div> *@
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
<partial name="_ValidationScriptsPartial"/>
|
||||
}
|
||||
@ -2,27 +2,41 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using BtcTransmuter.Data.Entities;
|
||||
using BtcTransmuter.Data.Models;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BtcTransmuter.Areas.Identity.Pages.Account
|
||||
{
|
||||
[AllowAnonymous]
|
||||
public class LoginModel : PageModel
|
||||
{
|
||||
private readonly BTCPayAuthService _btcPayAuthService;
|
||||
private readonly SignInManager<User> _signInManager;
|
||||
private readonly ILogger<LoginModel> _logger;
|
||||
private readonly IBtcTransmuterOptions _btcTransmuterOptions;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly UserManager<User> _userManager;
|
||||
|
||||
public LoginModel(SignInManager<User> signInManager, ILogger<LoginModel> logger)
|
||||
public LoginModel(BTCPayAuthService btcPayAuthService,SignInManager<User> signInManager, ILogger<LoginModel> logger, IBtcTransmuterOptions btcTransmuterOptions, IHttpClientFactory httpClientFactory, UserManager<User> userManager)
|
||||
{
|
||||
_btcPayAuthService = btcPayAuthService;
|
||||
_signInManager = signInManager;
|
||||
_logger = logger;
|
||||
_btcTransmuterOptions = btcTransmuterOptions;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
[BindProperty]
|
||||
@ -72,32 +86,66 @@ namespace BtcTransmuter.Areas.Identity.Pages.Account
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
// This doesn't count login failures towards account lockout
|
||||
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
|
||||
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
|
||||
if (result.Succeeded)
|
||||
|
||||
var user = await _btcPayAuthService.LoginAndRegisterIfNeeded(Input.Email, Input.Password);
|
||||
if (user != null)
|
||||
{
|
||||
_logger.LogInformation("User logged in.");
|
||||
await _signInManager.SignInAsync(user, Input.RememberMe);
|
||||
_logger.LogInformation("User logged in using BTCPay.");
|
||||
return LocalRedirect(returnUrl);
|
||||
}
|
||||
if (result.RequiresTwoFactor)
|
||||
else if (!_btcTransmuterOptions.DisableInternalAuth)
|
||||
{
|
||||
return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
|
||||
}
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
_logger.LogWarning("User account locked out.");
|
||||
return RedirectToPage("./Lockout");
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
|
||||
return Page();
|
||||
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe,
|
||||
lockoutOnFailure: true);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.LogInformation("User logged in.");
|
||||
return LocalRedirect(returnUrl);
|
||||
}
|
||||
|
||||
if (result.RequiresTwoFactor)
|
||||
{
|
||||
return RedirectToPage("./LoginWith2fa",
|
||||
new {ReturnUrl = returnUrl, RememberMe = Input.RememberMe});
|
||||
}
|
||||
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
_logger.LogWarning("User account locked out.");
|
||||
return RedirectToPage("./Lockout");
|
||||
}
|
||||
}
|
||||
|
||||
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
|
||||
}
|
||||
|
||||
// If we got this far, something failed, redisplay form
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
|
||||
public class GetCurrentUserResponse {
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[JsonProperty("emailConfirmed")]
|
||||
public bool EmailConfirmed { get; set; }
|
||||
|
||||
[JsonProperty("requiresEmailConfirmation")]
|
||||
public bool RequiresEmailConfirmation { get; set; }
|
||||
|
||||
|
||||
[JsonProperty("roles")]
|
||||
public string[] Roles { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Id}{Email}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
@page
|
||||
@model BTCPayAccountLinkModel
|
||||
@{
|
||||
ViewData["Title"] = "BTCPay Account Link";
|
||||
ViewData["ActivePage"] = ManageNavPages.BTCPayAccountLink;
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form id="change-password-form" method="post">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Input.AccessToken"></label>
|
||||
<input asp-for="Input.AccessToken" class="form-control" />
|
||||
<span asp-validation-for="Input.AccessToken" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Update</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using BtcTransmuter.Data.Entities;
|
||||
using BtcTransmuter.Data.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class BTCPayAccountLinkModel : PageModel
|
||||
{
|
||||
private readonly UserManager<User> _userManager;
|
||||
private readonly SignInManager<User> _signInManager;
|
||||
private readonly IBtcTransmuterOptions _btcTransmuterOptions;
|
||||
private readonly BTCPayAuthService _btcPayAuthService;
|
||||
|
||||
public BTCPayAccountLinkModel(
|
||||
UserManager<User> userManager,
|
||||
SignInManager<User> signInManager,
|
||||
IBtcTransmuterOptions btcTransmuterOptions, BTCPayAuthService btcPayAuthService)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
_btcTransmuterOptions = btcTransmuterOptions;
|
||||
_btcPayAuthService = btcPayAuthService;
|
||||
}
|
||||
|
||||
public bool CurrentTokenValid { get; set; }
|
||||
[BindProperty] public InputModel Input { get; set; }
|
||||
|
||||
[TempData] public string StatusMessage { get; set; }
|
||||
|
||||
public class InputModel
|
||||
{
|
||||
[Display(Name = "Access Token")] public string AccessToken { get; set; }
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnGetAsync()
|
||||
{
|
||||
if (_btcTransmuterOptions.BTCPayAuthServer == null)
|
||||
{
|
||||
return RedirectToPage("./ChangePassword");
|
||||
}
|
||||
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
var blob = user.Get<UserBlob>();
|
||||
Input = new InputModel()
|
||||
{
|
||||
AccessToken = blob.BTCPayAuthDetails.AccessToken
|
||||
};
|
||||
if (!string.IsNullOrEmpty(Input.AccessToken))
|
||||
{
|
||||
var currentToken = await _btcPayAuthService.CheckToken(user);
|
||||
CurrentTokenValid = currentToken != null && currentToken.Id == blob.BTCPayAuthDetails.UserId;
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
if (_btcTransmuterOptions.BTCPayAuthServer == null)
|
||||
{
|
||||
return RedirectToPage("./ChangePassword");
|
||||
}
|
||||
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
var blob = user.Get<UserBlob>();
|
||||
if (_btcTransmuterOptions.DisableInternalAuth && string.IsNullOrEmpty(Input.AccessToken))
|
||||
{
|
||||
ModelState.AddModelError("Input.AccessToken", "Access token is required.");
|
||||
}
|
||||
else if (_btcTransmuterOptions.DisableInternalAuth && !string.IsNullOrEmpty(Input.AccessToken))
|
||||
{
|
||||
var response = await _btcPayAuthService.CheckToken(user);
|
||||
CurrentTokenValid = response != null;
|
||||
if (CurrentTokenValid)
|
||||
{
|
||||
blob.BTCPayAuthDetails.AccessToken = Input.AccessToken;
|
||||
blob.BTCPayAuthDetails.UserId = response.Id;
|
||||
user.Set(blob);
|
||||
|
||||
await _userManager.UpdateAsync(user);
|
||||
}
|
||||
else if (!CurrentTokenValid && _btcTransmuterOptions.DisableInternalAuth)
|
||||
{
|
||||
ModelState.AddModelError("Input.AccessToken", "Invalid Access token.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return Page();
|
||||
}
|
||||
return RedirectToPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using BtcTransmuter.Data.Entities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class ChangePasswordModel : PageModel
|
||||
@ -15,22 +13,22 @@ namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
private readonly UserManager<User> _userManager;
|
||||
private readonly SignInManager<User> _signInManager;
|
||||
private readonly ILogger<ChangePasswordModel> _logger;
|
||||
private readonly IBtcTransmuterOptions _btcTransmuterOptions;
|
||||
|
||||
public ChangePasswordModel(
|
||||
UserManager<User> userManager,
|
||||
SignInManager<User> signInManager,
|
||||
ILogger<ChangePasswordModel> logger)
|
||||
ILogger<ChangePasswordModel> logger, IBtcTransmuterOptions btcTransmuterOptions)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
_logger = logger;
|
||||
_btcTransmuterOptions = btcTransmuterOptions;
|
||||
}
|
||||
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
[BindProperty] public InputModel Input { get; set; }
|
||||
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
[TempData] public string StatusMessage { get; set; }
|
||||
|
||||
public class InputModel
|
||||
{
|
||||
@ -40,7 +38,8 @@ namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
public string OldPassword { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
|
||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.",
|
||||
MinimumLength = 6)]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "New password")]
|
||||
public string NewPassword { get; set; }
|
||||
@ -53,6 +52,11 @@ namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
|
||||
public async Task<IActionResult> OnGetAsync()
|
||||
{
|
||||
if (_btcTransmuterOptions.DisableInternalAuth)
|
||||
{
|
||||
return RedirectToPage("./BTCPayAccountLink");
|
||||
}
|
||||
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
@ -70,6 +74,11 @@ namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
|
||||
public async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
if (_btcTransmuterOptions.DisableInternalAuth)
|
||||
{
|
||||
return RedirectToPage("./BTCPayAccountLink");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return Page();
|
||||
@ -81,13 +90,15 @@ namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
var changePasswordResult = await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword);
|
||||
var changePasswordResult =
|
||||
await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword);
|
||||
if (!changePasswordResult.Succeeded)
|
||||
{
|
||||
foreach (var error in changePasswordResult.Errors)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, error.Description);
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
@ -98,4 +109,4 @@ namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
return RedirectToPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
@page
|
||||
@model DownloadPersonalDataModel
|
||||
@{
|
||||
ViewData["Title"] = "Download Your Data";
|
||||
ViewData["ActivePage"] = ManageNavPages.PersonalData;
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BtcTransmuter.Data.Entities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class DownloadPersonalDataModel : PageModel
|
||||
{
|
||||
private readonly UserManager<User> _userManager;
|
||||
private readonly ILogger<DownloadPersonalDataModel> _logger;
|
||||
|
||||
public DownloadPersonalDataModel(
|
||||
UserManager<User> userManager,
|
||||
ILogger<DownloadPersonalDataModel> logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
_logger.LogInformation("User with ID '{UserId}' asked for their personal data.", _userManager.GetUserId(User));
|
||||
|
||||
// Only include personal data for download
|
||||
var personalData = new Dictionary<string, string>();
|
||||
var personalDataProps = typeof(User).GetProperties().Where(
|
||||
prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute)));
|
||||
foreach (var p in personalDataProps)
|
||||
{
|
||||
personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null");
|
||||
}
|
||||
|
||||
Response.Headers.Add("Content-Disposition", "attachment; filename=PersonalData.json");
|
||||
return new FileContentResult(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(personalData)), "text/json");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,35 +6,24 @@
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
<partial name="_StatusMessage" for="StatusMessage" />
|
||||
<partial name="_StatusMessage" for="StatusMessage"/>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form id="profile-form" method="post">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Username"></label>
|
||||
<input asp-for="Username" class="form-control" disabled />
|
||||
<input asp-for="Username" class="form-control" disabled/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Input.Email"></label>
|
||||
@if (Model.IsEmailConfirmed)
|
||||
{
|
||||
<div class="input-group">
|
||||
<input asp-for="Input.Email" class="form-control" />
|
||||
<span class="input-group-addon" aria-hidden="true"><span class="glyphicon glyphicon-ok text-success"></span></span>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input asp-for="Input.Email" class="form-control" />
|
||||
@* <button id="email-verification" type="submit" asp-page-handler="SendVerificationEmail" class="btn btn-link">Send verification email</button> *@
|
||||
}
|
||||
<input asp-for="Input.Email" class="form-control"/>
|
||||
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Input.PhoneNumber"></label>
|
||||
<input asp-for="Input.PhoneNumber" class="form-control" />
|
||||
<span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
|
||||
<label asp-for="Input.AllowBasicAuth"></label>
|
||||
<input asp-for="Input.AllowBasicAuth" class="form-check"/>
|
||||
<span asp-validation-for="Input.AllowBasicAuth" class="text-danger"></span>
|
||||
</div>
|
||||
<button id="update-profile-button" type="submit" class="btn btn-primary">Save</button>
|
||||
</form>
|
||||
@ -42,5 +31,5 @@
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
<partial name="_ValidationScriptsPartial"/>
|
||||
}
|
||||
@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using BtcTransmuter.Data.Entities;
|
||||
using BtcTransmuter.Data.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -16,37 +17,27 @@ namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
private readonly UserManager<User> _userManager;
|
||||
private readonly SignInManager<User> _signInManager;
|
||||
private readonly IEmailSender _emailSender;
|
||||
|
||||
public IndexModel(
|
||||
UserManager<User> userManager,
|
||||
SignInManager<User> signInManager,
|
||||
IEmailSender emailSender)
|
||||
SignInManager<User> signInManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
_emailSender = emailSender;
|
||||
}
|
||||
|
||||
public string Username { get; set; }
|
||||
|
||||
public bool IsEmailConfirmed { get; set; }
|
||||
[TempData] public string StatusMessage { get; set; }
|
||||
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
[BindProperty] public InputModel Input { get; set; }
|
||||
|
||||
public class InputModel
|
||||
{
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
[Required] [EmailAddress] public string Email { get; set; }
|
||||
|
||||
[Phone]
|
||||
[Display(Name = "Phone number")]
|
||||
public string PhoneNumber { get; set; }
|
||||
[Display(Name = "Allow Basic Auth using this account")]
|
||||
public bool AllowBasicAuth { get; set; }
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnGetAsync()
|
||||
@ -59,18 +50,15 @@ namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
|
||||
var userName = await _userManager.GetUserNameAsync(user);
|
||||
var email = await _userManager.GetEmailAsync(user);
|
||||
var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
|
||||
|
||||
Username = userName;
|
||||
|
||||
Input = new InputModel
|
||||
{
|
||||
Email = email,
|
||||
PhoneNumber = phoneNumber
|
||||
AllowBasicAuth = user.Get<UserBlob>().BasicAuth
|
||||
};
|
||||
|
||||
IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user);
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
@ -82,6 +70,7 @@ namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
}
|
||||
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
var blob = user.Get<UserBlob>();
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
@ -94,18 +83,27 @@ namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
if (!setEmailResult.Succeeded)
|
||||
{
|
||||
var userId = await _userManager.GetUserIdAsync(user);
|
||||
throw new InvalidOperationException($"Unexpected error occurred setting email for user with ID '{userId}'.");
|
||||
throw new InvalidOperationException(
|
||||
$"Unexpected error occurred setting email for user with ID '{userId}'.");
|
||||
}
|
||||
}
|
||||
|
||||
var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
|
||||
if (Input.PhoneNumber != phoneNumber)
|
||||
var blobChanged = false;
|
||||
if (Input.AllowBasicAuth != blob.BasicAuth)
|
||||
{
|
||||
var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber);
|
||||
if (!setPhoneResult.Succeeded)
|
||||
blob.BasicAuth = Input.AllowBasicAuth;
|
||||
blobChanged = true;
|
||||
}
|
||||
|
||||
if (blobChanged)
|
||||
{
|
||||
user.Set(blob);
|
||||
var updated = await _userManager.UpdateAsync(user);
|
||||
if (!updated.Succeeded)
|
||||
{
|
||||
var userId = await _userManager.GetUserIdAsync(user);
|
||||
throw new InvalidOperationException($"Unexpected error occurred setting phone number for user with ID '{userId}'.");
|
||||
throw new InvalidOperationException(
|
||||
$"Unexpected error occurred setting data for user with ID '{userId}'.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,36 +111,5 @@ namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
StatusMessage = "Your profile has been updated";
|
||||
return RedirectToPage();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostSendVerificationEmailAsync()
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return Page();
|
||||
}
|
||||
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
|
||||
var userId = await _userManager.GetUserIdAsync(user);
|
||||
var email = await _userManager.GetEmailAsync(user);
|
||||
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
var callbackUrl = Url.Page(
|
||||
"/Account/ConfirmEmail",
|
||||
pageHandler: null,
|
||||
values: new { userId = userId, code = code },
|
||||
protocol: Request.Scheme);
|
||||
await _emailSender.SendEmailAsync(
|
||||
email,
|
||||
"Confirm your email",
|
||||
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
|
||||
|
||||
StatusMessage = "Verification email sent. Please check your email.";
|
||||
return RedirectToPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
public static string Index => "Index";
|
||||
|
||||
public static string ChangePassword => "ChangePassword";
|
||||
public static string BTCPayAccountLink => "BTCPayAccountLink";
|
||||
|
||||
public static string ExternalLogins => "ExternalLogins";
|
||||
|
||||
@ -24,6 +25,7 @@ namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData);
|
||||
|
||||
public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication);
|
||||
public static string BTCPayAccountLinkNavClass(ViewContext viewContext) => PageNavClass(viewContext, BTCPayAccountLink);
|
||||
|
||||
private static string PageNavClass(ViewContext viewContext, string page)
|
||||
{
|
||||
|
||||
@ -13,9 +13,6 @@
|
||||
<p>
|
||||
<strong>Deleting this data will permanently remove your account, and this cannot be recovered.</strong>
|
||||
</p>
|
||||
<form id="download-data" asp-page="DownloadPersonalData" method="post" class="form-group">
|
||||
<button class="btn btn-primary" type="submit">Download</button>
|
||||
</form>
|
||||
<p>
|
||||
<a id="delete" asp-page="DeletePersonalData" class="btn btn-primary">Delete</a>
|
||||
</p>
|
||||
|
||||
@ -3,21 +3,17 @@ using BtcTransmuter.Data.Entities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BtcTransmuter.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public class PersonalDataModel : PageModel
|
||||
{
|
||||
private readonly UserManager<User> _userManager;
|
||||
private readonly ILogger<PersonalDataModel> _logger;
|
||||
|
||||
public PersonalDataModel(
|
||||
UserManager<User> userManager,
|
||||
ILogger<PersonalDataModel> logger)
|
||||
UserManager<User> userManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnGet()
|
||||
|
||||
@ -1,12 +1,22 @@
|
||||
@using BtcTransmuter.Data.Entities
|
||||
@using Microsoft.AspNetCore.Identity
|
||||
@inject SignInManager<User> SignInManager
|
||||
@inject IBtcTransmuterOptions BtcTransmuterOptions;
|
||||
@{
|
||||
var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any();
|
||||
}
|
||||
<ul class="nav nav-pills flex-column">
|
||||
<li class="nav-item"><a class="nav-link @ManageNavPages.IndexNavClass(ViewContext)" id="profile" asp-page="./Index">Profile</a></li>
|
||||
<li class="nav-item"><a class="nav-link @ManageNavPages.ChangePasswordNavClass(ViewContext)" id="change-password" asp-page="./ChangePassword">Password</a></li>
|
||||
@if (!BtcTransmuterOptions.DisableInternalAuth)
|
||||
{
|
||||
<li class="nav-item"><a class="nav-link @ManageNavPages.ChangePasswordNavClass(ViewContext)" id="change-password" asp-page="./ChangePassword">Password</a></li>
|
||||
}
|
||||
@if (BtcTransmuterOptions.BTCPayAuthServer != null)
|
||||
{
|
||||
<li class="nav-item"><a class="nav-link @ManageNavPages.BTCPayAccountLinkNavClass(ViewContext)" id="btcpay-account-link" asp-page="./BTCPayAccountLink">BTCPay account link</a></li>
|
||||
|
||||
}
|
||||
|
||||
@if (hasExternalLogins)
|
||||
{
|
||||
<li id="external-logins" class="nav-item"><a id="external-login" class="nav-link @ManageNavPages.ExternalLoginsNavClass(ViewContext)" asp-page="./ExternalLogins">External logins</a></li>
|
||||
|
||||
@ -4,13 +4,11 @@
|
||||
ViewData["Title"] = "Register";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<h1>Create a new account</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<form asp-route-returnUrl="@Model.ReturnUrl" method="post">
|
||||
<h4>Create a new account.</h4>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Input.Email"></label>
|
||||
|
||||
15
BtcTransmuter/Auth/BasicAuthenticationExtensions.cs
Normal file
15
BtcTransmuter/Auth/BasicAuthenticationExtensions.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
|
||||
namespace BtcTransmuter.Auth
|
||||
{
|
||||
public static class BasicAuthenticationExtensions
|
||||
{
|
||||
public static AuthenticationBuilder AddBasicAuth(this AuthenticationBuilder builder)
|
||||
{
|
||||
return builder.AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>(
|
||||
nameof(AuthenticationSchemes.Basic),
|
||||
o => { });
|
||||
}
|
||||
}
|
||||
}
|
||||
76
BtcTransmuter/Auth/BasicAuthenticationHandler.cs
Normal file
76
BtcTransmuter/Auth/BasicAuthenticationHandler.cs
Normal file
@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using BtcTransmuter.Abstractions.Extensions;
|
||||
using BtcTransmuter.Data.Entities;
|
||||
using BtcTransmuter.Data.Models;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace BtcTransmuter.Auth
|
||||
{
|
||||
// ReSharper disable once ClassNeverInstantiated.Global
|
||||
public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
|
||||
{
|
||||
private readonly IOptionsMonitor<IdentityOptions> _identityOptions;
|
||||
private readonly SignInManager<User> _signInManager;
|
||||
private readonly UserManager<User> _userManager;
|
||||
|
||||
public BasicAuthenticationHandler(
|
||||
IOptionsMonitor<IdentityOptions> identityOptions,
|
||||
IOptionsMonitor<BasicAuthenticationOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
ISystemClock clock,
|
||||
SignInManager<User> signInManager,
|
||||
UserManager<User> userManager) : base(options, logger, encoder, clock)
|
||||
{
|
||||
_identityOptions = identityOptions;
|
||||
_signInManager = signInManager;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
string authHeader = Context.Request.Headers["Authorization"];
|
||||
|
||||
if (authHeader == null || !authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase))
|
||||
return AuthenticateResult.NoResult();
|
||||
var encodedUsernamePassword = authHeader.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries)[1]?.Trim();
|
||||
var decodedUsernamePassword =
|
||||
Encoding.UTF8.GetString(Convert.FromBase64String(encodedUsernamePassword)).Split(':');
|
||||
var username = decodedUsernamePassword[0];
|
||||
var password = decodedUsernamePassword[1];
|
||||
|
||||
var result = await _signInManager.PasswordSignInAsync(username, password, true, true);
|
||||
if (!result.Succeeded)
|
||||
return AuthenticateResult.Fail(result.ToString());
|
||||
|
||||
var user = await _userManager.FindByNameAsync(username);
|
||||
if (!user.Get<UserBlob>().BasicAuth)
|
||||
{
|
||||
return AuthenticateResult.Fail("This user does not have basic auth enabled.");
|
||||
}
|
||||
|
||||
var claims = new List<Claim>()
|
||||
{
|
||||
new Claim(_identityOptions.CurrentValue.ClaimsIdentity.UserIdClaimType, user.Id)
|
||||
};
|
||||
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
claims.AddRange(roles.Select(s => new Claim(_identityOptions.CurrentValue.ClaimsIdentity.RoleClaimType, s) ));
|
||||
Context.SetIsApi(true);
|
||||
return AuthenticateResult.Success(new AuthenticationTicket(
|
||||
new ClaimsPrincipal(new ClaimsIdentity(claims, nameof(AuthenticationSchemes.Basic))), nameof(AuthenticationSchemes.Basic)));
|
||||
}
|
||||
}
|
||||
}
|
||||
8
BtcTransmuter/Auth/BasicAuthenticationOptions.cs
Normal file
8
BtcTransmuter/Auth/BasicAuthenticationOptions.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
|
||||
namespace BtcTransmuter.Auth
|
||||
{
|
||||
public class BasicAuthenticationOptions : AuthenticationSchemeOptions
|
||||
{
|
||||
}
|
||||
}
|
||||
16
BtcTransmuter/Auth/TransmuterSchemes.cs
Normal file
16
BtcTransmuter/Auth/TransmuterSchemes.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Server.HttpSys;
|
||||
|
||||
namespace BtcTransmuter.Auth
|
||||
{
|
||||
public class TransmuterSchemes
|
||||
{
|
||||
public const string AllSchemes = Local + "," + API;
|
||||
|
||||
public const string API = Basic;
|
||||
public const string Basic = nameof(AuthenticationSchemes.Basic);
|
||||
|
||||
//IdentityConstants.ApplicationScheme
|
||||
public const string Local = "Identity.Application" + "," + CookieAuthenticationDefaults.AuthenticationScheme;
|
||||
}
|
||||
}
|
||||
230
BtcTransmuter/BTCPayAuthService.cs
Normal file
230
BtcTransmuter/BTCPayAuthService.cs
Normal file
@ -0,0 +1,230 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BtcTransmuter.Areas.Identity.Pages.Account;
|
||||
using BtcTransmuter.Data.Entities;
|
||||
using BtcTransmuter.Data.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
||||
namespace BtcTransmuter
|
||||
{
|
||||
public class BTCPayAuthService
|
||||
{
|
||||
private readonly UserManager<User> _userManager;
|
||||
private readonly IBtcTransmuterOptions _btcTransmuterOptions;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly ILogger<BTCPayAuthService> _logger;
|
||||
|
||||
public BTCPayAuthService(
|
||||
UserManager<User> userManager, IBtcTransmuterOptions btcTransmuterOptions,
|
||||
IHttpClientFactory httpClientFactory, ILogger<BTCPayAuthService> logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_btcTransmuterOptions = btcTransmuterOptions;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<User> LoginAndRegisterIfNeeded(string user, string pass)
|
||||
{
|
||||
if (_btcTransmuterOptions.BTCPayAuthServer is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var response = await BasicAuthLogin(user, pass);
|
||||
if (response == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var matchedUser = await FindUserByBTCPayUserId(response.Id);
|
||||
if (matchedUser == null)
|
||||
{
|
||||
var key = await GenerateKey(user, pass);
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//create account
|
||||
matchedUser = new User()
|
||||
{
|
||||
Email = response.Email,
|
||||
Id = response.Id,
|
||||
UserName = response.Email,
|
||||
};
|
||||
matchedUser.Set(new UserBlob()
|
||||
{
|
||||
BTCPayAuthDetails = new BTCPayAuthDetails()
|
||||
{
|
||||
UserId = response.Id,
|
||||
AccessToken = key
|
||||
}
|
||||
});
|
||||
if ((await _userManager.CreateAsync(matchedUser)).Succeeded)
|
||||
{
|
||||
|
||||
if (response.Roles.Contains("ServerAdmin") || await _userManager.Users.CountAsync() == 1)
|
||||
{
|
||||
await _userManager.AddToRoleAsync(matchedUser, "Admin");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var tokenResponse = await CheckToken(matchedUser);
|
||||
|
||||
if (!(tokenResponse?.ToString()?.Equals(response.ToString()) is true) &&
|
||||
await GenerateKeyAndSet(user, pass, matchedUser))
|
||||
{
|
||||
await _userManager.UpdateAsync(matchedUser);
|
||||
}
|
||||
else if (!(tokenResponse?.ToString()?.Equals(response.ToString()) is true))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (response.Roles.Contains("ServerAdmin"))
|
||||
{
|
||||
await _userManager.AddToRoleAsync(matchedUser, "Admin");
|
||||
}
|
||||
else if(!await _userManager.HasPasswordAsync(matchedUser))
|
||||
{
|
||||
await _userManager.RemoveFromRoleAsync(matchedUser, "Admin");
|
||||
}
|
||||
|
||||
return matchedUser;
|
||||
}
|
||||
|
||||
private async Task<bool> GenerateKeyAndSet(string user, string pass, User matchedUser)
|
||||
{
|
||||
var key = await GenerateKey(user, pass);
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var blob = matchedUser.Get<UserBlob>();
|
||||
blob.BTCPayAuthDetails.AccessToken = key;
|
||||
if (_btcTransmuterOptions.DisableInternalAuth || !await _userManager.HasPasswordAsync(matchedUser))
|
||||
{
|
||||
matchedUser.Email = user;
|
||||
matchedUser.UserName = user;
|
||||
}
|
||||
|
||||
matchedUser.Set(blob);
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<string> GenerateKey(string user, string pass)
|
||||
{
|
||||
var client = _httpClientFactory.CreateClient("BTCPayAuthServer");
|
||||
var request = new HttpRequestMessage(HttpMethod.Post,
|
||||
new Uri(_btcTransmuterOptions.BTCPayAuthServer, "/api/v1/api-keys"));
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Basic",
|
||||
Convert.ToBase64String(Encoding.UTF8.GetBytes($"{user}:{pass}")));
|
||||
request.Content = new StringContent(JsonConvert.SerializeObject(new
|
||||
{
|
||||
label = "transmuter login access token",
|
||||
permissions = new[] {"btcpay.user.canmodifyprofile"}
|
||||
}), Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var accessTokenResponse =
|
||||
JsonConvert.DeserializeObject<JObject>((await response.Content.ReadAsStringAsync()));
|
||||
return accessTokenResponse["apiKey"].Value<string>();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<User> FindUserByBTCPayUserId(string userId)
|
||||
{
|
||||
return _userManager.Users.AsEnumerable().SingleOrDefault(user =>
|
||||
user.Get<UserBlob>().BTCPayAuthDetails.UserId == userId);
|
||||
}
|
||||
|
||||
public async Task<GetCurrentUserResponse> BasicAuthLogin(string user, string pass)
|
||||
{
|
||||
if (_btcTransmuterOptions.BTCPayAuthServer is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var client = _httpClientFactory.CreateClient("BTCPayAuthServer");
|
||||
var fetchUserId = new Uri(_btcTransmuterOptions.BTCPayAuthServer, "api/v1/users/me");
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, fetchUserId);
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Basic",
|
||||
Convert.ToBase64String(Encoding.UTF8.GetBytes($"{user}:{pass}")));
|
||||
var response = await client.SendAsync(request);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<GetCurrentUserResponse>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "error while attempting to authenticate with btcpay");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<GetCurrentUserResponse> CheckToken(User user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var blob = user.Get<UserBlob>();
|
||||
return await CheckToken(blob.BTCPayAuthDetails.AccessToken);
|
||||
}
|
||||
|
||||
public async Task<GetCurrentUserResponse> CheckToken(string token)
|
||||
{
|
||||
if (_btcTransmuterOptions.BTCPayAuthServer is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var client = _httpClientFactory.CreateClient("BTCPayAuthServer");
|
||||
|
||||
if (string.IsNullOrEmpty(token))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var fetchUserId = new Uri(_btcTransmuterOptions.BTCPayAuthServer, "api/v1/users/me");
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, fetchUserId);
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("token", token);
|
||||
var response = await client.SendAsync(request);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject<GetCurrentUserResponse>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,9 +6,9 @@
|
||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||
<UserSecretsId>aspnet-BtcTransmuter-065A2226-96CE-4371-A96D-97EFFCF0B153</UserSecretsId>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<Version>0.0.49</Version>
|
||||
<Version>0.0.59</Version>
|
||||
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
|
||||
<PackageVersion>0.0.49</PackageVersion>
|
||||
<PackageVersion>0.0.59</PackageVersion>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
@ -17,12 +17,13 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.1" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.5" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using IHostingEnvironment = Microsoft.AspNetCore.Hosting.IHostingEnvironment;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace BtcTransmuter
|
||||
@ -15,11 +15,19 @@ namespace BtcTransmuter
|
||||
{
|
||||
|
||||
RootPath = configuration.GetValue("RootPath", "");
|
||||
|
||||
BTCPayAuthServer = configuration.GetValue<Uri>("BTCPayAuthServer", null);
|
||||
DatabaseConnectionString = configuration.GetValue<string>("Database");
|
||||
DataProtectionDir = configuration.GetValue<string>("DataProtectionDir");
|
||||
DataProtectionApplicationName = configuration.GetValue<string>("DataProtectionApplicationName");
|
||||
DatabaseType = configuration.GetValue<DatabaseType>("DatabaseType", DatabaseType.Sqlite);
|
||||
UseDatabaseColumnEncryption = configuration.GetValue<bool>("UseDatabaseColumnEncryption", false);
|
||||
DisableInternalAuth = configuration.GetValue<bool>("DisableInternalAuth", false);
|
||||
if (DisableInternalAuth && BTCPayAuthServer == null)
|
||||
{
|
||||
DisableInternalAuth = false;
|
||||
logger.LogWarning($"Cannot disable internal auth while not setting BTCPayAuthServer");
|
||||
}
|
||||
ExtensionsDir = configuration.GetValue<string>("ExtensionsDir",
|
||||
Path.Combine(hostingEnvironment.ContentRootPath, "Extensions"));
|
||||
|
||||
@ -49,5 +57,9 @@ namespace BtcTransmuter
|
||||
|
||||
public DatabaseType DatabaseType { get; set; }
|
||||
public bool UseDatabaseColumnEncryption { get; set; }
|
||||
public bool DisableInternalAuth { get; set; }
|
||||
|
||||
public Uri BTCPayAuthServer { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using System.Threading.Tasks;
|
||||
using BtcTransmuter.Abstractions.Recipes;
|
||||
using BtcTransmuter.Abstractions.Settings;
|
||||
using BtcTransmuter.Auth;
|
||||
using BtcTransmuter.Data.Entities;
|
||||
using BtcTransmuter.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -10,7 +11,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace BtcTransmuter.Controllers
|
||||
{
|
||||
[Authorize(Roles = "Admin")]
|
||||
[Authorize(Roles = "Admin", AuthenticationSchemes = TransmuterSchemes.Local)]
|
||||
[Route("[controller]")]
|
||||
public class AdminController : Controller
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user