Skip to content

Commit

Permalink
Store device id with secure config provider instead of in db
Browse files Browse the repository at this point in the history
  • Loading branch information
dennisreimann committed Feb 5, 2025
1 parent d536162 commit b096d83
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 25 deletions.
1 change: 0 additions & 1 deletion BTCPayApp.Core/Auth/AuthStateProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ public override async Task<AuthenticationState> GetAuthenticationStateAsync()
if (UserInfo.Stores?.Any() is true)
claims.AddRange(UserInfo.Stores.Select(store =>
new Claim(store.Id, string.Join(',', store.Permissions ?? []))));
// TODO: Improve distinguishing the device owner
if (hasOwnerToken && !hasUserToken)
claims.Add(new Claim(identityOptions.CurrentValue.ClaimsIdentity.RoleClaimType, "DeviceOwner"));
user = new ClaimsPrincipal(new ClaimsIdentity(claims, "Greenfield"));
Expand Down
31 changes: 20 additions & 11 deletions BTCPayApp.Core/BTCPayServer/BTCPayConnectionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class BTCPayConnectionManager(
ILogger<BTCPayConnectionManager> logger,
BTCPayAppServerClient btcPayAppServerClient,
IBTCPayAppHubClient btcPayAppServerClientInterface,
ISecureConfigProvider secureProvider,
ConfigProvider configProvider,
SyncService syncService)
: BaseHostedService(logger), IHubConnectionObserver
Expand Down Expand Up @@ -75,22 +76,27 @@ protected override async Task ExecuteStartAsync(CancellationToken cancellationTo
await OnConnectionChanged(this, (BTCPayConnectionState.Init, BTCPayConnectionState.Init));
}

