mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-03-03 09:29:10 +01:00
GreenField: Switch to Blob for API Keys
This commit is contained in:
parent
d60b00e8cd
commit
c6d75de3d7
11 changed files with 101 additions and 26 deletions
|
@ -123,14 +123,6 @@ namespace BTCPayServer.Client
|
|||
yield return pp;
|
||||
}
|
||||
}
|
||||
public static IEnumerable<Permission> ToPermissions(string permissionsFormatted)
|
||||
{
|
||||
foreach(var part in permissionsFormatted.Split(';', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (Permission.TryParse(part, out var p))
|
||||
yield return p;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ContainsPolicy(string subpolicy)
|
||||
{
|
||||
|
|
|
@ -22,13 +22,19 @@ namespace BTCPayServer.Data
|
|||
[MaxLength(50)] public string UserId { get; set; }
|
||||
|
||||
public APIKeyType Type { get; set; } = APIKeyType.Legacy;
|
||||
public string Permissions { get; set; }
|
||||
|
||||
public byte[] Blob { get; set; }
|
||||
public StoreData StoreData { get; set; }
|
||||
public ApplicationUser User { get; set; }
|
||||
public string Label { get; set; }
|
||||
}
|
||||
|
||||
public class APIKeyBlob
|
||||
{
|
||||
public string[] Permissions { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public enum APIKeyType
|
||||
{
|
||||
Legacy,
|
||||
|
|
42
BTCPayServer.Data/Migrations/20200402065615_AddApiKeyBlob.cs
Normal file
42
BTCPayServer.Data/Migrations/20200402065615_AddApiKeyBlob.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20200402065615_AddApiKeyBlob")]
|
||||
public partial class AddApiKeyBlob : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Permissions",
|
||||
table: "ApiKeys");
|
||||
}
|
||||
|
||||
migrationBuilder.AddColumn<byte[]>(
|
||||
name: "Blob",
|
||||
table: "ApiKeys",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (this.SupportDropColumn(migrationBuilder.ActiveProvider))
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Blob",
|
||||
table: "ApiKeys");
|
||||
}
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Permissions",
|
||||
table: "ApiKeys",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,10 +22,10 @@ namespace BTCPayServer.Migrations
|
|||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.Property<string>("Label")
|
||||
.HasColumnType("TEXT");
|
||||
b.Property<byte[]>("Blob")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Permissions")
|
||||
b.Property<string>("Label")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("StoreId")
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace BTCPayServer.Tests
|
|||
var superApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text;
|
||||
|
||||
//this api key has access to everything
|
||||
await TestApiAgainstAccessToken(superApiKey, tester, user, $"{Policies.CanModifyServerSettings};{Policies.CanModifyStoreSettings};{Policies.CanViewProfile}");
|
||||
await TestApiAgainstAccessToken(superApiKey, tester, user, Policies.CanModifyServerSettings,Policies.CanModifyStoreSettings, Policies.CanViewProfile);
|
||||
|
||||
|
||||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
|
@ -100,7 +100,7 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.FindElement(By.Id("AddApiKey")).Click();
|
||||
s.Driver.FindElement(By.Id("Generate")).Click();
|
||||
var noPermissionsApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text;
|
||||
await TestApiAgainstAccessToken(noPermissionsApiKey, tester, user, string.Empty);
|
||||
await TestApiAgainstAccessToken(noPermissionsApiKey, tester, user);
|
||||
|
||||
await Assert.ThrowsAnyAsync<HttpRequestException>(async () =>
|
||||
{
|
||||
|
@ -133,7 +133,7 @@ namespace BTCPayServer.Tests
|
|||
var apiKeyRepo = s.Server.PayTester.GetService<APIKeyRepository>();
|
||||
|
||||
await TestApiAgainstAccessToken(results.Single(pair => pair.Key == "key").Value, tester, user,
|
||||
(await apiKeyRepo.GetKey(results.Single(pair => pair.Key == "key").Value)).Permissions);
|
||||
(await apiKeyRepo.GetKey(results.Single(pair => pair.Key == "key").Value)).GetBlob().Permissions);
|
||||
|
||||
authUrl = BTCPayServerClient.GenerateAuthorizeUri(tester.PayTester.ServerUri,
|
||||
new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true).ToString();
|
||||
|
@ -154,15 +154,15 @@ namespace BTCPayServer.Tests
|
|||
.Select(s1 => new KeyValuePair<string, string>(s1.Split("=")[0], s1.Split("=")[1]));
|
||||
|
||||
await TestApiAgainstAccessToken(results.Single(pair => pair.Key == "key").Value, tester, user,
|
||||
(await apiKeyRepo.GetKey(results.Single(pair => pair.Key == "key").Value)).Permissions);
|
||||
(await apiKeyRepo.GetKey(results.Single(pair => pair.Key == "key").Value)).GetBlob().Permissions);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async Task TestApiAgainstAccessToken(string accessToken, ServerTester tester, TestAccount testAccount,
|
||||
string expectedPermissionsString)
|
||||
params string[] expectedPermissionsArr)
|
||||
{
|
||||
var expectedPermissions = Permission.ToPermissions(expectedPermissionsString).ToArray();
|
||||
var expectedPermissions = Permission.ToPermissions(expectedPermissionsArr).ToArray();
|
||||
expectedPermissions ??= new Permission[0];
|
||||
var apikeydata = await TestApiAgainstAccessToken<ApiKeyData>(accessToken, $"api/v1/api-keys/current", tester.PayTester.HttpClient);
|
||||
var permissions = apikeydata.Permissions;
|
||||
|
|
|
@ -50,7 +50,10 @@ namespace BTCPayServer.Controllers.GreenField
|
|||
UserId = _userManager.GetUserId(User),
|
||||
Label = request.Label
|
||||
};
|
||||
key.Permissions = string.Join(";", request.Permissions.Select(p => p.ToString()).Distinct().ToArray());
|
||||
key.SetBlob(new APIKeyBlob()
|
||||
{
|
||||
Permissions = request.Permissions.Select(p => p.ToString()).Distinct().ToArray()
|
||||
});
|
||||
await _apiKeyRepository.CreateKey(key);
|
||||
return Ok(FromModel(key));
|
||||
}
|
||||
|
@ -82,7 +85,7 @@ namespace BTCPayServer.Controllers.GreenField
|
|||
{
|
||||
return new ApiKeyData()
|
||||
{
|
||||
Permissions = Permission.ToPermissions(data.Permissions).ToArray(),
|
||||
Permissions = Permission.ToPermissions(data.GetBlob().Permissions).ToArray(),
|
||||
ApiKey = data.Id,
|
||||
Label = data.Label ?? string.Empty
|
||||
};
|
||||
|
|
|
@ -129,7 +129,7 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
}
|
||||
|
||||
var permissions = Permission.ToPermissions(viewModel.Permissions).ToHashSet();
|
||||
var permissions = Permission.ToPermissions(viewModel.Permissions.Split(';')).ToHashSet();
|
||||
if (permissions.Contains(Permission.Create(Policies.CanModifyStoreSettings)))
|
||||
{
|
||||
if (!viewModel.SelectiveStores &&
|
||||
|
@ -238,7 +238,10 @@ namespace BTCPayServer.Controllers
|
|||
UserId = _userManager.GetUserId(User),
|
||||
Label = viewModel.Label
|
||||
};
|
||||
key.Permissions = string.Join(";", GetPermissionsFromViewModel(viewModel).Select(p => p.ToString()).Distinct().ToArray());
|
||||
key.SetBlob(new APIKeyBlob()
|
||||
{
|
||||
Permissions = GetPermissionsFromViewModel(viewModel).Select(p => p.ToString()).Distinct().ToArray()
|
||||
});
|
||||
await _apiKeyRepository.CreateKey(key);
|
||||
return key;
|
||||
}
|
||||
|
|
26
BTCPayServer/Data/APIKeyDataExtensions.cs
Normal file
26
BTCPayServer/Data/APIKeyDataExtensions.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using NBXplorer;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public static class APIKeyDataExtensions
|
||||
{
|
||||
public static APIKeyBlob GetBlob(this APIKeyData apiKeyData)
|
||||
{
|
||||
var result = apiKeyData.Blob == null
|
||||
? new APIKeyBlob()
|
||||
: JObject.Parse(ZipUtils.Unzip(apiKeyData.Blob)).ToObject<APIKeyBlob>();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool SetBlob(this APIKeyData apiKeyData, APIKeyBlob blob)
|
||||
{
|
||||
var original = new Serializer(null).ToString(apiKeyData.GetBlob());
|
||||
var newBlob = new Serializer(null).ToString(blob);
|
||||
if (original == newBlob)
|
||||
return false;
|
||||
apiKeyData.Blob = ZipUtils.Zip(newBlob);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -52,7 +52,7 @@ namespace BTCPayServer.Security.GreenField
|
|||
|
||||
List<Claim> claims = new List<Claim>();
|
||||
claims.Add(new Claim(_identityOptions.CurrentValue.ClaimsIdentity.UserIdClaimType, key.UserId));
|
||||
claims.AddRange(Permission.ToPermissions(key.Permissions).Select(permission =>
|
||||
claims.AddRange(Permission.ToPermissions(key.GetBlob().Permissions).Select(permission =>
|
||||
new Claim(GreenFieldConstants.ClaimTypes.Permission, permission.ToString())));
|
||||
return AuthenticateResult.Success(new AuthenticationTicket(
|
||||
new ClaimsPrincipal(new ClaimsIdentity(claims, GreenFieldConstants.AuthenticationType)),
|
||||
|
|
|
@ -22,13 +22,16 @@
|
|||
<td>@keyData.Label</td>
|
||||
<td>@keyData.Id</td>
|
||||
<td>
|
||||
@if (string.IsNullOrEmpty(keyData.Permissions))
|
||||
@{
|
||||
var permissions = keyData.GetBlob().Permissions;
|
||||
}
|
||||
@if (!permissions.Any())
|
||||
{
|
||||
<span>No permissions</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@string.Join(", ", Permission.ToPermissions(keyData.Permissions).Select(c => c.ToString()).Distinct().ToArray())</span>
|
||||
<span>@string.Join(", ", Permission.ToPermissions(permissions).Select(c => c.ToString()).Distinct().ToArray())</span>
|
||||
}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
@{
|
||||
Layout = "_Layout";
|
||||
ViewData["Title"] = $"Authorize {(Model.ApplicationName ?? "Application")}";
|
||||
var permissions = Permission.ToPermissions(Model.Permissions);
|
||||
var permissions = Permission.ToPermissions(Model.Permissions.Split(';'));
|
||||
var hasStorePermission = permissions.Any(p => p.Policy == Policies.CanModifyStoreSettings);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue