using DBreeze; using NBitcoin; using NBitcoin.DataEncoders; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace BTCPayServer.Authentication { public class TokenRepository { public TokenRepository(DBreezeEngine engine) { _Engine = engine; } private readonly DBreezeEngine _Engine; public DBreezeEngine Engine { get { return _Engine; } } public Task GetTokens(string sin) { List tokens = new List(); using(var tx = _Engine.GetTransaction()) { tx.ValuesLazyLoadingIsOn = false; foreach(var row in tx.SelectForward($"T_{sin}")) { var token = ToObject(row.Value); tokens.Add(token); } } return Task.FromResult(tokens.ToArray()); } public Task CreateToken(string sin, string tokenName) { var token = new BitTokenEntity { Name = tokenName, Value = Encoders.Base58.EncodeData(RandomUtils.GetBytes(32)), DateCreated = DateTimeOffset.UtcNow }; using(var tx = _Engine.GetTransaction()) { tx.Insert($"T_{sin}", token.Name, ToBytes(token)); tx.Commit(); } return Task.FromResult(token); } public Task PairWithAsync(string pairingCode, string pairedId) { if(pairedId == null) throw new ArgumentNullException(nameof(pairedId)); using(var tx = _Engine.GetTransaction()) { var row = tx.Select("PairingCodes", pairingCode); if(row == null || !row.Exists) return Task.FromResult(false); tx.RemoveKey("PairingCodes", pairingCode); try { var pairingEntity = ToObject(row.Value); if(pairingEntity.IsExpired()) return Task.FromResult(false); row = tx.Select($"T_{pairingEntity.SIN}", pairingEntity.Facade); if(row == null || !row.Exists) return Task.FromResult(false); var token = ToObject(row.Value); if(token.Active) return Task.FromResult(false); token.Active = true; token.PairedId = pairedId; token.SIN = pairingEntity.SIN; token.Label = pairingEntity.Label; token.PairingTime = DateTimeOffset.UtcNow; tx.Insert($"TbP_{pairedId}", token.Value, ToBytes(token)); tx.Insert($"T_{pairingEntity.SIN}", pairingEntity.Facade, ToBytes(token)); } finally { tx.Commit(); } } return Task.FromResult(true); } public Task GetTokensByPairedIdAsync(string pairedId) { List tokens = new List(); using(var tx = _Engine.GetTransaction()) { tx.ValuesLazyLoadingIsOn = false; foreach(var row in tx.SelectForward($"TbP_{pairedId}")) { tokens.Add(ToObject(row.Value)); } } return Task.FromResult(tokens.ToArray()); } public Task GetPairingAsync(string pairingCode) { using(var tx = _Engine.GetTransaction()) { var row = tx.Select("PairingCodes", pairingCode); if(row == null || !row.Exists) return Task.FromResult(null); var pairingEntity = ToObject(row.Value); if(pairingEntity.IsExpired()) return Task.FromResult(null); return Task.FromResult(pairingEntity); } } public Task AddPairingCodeAsync(PairingCodeEntity pairingCodeEntity) { pairingCodeEntity = Clone(pairingCodeEntity); pairingCodeEntity.Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(6)); using(var tx = _Engine.GetTransaction()) { tx.Insert("PairingCodes", pairingCodeEntity.Id, ToBytes(pairingCodeEntity)); tx.Commit(); } return Task.FromResult(pairingCodeEntity); } private byte[] ToBytes(T obj) { return ZipUtils.Zip(JsonConvert.SerializeObject(obj)); } private T ToObject(byte[] value) { return JsonConvert.DeserializeObject(ZipUtils.Unzip(value)); } private T Clone(T obj) { return ToObject(ToBytes(obj)); } public async Task DeleteToken(string sin, string tokenName, string storeId) { var token = await GetToken(sin, tokenName); if(token == null || (token.PairedId != null && token.PairedId != storeId)) return false; using(var tx = _Engine.GetTransaction()) { tx.RemoveKey($"T_{sin}", tokenName); if(token.PairedId != null) tx.RemoveKey($"TbP_" + token.PairedId, token.Value); tx.Commit(); } return true; } public Task GetToken(string sin, string tokenName) { using(var tx = _Engine.GetTransaction()) { tx.ValuesLazyLoadingIsOn = true; var row = tx.Select($"T_{sin}", tokenName); if(row == null || !row.Exists) return Task.FromResult(null); var token = ToObject(row.Value); if(!token.Active) return Task.FromResult(null); return Task.FromResult(token); } } } }