mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 14:34:23 +01:00
Greenfield API: Get current User
Builds on #1368 This PR adds a new endpoint: Get current user.. It only returns the current user's id and email for now( let's extend later) It also adds a new permission: `ProfileManagement` which is needed for this endpoint (and for update endpoints later)
This commit is contained in:
15
BTCPayServer.Client/BTCPayServerClient.Users.cs
Normal file
15
BTCPayServer.Client/BTCPayServerClient.Users.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Client.Models;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Client
|
||||||
|
{
|
||||||
|
public partial class BTCPayServerClient
|
||||||
|
{
|
||||||
|
public virtual async Task<ApplicationUserData> GetCurrentUser(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/users/me"), token);
|
||||||
|
return await HandleResponse<ApplicationUserData>(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
BTCPayServer.Client/Models/ApplicationUserData.cs
Normal file
8
BTCPayServer.Client/Models/ApplicationUserData.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace BTCPayServer.Client.Models
|
||||||
|
{
|
||||||
|
public class ApplicationUserData
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,17 @@ namespace BTCPayServer.Client
|
|||||||
{
|
{
|
||||||
public const string ServerManagement = nameof(ServerManagement);
|
public const string ServerManagement = nameof(ServerManagement);
|
||||||
public const string StoreManagement = nameof(StoreManagement);
|
public const string StoreManagement = nameof(StoreManagement);
|
||||||
|
public const string ProfileManagement = nameof(ProfileManagement);
|
||||||
|
|
||||||
|
public static string[] GetAllPermissionKeys()
|
||||||
|
{
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
ServerManagement,
|
||||||
|
StoreManagement,
|
||||||
|
ProfileManagement
|
||||||
|
};
|
||||||
|
}
|
||||||
public static string GetStorePermission(string storeId) => $"{nameof(StoreManagement)}:{storeId}";
|
public static string GetStorePermission(string storeId) => $"{nameof(StoreManagement)}:{storeId}";
|
||||||
|
|
||||||
public static IEnumerable<string> ExtractStorePermissionsIds(IEnumerable<string> permissions) => permissions
|
public static IEnumerable<string> ExtractStorePermissionsIds(IEnumerable<string> permissions) => permissions
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using BTCPayServer.Data;
|
|||||||
using BTCPayServer.Security.APIKeys;
|
using BTCPayServer.Security.APIKeys;
|
||||||
using BTCPayServer.Tests.Logging;
|
using BTCPayServer.Tests.Logging;
|
||||||
using BTCPayServer.Views.Manage;
|
using BTCPayServer.Views.Manage;
|
||||||
using ExchangeSharp;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using OpenQA.Selenium;
|
using OpenQA.Selenium;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BTCPayServer.Client;
|
using BTCPayServer.Client;
|
||||||
@@ -22,7 +23,7 @@ namespace BTCPayServer.Tests
|
|||||||
Logs.LogProvider = new XUnitLogProvider(helper);
|
Logs.LogProvider = new XUnitLogProvider(helper);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact(Timeout = TestTimeout)]
|
||||||
[Trait("Integration", "Integration")]
|
[Trait("Integration", "Integration")]
|
||||||
public async Task ApiKeysControllerTests()
|
public async Task ApiKeysControllerTests()
|
||||||
{
|
{
|
||||||
@@ -32,7 +33,7 @@ namespace BTCPayServer.Tests
|
|||||||
var user = tester.NewAccount();
|
var user = tester.NewAccount();
|
||||||
user.GrantAccess();
|
user.GrantAccess();
|
||||||
await user.MakeAdmin();
|
await user.MakeAdmin();
|
||||||
string apiKey = await GenerateAPIKey(tester, user);
|
string apiKey = await GenerateAPIKey(tester, user, Permissions.ServerManagement, Permissions.StoreManagement);
|
||||||
var client = new BTCPayServerClient(tester.PayTester.ServerUri, apiKey);
|
var client = new BTCPayServerClient(tester.PayTester.ServerUri, apiKey);
|
||||||
//Get current api key
|
//Get current api key
|
||||||
var apiKeyData = await client.GetCurrentAPIKeyInfo();
|
var apiKeyData = await client.GetCurrentAPIKeyInfo();
|
||||||
@@ -49,15 +50,42 @@ namespace BTCPayServer.Tests
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact(Timeout = TestTimeout)]
|
||||||
|
[Trait("Integration", "Integration")]
|
||||||
|
public async Task UsersControllerTests()
|
||||||
|
{
|
||||||
|
using (var tester = ServerTester.Create())
|
||||||
|
{
|
||||||
|
await tester.StartAsync();
|
||||||
|
var user = tester.NewAccount();
|
||||||
|
user.GrantAccess();
|
||||||
|
await user.MakeAdmin();
|
||||||
|
string apiKeyProfile = await GenerateAPIKey(tester, user, Permissions.ProfileManagement);
|
||||||
|
string apiKeyInsufficient = await GenerateAPIKey(tester, user, Permissions.StoreManagement);
|
||||||
|
var clientProfile = new BTCPayServerClient(tester.PayTester.ServerUri, apiKeyProfile);
|
||||||
|
var clientInsufficient= new BTCPayServerClient(tester.PayTester.ServerUri, apiKeyInsufficient);
|
||||||
|
|
||||||
|
var apiKeyProfileUserData = await clientProfile.GetCurrentUser();
|
||||||
|
Assert.NotNull(apiKeyProfileUserData);
|
||||||
|
Assert.Equal(apiKeyProfileUserData.Id, user.UserId);
|
||||||
|
Assert.Equal(apiKeyProfileUserData.Email, user.RegisterDetails.Email);
|
||||||
|
|
||||||
private static async Task<string> GenerateAPIKey(ServerTester tester, TestAccount user)
|
await Assert.ThrowsAsync<HttpRequestException>(async () => await clientInsufficient.GetCurrentUser());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<string> GenerateAPIKey(ServerTester tester, TestAccount user, params string[] permissions)
|
||||||
{
|
{
|
||||||
var manageController = tester.PayTester.GetController<ManageController>(user.UserId, user.StoreId, user.IsAdmin);
|
var manageController = tester.PayTester.GetController<ManageController>(user.UserId, user.StoreId, user.IsAdmin);
|
||||||
var x = Assert.IsType<RedirectToActionResult>(await manageController.AddApiKey(
|
var x = Assert.IsType<RedirectToActionResult>(await manageController.AddApiKey(
|
||||||
new ManageController.AddApiKeyViewModel()
|
new ManageController.AddApiKeyViewModel()
|
||||||
{
|
{
|
||||||
ServerManagementPermission = true,
|
PermissionValues = permissions.Select(s => new ManageController.AddApiKeyViewModel.PermissionValueItem()
|
||||||
StoreManagementPermission = true,
|
{
|
||||||
|
Permission = s,
|
||||||
|
Value = true
|
||||||
|
}).ToList(),
|
||||||
StoreMode = ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores
|
StoreMode = ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores
|
||||||
}));
|
}));
|
||||||
var statusMessage = manageController.TempData.GetStatusMessageModel();
|
var statusMessage = manageController.TempData.GetStatusMessageModel();
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using NBitcoin.DataEncoders;
|
using NBitcoin.DataEncoders;
|
||||||
using NSwag.Annotations;
|
using NSwag.Annotations;
|
||||||
|
using YamlDotNet.Core.Tokens;
|
||||||
|
|
||||||
namespace BTCPayServer.Controllers
|
namespace BTCPayServer.Controllers
|
||||||
{
|
{
|
||||||
@@ -30,8 +31,6 @@ namespace BTCPayServer.Controllers
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("api-keys/{id}/delete")]
|
[HttpGet("api-keys/{id}/delete")]
|
||||||
public async Task<IActionResult> RemoveAPIKey(string id)
|
public async Task<IActionResult> RemoveAPIKey(string id)
|
||||||
@@ -113,6 +112,10 @@ namespace BTCPayServer.Controllers
|
|||||||
ServerManagementPermission = permissions.Contains(Permissions.ServerManagement),
|
ServerManagementPermission = permissions.Contains(Permissions.ServerManagement),
|
||||||
StoreManagementPermission = permissions.Contains(Permissions.StoreManagement),
|
StoreManagementPermission = permissions.Contains(Permissions.StoreManagement),
|
||||||
PermissionsFormatted = permissions,
|
PermissionsFormatted = permissions,
|
||||||
|
PermissionValues = permissions.Where(s =>
|
||||||
|
!s.Contains(Permissions.StoreManagement, StringComparison.InvariantCultureIgnoreCase) &&
|
||||||
|
s != Permissions.ServerManagement)
|
||||||
|
.Select(s => new AddApiKeyViewModel.PermissionValueItem() {Permission = s, Value = true}).ToList(),
|
||||||
ApplicationName = applicationName,
|
ApplicationName = applicationName,
|
||||||
SelectiveStores = selectiveStores,
|
SelectiveStores = selectiveStores,
|
||||||
Strict = strict,
|
Strict = strict,
|
||||||
@@ -262,7 +265,7 @@ namespace BTCPayServer.Controllers
|
|||||||
|
|
||||||
private IEnumerable<string> GetPermissionsFromViewModel(AddApiKeyViewModel viewModel)
|
private IEnumerable<string> GetPermissionsFromViewModel(AddApiKeyViewModel viewModel)
|
||||||
{
|
{
|
||||||
var permissions = new List<string>();
|
var permissions = viewModel.PermissionValues.Where(tuple => tuple.Value).Select(tuple => tuple.Permission).ToList();
|
||||||
|
|
||||||
if (viewModel.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific)
|
if (viewModel.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific)
|
||||||
{
|
{
|
||||||
@@ -278,13 +281,18 @@ namespace BTCPayServer.Controllers
|
|||||||
permissions.Add(Permissions.ServerManagement);
|
permissions.Add(Permissions.ServerManagement);
|
||||||
}
|
}
|
||||||
|
|
||||||
return permissions;
|
return permissions.Distinct();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<T> SetViewModelValues<T>(T viewModel) where T : AddApiKeyViewModel
|
private async Task<T> SetViewModelValues<T>(T viewModel) where T : AddApiKeyViewModel
|
||||||
{
|
{
|
||||||
viewModel.Stores = await _StoreRepository.GetStoresByUserId(_userManager.GetUserId(User));
|
viewModel.Stores = await _StoreRepository.GetStoresByUserId(_userManager.GetUserId(User));
|
||||||
viewModel.IsServerAdmin = (await _authorizationService.AuthorizeAsync(User, Policies.CanModifyServerSettings.Key)).Succeeded;
|
viewModel.IsServerAdmin =
|
||||||
|
(await _authorizationService.AuthorizeAsync(User, Policies.CanModifyServerSettings.Key)).Succeeded;
|
||||||
|
viewModel.PermissionValues ??= Permissions.GetAllPermissionKeys().Where(s =>
|
||||||
|
!s.Contains(Permissions.StoreManagement, StringComparison.InvariantCultureIgnoreCase) &&
|
||||||
|
s != Permissions.ServerManagement)
|
||||||
|
.Select(s => new AddApiKeyViewModel.PermissionValueItem() {Permission = s, Value = true}).ToList();
|
||||||
return viewModel;
|
return viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,12 +306,19 @@ namespace BTCPayServer.Controllers
|
|||||||
public bool ServerManagementPermission { get; set; }
|
public bool ServerManagementPermission { get; set; }
|
||||||
public bool StoreManagementPermission { get; set; }
|
public bool StoreManagementPermission { get; set; }
|
||||||
public string Command { get; set; }
|
public string Command { get; set; }
|
||||||
|
public List<PermissionValueItem> PermissionValues { get; set; }
|
||||||
|
|
||||||
public enum ApiKeyStoreMode
|
public enum ApiKeyStoreMode
|
||||||
{
|
{
|
||||||
AllStores,
|
AllStores,
|
||||||
Specific
|
Specific
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class PermissionValueItem
|
||||||
|
{
|
||||||
|
public string Permission { get; set; }
|
||||||
|
public bool Value { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AuthorizeApiKeysViewModel : AddApiKeyViewModel
|
public class AuthorizeApiKeysViewModel : AddApiKeyViewModel
|
||||||
|
|||||||
47
BTCPayServer/Controllers/RestApi/Users/UsersController.cs
Normal file
47
BTCPayServer/Controllers/RestApi/Users/UsersController.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Client.Models;
|
||||||
|
using BTCPayServer.Data;
|
||||||
|
using BTCPayServer.Hosting.OpenApi;
|
||||||
|
using BTCPayServer.Security;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using NSwag.Annotations;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Controllers.RestApi.Users
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[IncludeInOpenApiDocs]
|
||||||
|
[OpenApiTags("Users")]
|
||||||
|
[Authorize(AuthenticationSchemes = AuthenticationSchemes.ApiKey)]
|
||||||
|
public class UsersController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
|
||||||
|
public UsersController(UserManager<ApplicationUser> userManager)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
[OpenApiOperation("Get current user information", "View information about the current user")]
|
||||||
|
[SwaggerResponse(StatusCodes.Status200OK, typeof(ApiKeyData),
|
||||||
|
Description = "Information about the current user")]
|
||||||
|
[Authorize(Policy = Policies.CanModifyProfile.Key, AuthenticationSchemes = AuthenticationSchemes.ApiKey)]
|
||||||
|
[HttpGet("~/api/v1/users/me")]
|
||||||
|
public async Task<ActionResult<ApplicationUserData>> GetCurrentUser()
|
||||||
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
return FromModel(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ApplicationUserData FromModel(ApplicationUser data)
|
||||||
|
{
|
||||||
|
return new ApplicationUserData()
|
||||||
|
{
|
||||||
|
Id = data.Id,
|
||||||
|
Email = data.Email
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,6 +34,9 @@ namespace BTCPayServer.Security.APIKeys
|
|||||||
bool success = false;
|
bool success = false;
|
||||||
switch (requirement.Policy)
|
switch (requirement.Policy)
|
||||||
{
|
{
|
||||||
|
case Policies.CanModifyProfile.Key:
|
||||||
|
success = context.HasPermissions(Permissions.ProfileManagement);
|
||||||
|
break;
|
||||||
case Policies.CanListStoreSettings.Key:
|
case Policies.CanListStoreSettings.Key:
|
||||||
var selectiveStorePermissions =
|
var selectiveStorePermissions =
|
||||||
Permissions.ExtractStorePermissionsIds(context.GetPermissions());
|
Permissions.ExtractStorePermissionsIds(context.GetPermissions());
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace BTCPayServer.Security.APIKeys
|
|||||||
{Client.Permissions.StoreManagement, ("Manage your stores", "The app will be able to create, modify and delete all your stores.")},
|
{Client.Permissions.StoreManagement, ("Manage your stores", "The app will be able to create, modify and delete all your stores.")},
|
||||||
{$"{nameof(Client.Permissions.StoreManagement)}:", ("Manage selected stores", "The app will be able to modify and delete selected stores.")},
|
{$"{nameof(Client.Permissions.StoreManagement)}:", ("Manage selected stores", "The app will be able to modify and delete selected stores.")},
|
||||||
{Client.Permissions.ServerManagement, ("Manage your server", "The app will have total control on your server")},
|
{Client.Permissions.ServerManagement, ("Manage your server", "The app will have total control on your server")},
|
||||||
|
{Client.Permissions.ProfileManagement, ("Manage your profile", "The app will be able to view and modify your user profile.")},
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
using System;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
|
|
||||||
namespace BTCPayServer.Security
|
namespace BTCPayServer.Security
|
||||||
{
|
{
|
||||||
@@ -15,6 +11,8 @@ namespace BTCPayServer.Security
|
|||||||
options.AddPolicy(CanCreateInvoice.Key);
|
options.AddPolicy(CanCreateInvoice.Key);
|
||||||
options.AddPolicy(CanGetRates.Key);
|
options.AddPolicy(CanGetRates.Key);
|
||||||
options.AddPolicy(CanModifyServerSettings.Key);
|
options.AddPolicy(CanModifyServerSettings.Key);
|
||||||
|
options.AddPolicy(CanModifyServerSettings.Key);
|
||||||
|
options.AddPolicy(CanModifyProfile.Key);
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +25,10 @@ namespace BTCPayServer.Security
|
|||||||
{
|
{
|
||||||
public const string Key = "btcpay.store.canmodifyserversettings";
|
public const string Key = "btcpay.store.canmodifyserversettings";
|
||||||
}
|
}
|
||||||
|
public class CanModifyProfile
|
||||||
|
{
|
||||||
|
public const string Key = "btcpay.store.canmodifyprofile";
|
||||||
|
}
|
||||||
public class CanModifyStoreSettings
|
public class CanModifyStoreSettings
|
||||||
{
|
{
|
||||||
public const string Key = "btcpay.store.canmodifystoresettings";
|
public const string Key = "btcpay.store.canmodifystoresettings";
|
||||||
|
|||||||
@@ -45,6 +45,17 @@
|
|||||||
<p>@GetDescription(Permissions.ServerManagement).</p>
|
<p>@GetDescription(Permissions.ServerManagement).</p>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@for (int i = 0; i < Model.PermissionValues.Count; i++)
|
||||||
|
{
|
||||||
|
<div class="list-group-item form-group">
|
||||||
|
<input type="hidden" asp-for="PermissionValues[i].Permission">
|
||||||
|
<input type="checkbox" asp-for="PermissionValues[i].Value" class="form-check-inline"/>
|
||||||
|
<label asp-for="PermissionValues[i].Value" class="h5">@GetTitle(Model.PermissionValues[i].Permission)</label>
|
||||||
|
<span asp-validation-for="PermissionValues[i].Value" class="text-danger"></span>
|
||||||
|
<p>@GetDescription(Model.PermissionValues[i].Permission).</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@if (Model.StoreMode == ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores)
|
@if (Model.StoreMode == ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores)
|
||||||
{
|
{
|
||||||
<div class="list-group-item form-group">
|
<div class="list-group-item form-group">
|
||||||
|
|||||||
@@ -74,7 +74,24 @@
|
|||||||
<p>@GetDescription(Permissions.ServerManagement).</p>
|
<p>@GetDescription(Permissions.ServerManagement).</p>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@for (int i = 0; i < Model.PermissionValues.Count; i++)
|
||||||
|
{
|
||||||
|
<div class="list-group-item form-group">
|
||||||
|
<input type="hidden" asp-for="PermissionValues[i].Permission">
|
||||||
|
@if (Model.Strict || !Model.IsServerAdmin)
|
||||||
|
{
|
||||||
|
<input type="hidden" asp-for="PermissionValues[i].Value"/>
|
||||||
|
<input type="checkbox" class="form-check-inline" checked="@Model.PermissionValues[i].Value" disabled/>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<input type="checkbox" asp-for="PermissionValues[i].Value" class="form-check-inline"/>
|
||||||
|
}
|
||||||
|
<label asp-for="PermissionValues[i].Value" class="h5">@GetTitle(Model.PermissionValues[i].Permission)</label>
|
||||||
|
<span asp-validation-for="PermissionValues[i].Value" class="text-danger"></span>
|
||||||
|
<p>@GetDescription(Model.PermissionValues[i].Permission).</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@if (Model.PermissionsFormatted.Contains(Permissions.StoreManagement))
|
@if (Model.PermissionsFormatted.Contains(Permissions.StoreManagement))
|
||||||
{
|
{
|
||||||
@if (Model.StoreMode == ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores)
|
@if (Model.StoreMode == ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores)
|
||||||
|
|||||||
Reference in New Issue
Block a user