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 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 IEnumerable<string> ExtractStorePermissionsIds(IEnumerable<string> permissions) => permissions
|
||||
|
||||
@@ -9,7 +9,6 @@ using BTCPayServer.Data;
|
||||
using BTCPayServer.Security.APIKeys;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using BTCPayServer.Views.Manage;
|
||||
using ExchangeSharp;
|
||||
using Newtonsoft.Json;
|
||||
using OpenQA.Selenium;
|
||||
using Xunit;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client;
|
||||
@@ -22,7 +23,7 @@ namespace BTCPayServer.Tests
|
||||
Logs.LogProvider = new XUnitLogProvider(helper);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task ApiKeysControllerTests()
|
||||
{
|
||||
@@ -32,7 +33,7 @@ namespace BTCPayServer.Tests
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
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);
|
||||
//Get current api key
|
||||
var apiKeyData = await client.GetCurrentAPIKeyInfo();
|
||||
@@ -50,14 +51,41 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<string> GenerateAPIKey(ServerTester tester, TestAccount user)
|
||||
[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);
|
||||
|
||||
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 x = Assert.IsType<RedirectToActionResult>(await manageController.AddApiKey(
|
||||
new ManageController.AddApiKeyViewModel()
|
||||
{
|
||||
ServerManagementPermission = true,
|
||||
StoreManagementPermission = true,
|
||||
PermissionValues = permissions.Select(s => new ManageController.AddApiKeyViewModel.PermissionValueItem()
|
||||
{
|
||||
Permission = s,
|
||||
Value = true
|
||||
}).ToList(),
|
||||
StoreMode = ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores
|
||||
}));
|
||||
var statusMessage = manageController.TempData.GetStatusMessageModel();
|
||||
|
||||
@@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using NBitcoin;
|
||||
using NBitcoin.DataEncoders;
|
||||
using NSwag.Annotations;
|
||||
using YamlDotNet.Core.Tokens;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
@@ -31,8 +32,6 @@ namespace BTCPayServer.Controllers
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpGet("api-keys/{id}/delete")]
|
||||
public async Task<IActionResult> RemoveAPIKey(string id)
|
||||
{
|
||||
@@ -113,6 +112,10 @@ namespace BTCPayServer.Controllers
|
||||
ServerManagementPermission = permissions.Contains(Permissions.ServerManagement),
|
||||
StoreManagementPermission = permissions.Contains(Permissions.StoreManagement),
|
||||
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,
|
||||
SelectiveStores = selectiveStores,
|
||||
Strict = strict,
|
||||
@@ -262,7 +265,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -278,13 +281,18 @@ namespace BTCPayServer.Controllers
|
||||
permissions.Add(Permissions.ServerManagement);
|
||||
}
|
||||
|
||||
return permissions;
|
||||
return permissions.Distinct();
|
||||
}
|
||||
|
||||
private async Task<T> SetViewModelValues<T>(T viewModel) where T : AddApiKeyViewModel
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -298,12 +306,19 @@ namespace BTCPayServer.Controllers
|
||||
public bool ServerManagementPermission { get; set; }
|
||||
public bool StoreManagementPermission { get; set; }
|
||||
public string Command { get; set; }
|
||||
public List<PermissionValueItem> PermissionValues { get; set; }
|
||||
|
||||
public enum ApiKeyStoreMode
|
||||
{
|
||||
AllStores,
|
||||
Specific
|
||||
}
|
||||
|
||||
public class PermissionValueItem
|
||||
{
|
||||
public string Permission { get; set; }
|
||||
public bool Value { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
switch (requirement.Policy)
|
||||
{
|
||||
case Policies.CanModifyProfile.Key:
|
||||
success = context.HasPermissions(Permissions.ProfileManagement);
|
||||
break;
|
||||
case Policies.CanListStoreSettings.Key:
|
||||
var selectiveStorePermissions =
|
||||
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.")},
|
||||
{$"{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.ProfileManagement, ("Manage your profile", "The app will be able to view and modify your user profile.")},
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace BTCPayServer.Security
|
||||
{
|
||||
@@ -15,6 +11,8 @@ namespace BTCPayServer.Security
|
||||
options.AddPolicy(CanCreateInvoice.Key);
|
||||
options.AddPolicy(CanGetRates.Key);
|
||||
options.AddPolicy(CanModifyServerSettings.Key);
|
||||
options.AddPolicy(CanModifyServerSettings.Key);
|
||||
options.AddPolicy(CanModifyProfile.Key);
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -27,6 +25,10 @@ namespace BTCPayServer.Security
|
||||
{
|
||||
public const string Key = "btcpay.store.canmodifyserversettings";
|
||||
}
|
||||
public class CanModifyProfile
|
||||
{
|
||||
public const string Key = "btcpay.store.canmodifyprofile";
|
||||
}
|
||||
public class CanModifyStoreSettings
|
||||
{
|
||||
public const string Key = "btcpay.store.canmodifystoresettings";
|
||||
|
||||
@@ -45,6 +45,17 @@
|
||||
<p>@GetDescription(Permissions.ServerManagement).</p>
|
||||
</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)
|
||||
{
|
||||
<div class="list-group-item form-group">
|
||||
|
||||
@@ -74,7 +74,24 @@
|
||||
<p>@GetDescription(Permissions.ServerManagement).</p>
|
||||
</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.StoreMode == ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores)
|
||||
|
||||
Reference in New Issue
Block a user