mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-23 14:40:36 +01:00
switch to a long for device identifier
This commit is contained in:
parent
c0ad32eaf7
commit
7bdb136c27
4 changed files with 70 additions and 55 deletions
|
@ -27,7 +27,7 @@ public interface IBTCPayAppHubClient
|
|||
//methods available on the hub in the server
|
||||
public interface IBTCPayAppHubServer
|
||||
{
|
||||
Task<bool> DeviceMasterSignal(string deviceIdentifier, bool active);
|
||||
Task<bool> DeviceMasterSignal(long deviceIdentifier, bool active);
|
||||
|
||||
Task<Dictionary<string,string>> Pair(PairRequest request);
|
||||
Task<AppHandshakeResponse> Handshake(AppHandshake request);
|
||||
|
|
|
@ -15,6 +15,8 @@ using Microsoft.AspNetCore.Authorization;
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using VSSProto;
|
||||
|
||||
namespace BTCPayServer.App.API;
|
||||
|
@ -29,20 +31,21 @@ public class VSSController : Controller, IVSSAPI
|
|||
private readonly ApplicationDbContextFactory _dbContextFactory;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly BTCPayAppState _appState;
|
||||
private readonly ILogger<VSSController> _logger;
|
||||
|
||||
public VSSController(ApplicationDbContextFactory dbContextFactory,
|
||||
UserManager<ApplicationUser> userManager, BTCPayAppState appState)
|
||||
UserManager<ApplicationUser> userManager, BTCPayAppState appState, ILogger<VSSController> logger)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_userManager = userManager;
|
||||
_appState = appState;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpPost(HttpVSSAPIClient.GET_OBJECT)]
|
||||
[MediaTypeConstraint("application/octet-stream")]
|
||||
public async Task<GetObjectResponse> GetObjectAsync(GetObjectRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
var userId = _userManager.GetUserId(User);
|
||||
await using var dbContext = _dbContextFactory.CreateContext();
|
||||
var store = await dbContext.AppStorageItems.SingleOrDefaultAsync(data =>
|
||||
|
@ -74,22 +77,20 @@ public class VSSController : Controller, IVSSAPI
|
|||
|
||||
private bool VerifyGlobalVersion(long globalVersion)
|
||||
{
|
||||
|
||||
var userId = _userManager.GetUserId(User);
|
||||
var conn = _appState.Connections.SingleOrDefault(pair => pair.Value.Master && pair.Value.UserId == userId);
|
||||
if (conn.Key == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// This has a high collision rate, but we're not expecting something insane here since we have auth and other checks in place.
|
||||
return globalVersion == conn.Value.DeviceIdentifier.GetHashCode();
|
||||
|
||||
return globalVersion == conn.Value.DeviceIdentifier;
|
||||
}
|
||||
|
||||
[HttpPost(HttpVSSAPIClient.PUT_OBJECTS)]
|
||||
[MediaTypeConstraint("application/octet-stream")]
|
||||
public async Task<PutObjectResponse> PutObjectAsync(PutObjectRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
if (!VerifyGlobalVersion(request.GlobalVersion))
|
||||
return SetResult<PutObjectResponse>(BadRequest(new ErrorResponse()
|
||||
{
|
||||
|
@ -99,7 +100,8 @@ public class VSSController : Controller, IVSSAPI
|
|||
var userId = _userManager.GetUserId(User);
|
||||
|
||||
await using var dbContext = _dbContextFactory.CreateContext();
|
||||
|
||||
return await dbContext.Database.CreateExecutionStrategy().ExecuteAsync(async async =>
|
||||
{
|
||||
await using var dbContextTransaction = await dbContext.Database.BeginTransactionAsync(cancellationToken);
|
||||
try
|
||||
{
|
||||
|
@ -122,31 +124,32 @@ public class VSSController : Controller, IVSSAPI
|
|||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
await dbContextTransaction.CommitAsync(cancellationToken);
|
||||
|
||||
return new PutObjectResponse();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error while processing transaction");
|
||||
await dbContextTransaction.RollbackAsync(cancellationToken);
|
||||
return SetResult<PutObjectResponse>(BadRequest(new ErrorResponse()
|
||||
{
|
||||
ErrorCode = ErrorCode.ConflictException, Message = e.Message
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
return new PutObjectResponse();
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
[HttpPost(HttpVSSAPIClient.DELETE_OBJECT)]
|
||||
[MediaTypeConstraint("application/octet-stream")]
|
||||
public async Task<DeleteObjectResponse> DeleteObjectAsync(DeleteObjectRequest request, CancellationToken cancellationToken)
|
||||
public async Task<DeleteObjectResponse> DeleteObjectAsync(DeleteObjectRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
|
||||
var userId = _userManager.GetUserId(User);
|
||||
await using var dbContext = _dbContextFactory.CreateContext();
|
||||
var store = await dbContext.AppStorageItems
|
||||
.Where(data => data.Key == request.KeyValue.Key && data.UserId == userId &&
|
||||
data.Version == request.KeyValue.Version).ExecuteDeleteAsync(cancellationToken: cancellationToken);
|
||||
data.Version == request.KeyValue.Version)
|
||||
.ExecuteDeleteAsync(cancellationToken: cancellationToken);
|
||||
return store == 0
|
||||
? SetResult<DeleteObjectResponse>(
|
||||
new NotFoundObjectResult(new ErrorResponse()
|
||||
|
@ -157,13 +160,15 @@ public class VSSController : Controller, IVSSAPI
|
|||
}
|
||||
|
||||
[HttpPost(HttpVSSAPIClient.LIST_KEY_VERSIONS)]
|
||||
public async Task<ListKeyVersionsResponse> ListKeyVersionsAsync(ListKeyVersionsRequest request, CancellationToken cancellationToken)
|
||||
public async Task<ListKeyVersionsResponse> ListKeyVersionsAsync(ListKeyVersionsRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var userId = _userManager.GetUserId(User);
|
||||
await using var dbContext = _dbContextFactory.CreateContext();
|
||||
var items = await dbContext.AppStorageItems
|
||||
.Where(data => data.UserId == userId)
|
||||
.Select(data => new KeyValue() {Key = data.Key, Version = data.Version}).ToListAsync(cancellationToken: cancellationToken);
|
||||
.Select(data => new KeyValue() {Key = data.Key, Version = data.Version})
|
||||
.ToListAsync(cancellationToken: cancellationToken);
|
||||
return new ListKeyVersionsResponse {KeyVersions = {items}};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -300,7 +300,7 @@ public class BTCPayAppHub : Hub<IBTCPayAppHubClient>, IBTCPayAppHubServer
|
|||
await _appState.InvoiceUpdate(identifier, lightningInvoice);
|
||||
}
|
||||
|
||||
public async Task<bool> DeviceMasterSignal(string deviceIdentifier, bool active)
|
||||
public async Task<bool> DeviceMasterSignal(long deviceIdentifier, bool active)
|
||||
{
|
||||
return await _appState.DeviceMasterSignal(Context.ConnectionId,deviceIdentifier,active);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace BTCPayServer.Controllers;
|
|||
|
||||
public record ConnectedInstance(
|
||||
string UserId,
|
||||
string DeviceIdentifier,
|
||||
long? DeviceIdentifier,
|
||||
bool Master,
|
||||
// string ProvidedAccessKey,
|
||||
HashSet<string> Groups);
|
||||
|
@ -45,7 +45,7 @@ public class BTCPayAppState : IHostedService
|
|||
private DerivationSchemeParser _derivationSchemeParser;
|
||||
|
||||
|
||||
public ConcurrentDictionary<string, ConnectedInstance> Connections { get; set; }
|
||||
public ConcurrentDictionary<string, ConnectedInstance> Connections { get; set; } = new();
|
||||
|
||||
|
||||
private CancellationTokenSource? _cts;
|
||||
|
@ -266,24 +266,29 @@ public class BTCPayAppState : IHostedService
|
|||
}
|
||||
|
||||
|
||||
public async Task<bool> DeviceMasterSignal(string contextConnectionId, string deviceIdentifier, bool active)
|
||||
public async Task<bool> DeviceMasterSignal(string contextConnectionId, long deviceIdentifier, bool active)
|
||||
{
|
||||
|
||||
if (!Connections.TryGetValue(contextConnectionId, out var connectedInstance))
|
||||
{
|
||||
_logger.LogWarning("DeviceMasterSignal called on non existing connection");
|
||||
return false;
|
||||
}
|
||||
if(!string.IsNullOrEmpty(connectedInstance.DeviceIdentifier) && connectedInstance.DeviceIdentifier != deviceIdentifier)
|
||||
if(connectedInstance.DeviceIdentifier != null && connectedInstance.DeviceIdentifier != deviceIdentifier)
|
||||
{
|
||||
_logger.LogWarning("DeviceMasterSignal called with different device identifier");
|
||||
return false;
|
||||
}
|
||||
if(string.IsNullOrEmpty(connectedInstance.DeviceIdentifier))
|
||||
if(connectedInstance.DeviceIdentifier == null)
|
||||
{
|
||||
Connections[contextConnectionId] = connectedInstance with {DeviceIdentifier = deviceIdentifier};
|
||||
_logger.LogInformation("DeviceMasterSignal called with device identifier {deviceIdentifier}", deviceIdentifier);
|
||||
connectedInstance = connectedInstance with {DeviceIdentifier = deviceIdentifier};
|
||||
Connections[contextConnectionId] = connectedInstance;
|
||||
}
|
||||
|
||||
if(connectedInstance.Master == active)
|
||||
{
|
||||
_logger.LogInformation("DeviceMasterSignal called with same active state");
|
||||
return true;
|
||||
}
|
||||
else if (active)
|
||||
|
@ -291,17 +296,22 @@ public class BTCPayAppState : IHostedService
|
|||
//check if there is any other master connection with the same user id
|
||||
if (Connections.Values.Any(c => c.UserId == connectedInstance.UserId && c.Master))
|
||||
{
|
||||
_logger.LogWarning("DeviceMasterSignal called with active state but there is already a master connection");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Connections[contextConnectionId] = connectedInstance with {Master = true};
|
||||
_logger.LogInformation("DeviceMasterSignal called with active state");
|
||||
connectedInstance = connectedInstance with {Master = true};
|
||||
Connections[contextConnectionId] = connectedInstance;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Connections[contextConnectionId] = connectedInstance with {Master = false};
|
||||
_logger.LogInformation("DeviceMasterSignal called with inactive state");
|
||||
connectedInstance = connectedInstance with {Master = false};
|
||||
Connections[contextConnectionId] = connectedInstance;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
@ -319,7 +329,7 @@ public class BTCPayAppState : IHostedService
|
|||
|
||||
public async Task Connected(string contextConnectionId, string userId)
|
||||
{
|
||||
Connections.TryAdd(contextConnectionId, new ConnectedInstance(userId, string.Empty, false, new HashSet<string>()));
|
||||
Connections.TryAdd(contextConnectionId, new ConnectedInstance(userId, null, false, new HashSet<string>()));
|
||||
|
||||
if (_nodeInfo.Length > 0)
|
||||
await _hubContext.Clients.Client(contextConnectionId).NotifyServerNode(_nodeInfo);
|
||||
|
|
Loading…
Add table
Reference in a new issue