private async Task OnMasterUpdated(object? sender, long? e)
private async Task OnMasterUpdated(object? sender, long? masterId)
{
await WrapInLock(async () =>
{
if (_cts.IsCancellationRequested)
return;
if (e is null && ConnectionState == BTCPayConnectionState.ConnectedAsSlave && !ForceSlaveMode)

var deviceId = await secureProvider.GetDeviceIdentifier();
if (masterId is null && ConnectionState == BTCPayConnectionState.ConnectedAsSlave && !ForceSlaveMode)
{
logger.LogInformation("OnMasterUpdated: Syncing slave {DeviceId}", deviceId);
ConnectionState = BTCPayConnectionState.Syncing;
}
else if (await configProvider.GetDeviceIdentifier() == e)
else if (deviceId == masterId)
{
logger.LogInformation("OnMasterUpdated: Setting master to {DeviceId}", deviceId);
ConnectionState = BTCPayConnectionState.ConnectedAsMaster;
}
else if (ConnectionState == BTCPayConnectionState.ConnectedAsMaster && e != await configProvider.GetDeviceIdentifier())
else if (ConnectionState == BTCPayConnectionState.ConnectedAsMaster && masterId != deviceId)
{
logger.LogInformation("OnMasterUpdated: New master {MasterId} - Device: {DeviceId}", masterId, deviceId);
ConnectionState = BTCPayConnectionState.Syncing;
}
}, _cts.Token);
Expand All @@ -111,7 +117,7 @@ await WrapInLock(async () =>

private async Task OnConnectionChanged(object? sender, (BTCPayConnectionState Old, BTCPayConnectionState New) e)
{
var deviceIdentifier = await configProvider.GetDeviceIdentifier();
var deviceIdentifier = await secureProvider.GetDeviceIdentifier();
var newState = e.New;
try
{
Expand Down Expand Up @@ -195,10 +201,12 @@ private async Task OnConnectionChanged(object? sender, (BTCPayConnectionState Ol
var masterDevice = await HubProxy!.GetCurrentMaster();
if (deviceIdentifier == masterDevice)
{
logger.LogInformation("Syncing master to remote: {DeviceId}", deviceIdentifier);
await syncService.SyncToRemote(CancellationToken.None);
}
else
{
logger.LogInformation("Syncing to local. Master: {MasterId} - Device: {DeviceId}", masterDevice, deviceIdentifier);
await syncService.SyncToLocal();
}
newState = BTCPayConnectionState.ConnectedFinishedInitialSync;
Expand Down Expand Up @@ -308,13 +316,13 @@ protected override async Task ExecuteStopAsync(CancellationToken cancellationTok
await _cts.CancelAsync();
if (_connectionState == BTCPayConnectionState.ConnectedAsMaster)
{
logger.LogInformation("Sending device master signal to turn off");
var deviceIdentifier = await configProvider.GetDeviceIdentifier();
var deviceId = await secureProvider.GetDeviceIdentifier();
logger.LogInformation("Sending device master signal to turn off {DeviceId}", deviceId);
await syncService.StopSync();
await syncService.SyncToRemote(CancellationToken.None);
if (HubProxy is not null)
{
await HubProxy.DeviceMasterSignal(deviceIdentifier, false);
await HubProxy.DeviceMasterSignal(deviceId, false);
}
}

Expand Down Expand Up @@ -356,10 +364,11 @@ public async Task SwitchToSlave()
if (_connectionState == BTCPayConnectionState.ConnectedAsMaster)
{
ForceSlaveMode = true;
logger.LogInformation("Sending device master signal to turn off");
var deviceId = await secureProvider.GetDeviceIdentifier();
logger.LogInformation("Sending device master signal to turn off {DeviceId}", deviceId);
await syncService.StopSync();
await syncService.SyncToRemote( CancellationToken.None);
await HubProxy!.DeviceMasterSignal(await configProvider.GetDeviceIdentifier(), false);
await syncService.SyncToRemote(CancellationToken.None);
await HubProxy!.DeviceMasterSignal(deviceId, false);
}
}
}
4 changes: 2 additions & 2 deletions BTCPayApp.Core/Backup/SyncService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public async Task<bool> SetEncryptionKey(string key)

await api.PutObjectAsync(new PutObjectRequest
{
GlobalVersion = await configProvider.GetDeviceIdentifier(),
GlobalVersion = await secureConfigProvider.GetDeviceIdentifier(),
TransactionItems =
{
new KeyValue
Expand Down Expand Up @@ -327,7 +327,7 @@ public async Task SyncToRemote(CancellationToken cancellationToken = default)

var putObjectRequest = new PutObjectRequest
{
GlobalVersion = await configProvider.GetDeviceIdentifier()
GlobalVersion = await secureConfigProvider.GetDeviceIdentifier()
};
var outbox = await db.OutboxItems.GroupBy(outbox1 => outbox1.Key)
.ToListAsync(cancellationToken: cancellationToken);
Expand Down
3 changes: 1 addition & 2 deletions BTCPayApp.Core/Contracts/ISecureConfigProvider.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
namespace BTCPayApp.Core.Contracts;

public interface ISecureConfigProvider

{
Task<T?> Get<T>(string key);
Task Set<T>(string key, T? value);
}
}
10 changes: 8 additions & 2 deletions BTCPayApp.Core/Helpers/ConfigExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ namespace BTCPayApp.Core.Helpers;
public static class ConfigExtensions
{
private const string ConfigDeviceIdentifierKey = "deviceIdentifier";
public static async Task<long> GetDeviceIdentifier(this ConfigProvider configProvider)
public static async Task<long> GetDeviceIdentifier(this ISecureConfigProvider configProvider)
{
return await configProvider.GetOrSet(ConfigDeviceIdentifierKey, () => Task.FromResult(RandomUtils.GetInt64()), false);
var id = await configProvider.Get<long>(ConfigDeviceIdentifierKey);
if (id == 0)
{
id = RandomUtils.GetInt64();
await configProvider.Set(ConfigDeviceIdentifierKey, id);
}
return id;
}
}
7 changes: 1 addition & 6 deletions BTCPayApp.Core/Wallet/OnChainWalletManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -287,13 +287,8 @@ public async Task AddDerivation(string key, string name, string? descriptor)
}
}

private async Task ConnectionChanged(object? sender,
(BTCPayConnectionState Old, BTCPayConnectionState New) valueTuple)
private async Task ConnectionChanged(object? sender, (BTCPayConnectionState Old, BTCPayConnectionState New) valueTuple)
{
// if (valueTuple.New is BTCPayConnectionState.ConnectedFinishedInitialSync)
// {
// WalletConfig = await _configProvider.Get<WalletConfig>(WalletConfig.Key);
// }
await DetermineState();
}

Expand Down
2 changes: 1 addition & 1 deletion BTCPayApp.Tests/HeadlessTestNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ private HeadlessTestNode(string nodeName, ITestOutputHelper testOutputHelper, st
collection.Replace(ServiceDescriptor.Singleton<ILoggerFactory, TestLoggerFactory>(provider => new TestLoggerFactory(nodeName, ActivatorUtilities.CreateInstance<LoggerFactory>(provider))));
// collection.Replace(ServiceDescriptor.Singleton<IDbContextFactory<AppDbContext>, TestDbContextFactory>());
collection.AddDataProtection(options => { options.ApplicationDiscriminator = "BTCPayApp"; });
collection.AddSingleton<ISecureConfigProvider, TestSecureConfigProvider>();
collection.AddSingleton<ISecureConfigProvider, DesktopSecureConfigProvider>();
collection.AddSingleton<IFingerprint, StubFingerprintProvider>();
collection.AddSingleton<IDataDirectoryProvider, DesktopDataDirectoryProvider>();
});
Expand Down

0 comments on commit b096d83

Please sign in to comment.