mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 14:34:23 +01:00
Greenfield: Expand user data (#6649)
This commit is contained in:
@@ -50,6 +50,11 @@ namespace BTCPayServer.Client.Models
|
||||
/// </summary>
|
||||
public string[] Roles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// the invitation url of the user
|
||||
/// </summary>
|
||||
public string InvitationUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// the date the user was created. Null if created before v1.0.5.6.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
@@ -10,32 +12,24 @@ namespace BTCPayServer.Client.Models
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
public class StoreUserData
|
||||
public class StoreUserData : ApplicationUserData
|
||||
{
|
||||
/// <summary>
|
||||
/// the id of the user
|
||||
/// </summary>
|
||||
[Obsolete("Use Id instead")]
|
||||
public string UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// the store role of the user
|
||||
/// </summary>
|
||||
public string StoreRole { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// the store role of the user
|
||||
/// </summary>
|
||||
[Obsolete("Use StoreRole instead")]
|
||||
public string Role { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// the email AND username of the user
|
||||
/// </summary>
|
||||
public string Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// the name of the user
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// the image url of the user
|
||||
/// </summary>
|
||||
public string ImageUrl { get; set; }
|
||||
}
|
||||
|
||||
public class RoleData
|
||||
|
||||
@@ -248,7 +248,7 @@ namespace BTCPayServer.Tests
|
||||
await Assert.ThrowsAsync<GreenfieldAPIException>(() => newUserClient.GetInvoices(store.Id));
|
||||
|
||||
// if user is a guest or owner, then it should be ok
|
||||
await unrestricted.AddStoreUser(store.Id, new StoreUserData() { UserId = newUser.Id });
|
||||
await unrestricted.AddStoreUser(store.Id, new StoreUserData() { Id = newUser.Id });
|
||||
await newUserClient.GetInvoices(store.Id);
|
||||
}
|
||||
|
||||
@@ -4017,8 +4017,8 @@ namespace BTCPayServer.Tests
|
||||
#pragma warning restore CS0618
|
||||
var users = await client.GetStoreUsers(user.StoreId);
|
||||
var storeUser = Assert.Single(users);
|
||||
Assert.Equal(user.UserId, storeUser.UserId);
|
||||
Assert.Equal(ownerRole.Id, storeUser.Role);
|
||||
Assert.Equal(user.UserId, storeUser.Id);
|
||||
Assert.Equal(ownerRole.Id, storeUser.StoreRole);
|
||||
Assert.Equal(user.Email, storeUser.Email);
|
||||
Assert.Equal("The Admin", storeUser.Name);
|
||||
Assert.Equal("avatar.jpg", storeUser.ImageUrl);
|
||||
@@ -4050,15 +4050,15 @@ namespace BTCPayServer.Tests
|
||||
await AssertPermissionError(Policies.CanModifyStoreSettings, async () => await guestClient.RemoveStoreUser(user.StoreId, user.UserId));
|
||||
|
||||
// add users to store
|
||||
await client.AddStoreUser(user.StoreId, new StoreUserData { Role = managerRole.Id, UserId = manager.UserId });
|
||||
await client.AddStoreUser(user.StoreId, new StoreUserData { Role = employeeRole.Id, UserId = employee.UserId });
|
||||
await client.AddStoreUser(user.StoreId, new StoreUserData { StoreRole = managerRole.Id, Id = manager.UserId });
|
||||
await client.AddStoreUser(user.StoreId, new StoreUserData { StoreRole = employeeRole.Id, Id = employee.UserId });
|
||||
|
||||
// add with email
|
||||
await client.AddStoreUser(user.StoreId, new StoreUserData { Role = guestRole.Id, UserId = guest.Email });
|
||||
await client.AddStoreUser(user.StoreId, new StoreUserData { StoreRole = guestRole.Id, Id = guest.Email });
|
||||
|
||||
// test unknown user
|
||||
await AssertAPIError("user-not-found", async () => await client.AddStoreUser(user.StoreId, new StoreUserData { Role = managerRole.Id, UserId = "unknown" }));
|
||||
await AssertAPIError("user-not-found", async () => await client.UpdateStoreUser(user.StoreId, "unknown", new StoreUserData { Role = ownerRole.Id }));
|
||||
await AssertAPIError("user-not-found", async () => await client.AddStoreUser(user.StoreId, new StoreUserData { StoreRole = managerRole.Id, Id = "unknown" }));
|
||||
await AssertAPIError("user-not-found", async () => await client.UpdateStoreUser(user.StoreId, "unknown", new StoreUserData { StoreRole = ownerRole.Id }));
|
||||
await AssertAPIError("user-not-found", async () => await client.RemoveStoreUser(user.StoreId, "unknown"));
|
||||
|
||||
//test no access to api for employee
|
||||
@@ -4080,7 +4080,7 @@ namespace BTCPayServer.Tests
|
||||
await AssertPermissionError(Policies.CanModifyStoreSettings, async () => await managerClient.RemoveStoreUser(user.StoreId, user.UserId));
|
||||
|
||||
// updates
|
||||
await client.UpdateStoreUser(user.StoreId, employee.UserId, new StoreUserData { Role = ownerRole.Id });
|
||||
await client.UpdateStoreUser(user.StoreId, employee.UserId, new StoreUserData { StoreRole = ownerRole.Id });
|
||||
await employeeClient.GetStore(user.StoreId);
|
||||
|
||||
// remove
|
||||
@@ -4088,9 +4088,9 @@ namespace BTCPayServer.Tests
|
||||
await AssertHttpError(403, async () => await employeeClient.GetStore(user.StoreId));
|
||||
|
||||
// test duplicate add
|
||||
await client.AddStoreUser(user.StoreId, new StoreUserData { Role = ownerRole.Id, UserId = employee.UserId });
|
||||
await client.AddStoreUser(user.StoreId, new StoreUserData { StoreRole = ownerRole.Id, Id = employee.UserId });
|
||||
await AssertAPIError("duplicate-store-user-role", async () =>
|
||||
await client.AddStoreUser(user.StoreId, new StoreUserData { Role = ownerRole.Id, UserId = employee.UserId }));
|
||||
await client.AddStoreUser(user.StoreId, new StoreUserData { StoreRole = ownerRole.Id, Id = employee.UserId }));
|
||||
await employeeClient.RemoveStoreUser(user.StoreId, user.UserId);
|
||||
|
||||
//test no access to api when unrelated to store at all
|
||||
|
||||
@@ -194,5 +194,5 @@
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ProjectExtensions><VisualStudio><UserProperties wwwroot_4swagger_4v1_4swagger_1template_1invoices_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1misc_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1pull-payments_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1serverinfo_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1stores_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1stores-payment-methods_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1users_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1webhooks_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" /></VisualStudio></ProjectExtensions>
|
||||
<ProjectExtensions><VisualStudio><UserProperties wwwroot_4swagger_4v1_4swagger_1template_1invoices_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1misc_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1pull-payments_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1serverinfo_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1stores_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1stores-payment-methods_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1stores-users_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1users_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1webhooks_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" /></VisualStudio></ProjectExtensions>
|
||||
</Project>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
@@ -11,6 +14,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NicolasDorier.RateLimits;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
|
||||
namespace BTCPayServer.Controllers.Greenfield
|
||||
@@ -22,15 +26,18 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
{
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly CallbackGenerator _callbackGenerator;
|
||||
private readonly UriResolver _uriResolver;
|
||||
|
||||
public GreenfieldStoreUsersController(
|
||||
StoreRepository storeRepository,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
CallbackGenerator callbackGenerator,
|
||||
UriResolver uriResolver)
|
||||
{
|
||||
_storeRepository = storeRepository;
|
||||
_userManager = userManager;
|
||||
_callbackGenerator = callbackGenerator;
|
||||
_uriResolver = uriResolver;
|
||||
}
|
||||
|
||||
@@ -47,10 +54,12 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
public async Task<IActionResult> RemoveStoreUser(string storeId, string idOrEmail)
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null) return StoreNotFound();
|
||||
if (store == null)
|
||||
return StoreNotFound();
|
||||
|
||||
var user = await _userManager.FindByIdOrEmail(idOrEmail);
|
||||
if (user == null) return UserNotFound();
|
||||
if (user == null)
|
||||
return UserNotFound();
|
||||
|
||||
return await _storeRepository.RemoveStoreUser(storeId, user.Id)
|
||||
? Ok()
|
||||
@@ -63,17 +72,23 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
public async Task<IActionResult> AddOrUpdateStoreUser(string storeId, StoreUserData request, string idOrEmail = null)
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null) return StoreNotFound();
|
||||
if (store == null)
|
||||
return StoreNotFound();
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
request.StoreRole ??= request.Role;
|
||||
request.Id ??= request.UserId;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
var user = await _userManager.FindByIdOrEmail(idOrEmail ?? request.UserId);
|
||||
if (user == null) return UserNotFound();
|
||||
var user = await _userManager.FindByIdOrEmail(idOrEmail ?? request.Id);
|
||||
if (user == null)
|
||||
return UserNotFound();
|
||||
|
||||
StoreRoleId roleId = null;
|
||||
if (request.Role is not null)
|
||||
if (request.StoreRole is not null)
|
||||
{
|
||||
roleId = await _storeRepository.ResolveStoreRoleId(storeId, request.Role);
|
||||
roleId = await _storeRepository.ResolveStoreRoleId(storeId, request.StoreRole);
|
||||
if (roleId is null)
|
||||
ModelState.AddModelError(nameof(request.Role), "The role id provided does not exist");
|
||||
ModelState.AddModelError(nameof(request.StoreRole), "The role id provided does not exist");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
@@ -87,21 +102,21 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
: this.CreateAPIError(409, "duplicate-store-user-role", "The user is already added to the store");
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<StoreUserData>> FromModel(StoreData data)
|
||||
private async Task<IEnumerable<StoreUserData>> FromModel(StoreData store)
|
||||
{
|
||||
var storeUsers = new List<StoreUserData>();
|
||||
foreach (var storeUser in data.UserStores)
|
||||
foreach (var storeUser in store.UserStores)
|
||||
{
|
||||
var user = await _userManager.FindByIdOrEmail(storeUser.ApplicationUserId);
|
||||
var blob = user?.GetBlob();
|
||||
storeUsers.Add(new StoreUserData
|
||||
{
|
||||
UserId = storeUser.ApplicationUserId,
|
||||
Role = storeUser.StoreRoleId,
|
||||
Email = user?.Email,
|
||||
Name = blob?.Name,
|
||||
ImageUrl = blob?.ImageUrl == null ? null : await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), UnresolvedUri.Create(blob.ImageUrl))
|
||||
});
|
||||
if (user == null)
|
||||
continue;
|
||||
var data = await UserService.ForAPI<StoreUserData>(user, [], _callbackGenerator, _uriResolver, Request);
|
||||
data.StoreRole = storeUser.StoreRoleId;
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
data.UserId = storeUser.ApplicationUserId;
|
||||
data.Role = storeUser.StoreRoleId;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
storeUsers.Add(data);
|
||||
}
|
||||
return storeUsers;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -84,7 +85,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
var user = await _userManager.FindByIdOrEmail(idOrEmail);
|
||||
if (user != null)
|
||||
{
|
||||
return Ok(await FromModel(user));
|
||||
return Ok(await ForAPI(user));
|
||||
}
|
||||
return this.UserNotFound();
|
||||
}
|
||||
@@ -128,7 +129,13 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
[HttpGet("~/api/v1/users/")]
|
||||
public async Task<ActionResult<ApplicationUserData[]>> GetUsers()
|
||||
{
|
||||
return Ok(await _userService.GetUsersWithRoles());
|
||||
var usersWithRoles = await _userService.GetUsersWithRoles();
|
||||
List<ApplicationUserData> users = [];
|
||||
foreach (var user in usersWithRoles)
|
||||
{
|
||||
users.Add(await UserService.ForAPI<ApplicationUserData>(user.User, user.Roles, _callbackGenerator, _uriResolver, Request));
|
||||
}
|
||||
return Ok(users);
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanViewProfile, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
@@ -136,7 +143,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
public async Task<ActionResult<ApplicationUserData>> GetCurrentUser()
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
return await FromModel(user!);
|
||||
return await ForAPI(user!);
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyProfile, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
@@ -232,7 +239,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
if (!ModelState.IsValid)
|
||||
return this.CreateValidationError(ModelState);
|
||||
|
||||
var model = await FromModel(user);
|
||||
var model = await ForAPI(user);
|
||||
return Ok(model);
|
||||
}
|
||||
|
||||
@@ -264,7 +271,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
user.SetBlob(blob);
|
||||
await _userManager.UpdateAsync(user);
|
||||
_eventAggregator.Publish(new UserEvent.Updated(user));
|
||||
var model = await FromModel(user);
|
||||
var model = await ForAPI(user);
|
||||
return Ok(model);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -414,7 +421,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
};
|
||||
_eventAggregator.Publish(userEvent);
|
||||
|
||||
var model = await FromModel(user);
|
||||
var model = await ForAPI(user);
|
||||
return CreatedAtAction(string.Empty, model);
|
||||
}
|
||||
|
||||
@@ -449,14 +456,11 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
return Ok();
|
||||
}
|
||||
|
||||
private async Task<ApplicationUserData> FromModel(ApplicationUser data)
|
||||
private async Task<ApplicationUserData> ForAPI(ApplicationUser data)
|
||||
{
|
||||
var roles = (await _userManager.GetRolesAsync(data)).ToArray();
|
||||
var model = UserService.FromModel(data, roles);
|
||||
model.ImageUrl = string.IsNullOrEmpty(model.ImageUrl)
|
||||
? null
|
||||
: await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), UnresolvedUri.Create(model.ImageUrl));
|
||||
return model;
|
||||
var blob = data.GetBlob();
|
||||
return await UserService.ForAPI<ApplicationUserData>(data, roles, _callbackGenerator, _uriResolver, Request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace BTCPayServer.Controllers
|
||||
InvitationUrl =
|
||||
string.IsNullOrEmpty(blob?.InvitationToken)
|
||||
? null
|
||||
: _callbackGenerator.ForInvitation(u, blob.InvitationToken, Request),
|
||||
: _callbackGenerator.ForInvitation(u.Id, blob.InvitationToken, Request),
|
||||
EmailConfirmed = u.RequiresEmailConfirmation ? u.EmailConfirmed : null,
|
||||
Approved = u.RequiresApproval ? u.Approved : null,
|
||||
Created = u.Created,
|
||||
@@ -97,7 +97,7 @@ namespace BTCPayServer.Controllers
|
||||
Id = user.Id,
|
||||
Email = user.Email,
|
||||
Name = blob?.Name,
|
||||
InvitationUrl = string.IsNullOrEmpty(blob?.InvitationToken) ? null : _callbackGenerator.ForInvitation(user, blob.InvitationToken, Request),
|
||||
InvitationUrl = string.IsNullOrEmpty(blob?.InvitationToken) ? null : _callbackGenerator.ForInvitation(user.Id, blob.InvitationToken, Request),
|
||||
ImageUrl = string.IsNullOrEmpty(blob?.ImageUrl) ? null : await _uriResolver.Resolve(Request.GetAbsoluteRootUri(), UnresolvedUri.Create(blob.ImageUrl)),
|
||||
EmailConfirmed = user.RequiresEmailConfirmation ? user.EmailConfirmed : null,
|
||||
Approved = user.RequiresApproval ? user.Approved : null,
|
||||
|
||||
@@ -49,12 +49,12 @@ namespace BTCPayServer.Services
|
||||
public async Task<string> ForInvitation(ApplicationUser user, HttpRequest request)
|
||||
{
|
||||
var code = await UserManager.GenerateInvitationTokenAsync<ApplicationUser>(user.Id) ?? throw Bug();
|
||||
return ForInvitation(user, code, request);
|
||||
return ForInvitation(user.Id, code, request);
|
||||
}
|
||||
public string ForInvitation(ApplicationUser user, string code, HttpRequest request)
|
||||
public string ForInvitation(string userId, string code, HttpRequest request)
|
||||
{
|
||||
return LinkGenerator.GetUriByAction(nameof(UIAccountController.AcceptInvite), "UIAccount",
|
||||
new { userId = user.Id, code }, request.Scheme, request.Host, request.PathBase) ?? throw Bug();
|
||||
new { userId, code }, request.Scheme, request.Host, request.PathBase) ?? throw Bug();
|
||||
}
|
||||
public async Task<string> ForPasswordReset(ApplicationUser user, HttpRequest request)
|
||||
{
|
||||
|
||||
@@ -4,10 +4,12 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Events;
|
||||
using BTCPayServer.Storage.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@@ -40,17 +42,30 @@ namespace BTCPayServer.Services
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<List<ApplicationUserData>> GetUsersWithRoles()
|
||||
public record ApplicationUserWithRoles(ApplicationUser User, string[] Roles);
|
||||
public async Task<List<ApplicationUserWithRoles>> GetUsersWithRoles()
|
||||
{
|
||||
await using var context = _applicationDbContextFactory.CreateContext();
|
||||
return await (context.Users.Select(p => FromModel(p, p.UserRoles.Join(context.Roles, userRole => userRole.RoleId, role => role.Id,
|
||||
(userRole, role) => role.Name).ToArray()))).ToListAsync();
|
||||
var res = await context.Users.Select(p =>
|
||||
new
|
||||
{
|
||||
User = p,
|
||||
Roles = p.UserRoles.Join(context.Roles, userRole => userRole.RoleId,
|
||||
role => role.Id, (userRole, role) => role.Name).ToArray()
|
||||
})
|
||||
.ToListAsync();
|
||||
return res.Select(p => new ApplicationUserWithRoles(p.User, (p.Roles ?? [])!)).ToList();
|
||||
}
|
||||
|
||||
public static ApplicationUserData FromModel(ApplicationUser data, string?[] roles)
|
||||
public static async Task<T> ForAPI<T>(
|
||||
ApplicationUser data,
|
||||
string?[] roles,
|
||||
CallbackGenerator callbackGenerator,
|
||||
UriResolver uriResolver,
|
||||
HttpRequest request) where T : ApplicationUserData, new()
|
||||
{
|
||||
var blob = data.GetBlob() ?? new();
|
||||
return new ApplicationUserData
|
||||
var blob = data.GetBlob() ?? new UserBlob();
|
||||
return new T
|
||||
{
|
||||
Id = data.Id,
|
||||
Email = data.Email,
|
||||
@@ -60,9 +75,13 @@ namespace BTCPayServer.Services
|
||||
RequiresApproval = data.RequiresApproval,
|
||||
Created = data.Created,
|
||||
Name = blob.Name,
|
||||
ImageUrl = blob.ImageUrl,
|
||||
Roles = roles,
|
||||
Disabled = data.LockoutEnabled && data.LockoutEnd is not null && DateTimeOffset.UtcNow < data.LockoutEnd.Value.UtcDateTime
|
||||
Disabled = IsDisabled(data),
|
||||
ImageUrl = string.IsNullOrEmpty(blob.ImageUrl)
|
||||
? null
|
||||
: await uriResolver.Resolve(request.GetAbsoluteRootUri(), UnresolvedUri.Create(blob.ImageUrl)),
|
||||
InvitationUrl = string.IsNullOrEmpty(blob.InvitationToken) ? null
|
||||
: callbackGenerator.ForInvitation(data.Id, blob.InvitationToken, request)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -78,8 +97,8 @@ namespace BTCPayServer.Services
|
||||
|
||||
private static bool IsDisabled(ApplicationUser user)
|
||||
{
|
||||
return user.LockoutEnabled && user.LockoutEnd is not null &&
|
||||
DateTimeOffset.UtcNow < user.LockoutEnd.Value.UtcDateTime;
|
||||
return user is { LockoutEnabled: true, LockoutEnd: {} lockoutEnd } &&
|
||||
DateTimeOffset.UtcNow < lockoutEnd.UtcDateTime;
|
||||
}
|
||||
|
||||
public static bool TryCanLogin([NotNullWhen(true)] ApplicationUser? user, [MaybeNullWhen(true)] out string error)
|
||||
|
||||
@@ -235,30 +235,25 @@
|
||||
"properties": {
|
||||
"userId": {
|
||||
"type": "string",
|
||||
"description": "The id of the user",
|
||||
"nullable": false
|
||||
"description": "The id of the user (Deprecated, use `id` instead)",
|
||||
"nullable": false,
|
||||
"deprecated": true
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"description": "The role of the user. Default roles are `Owner`, `Manager`, `Employee` and `Guest` (Deprecated, use `storeRole` instead)",
|
||||
"nullable": false,
|
||||
"deprecated": true
|
||||
},
|
||||
"storeRole": {
|
||||
"type": "string",
|
||||
"description": "The role of the user. Default roles are `Owner`, `Manager`, `Employee` and `Guest`",
|
||||
"nullable": false
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"description": "The email of the user",
|
||||
"nullable": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user",
|
||||
"nullable": true
|
||||
},
|
||||
"imageUrl": {
|
||||
"type": "string",
|
||||
"description": "The profile picture URL of the user",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ApplicationUserData"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -520,6 +520,11 @@
|
||||
"description": "The profile picture URL of the user",
|
||||
"nullable": true
|
||||
},
|
||||
"invitationUrl": {
|
||||
"type": "string",
|
||||
"description": "The pending invitation URL of the user",
|
||||
"nullable": true
|
||||
},
|
||||
"emailConfirmed": {
|
||||
"type": "boolean",
|
||||
"description": "True if the email has been confirmed by the user"
|
||||
@@ -546,6 +551,10 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"disabled": {
|
||||
"type": "boolean",
|
||||
"description": "True if an admin has disabled the user"
|
||||
},
|
||||
"roles": {
|
||||
"type": "array",
|
||||
"nullable": false,
|
||||
|
||||
Reference in New Issue
Block a user