2021-04-06 18:00:07 -07:00
#nullable enable
2020-10-03 14:12:55 +02:00
using System ;
2020-03-13 11:47:22 +01:00
using System.Linq ;
2020-03-18 23:10:15 +09:00
using System.Threading ;
2020-03-12 14:59:24 +01:00
using System.Threading.Tasks ;
2020-11-17 13:46:23 +01:00
using BTCPayServer.Abstractions.Constants ;
2022-02-24 09:00:44 +01:00
using BTCPayServer.Abstractions.Extensions ;
2020-06-28 17:55:27 +09:00
using BTCPayServer.Client ;
2020-03-12 14:59:24 +01:00
using BTCPayServer.Client.Models ;
2020-03-13 11:47:22 +01:00
using BTCPayServer.Configuration ;
2020-03-12 14:59:24 +01:00
using BTCPayServer.Data ;
2020-03-13 11:47:22 +01:00
using BTCPayServer.Events ;
2021-12-31 16:59:02 +09:00
using BTCPayServer.Logging ;
2020-03-12 14:59:24 +01:00
using BTCPayServer.Security ;
2022-01-14 13:05:23 +09:00
using BTCPayServer.Security.Greenfield ;
2020-03-13 11:47:22 +01:00
using BTCPayServer.Services ;
2020-03-12 14:59:24 +01:00
using Microsoft.AspNetCore.Authorization ;
2020-06-30 08:26:19 +02:00
using Microsoft.AspNetCore.Cors ;
2020-03-12 14:59:24 +01:00
using Microsoft.AspNetCore.Identity ;
using Microsoft.AspNetCore.Mvc ;
2020-03-18 23:10:15 +09:00
using NicolasDorier.RateLimits ;
2020-03-12 14:59:24 +01:00
2022-01-14 13:05:23 +09:00
namespace BTCPayServer.Controllers.Greenfield
2020-03-12 14:59:24 +01:00
{
[ApiController]
2020-03-23 14:23:23 +01:00
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
2020-06-30 08:26:19 +02:00
[EnableCors(CorsPolicies.All)]
2022-01-07 12:17:59 +09:00
public class GreenfieldUsersController : ControllerBase
2020-03-12 14:59:24 +01:00
{
2022-05-24 13:18:16 +09:00
public PoliciesSettings PoliciesSettings { get ; }
2021-11-22 17:16:08 +09:00
public Logs Logs { get ; }
2020-03-12 14:59:24 +01:00
private readonly UserManager < ApplicationUser > _userManager ;
2020-03-13 11:47:22 +01:00
private readonly RoleManager < IdentityRole > _roleManager ;
private readonly SettingsRepository _settingsRepository ;
private readonly EventAggregator _eventAggregator ;
2020-03-18 20:51:50 +09:00
private readonly IPasswordValidator < ApplicationUser > _passwordValidator ;
2022-06-21 12:33:20 +09:00
private readonly IRateLimitService _throttleService ;
2020-03-19 13:30:53 +09:00
private readonly BTCPayServerOptions _options ;
2020-03-18 23:10:15 +09:00
private readonly IAuthorizationService _authorizationService ;
2021-03-14 12:24:32 -07:00
private readonly UserService _userService ;
2020-03-12 14:59:24 +01:00
2022-01-07 12:17:59 +09:00
public GreenfieldUsersController ( UserManager < ApplicationUser > userManager ,
2021-12-31 16:59:02 +09:00
RoleManager < IdentityRole > roleManager ,
2020-12-11 15:11:08 +01:00
SettingsRepository settingsRepository ,
2022-05-24 13:18:16 +09:00
PoliciesSettings policiesSettings ,
2020-03-18 20:51:50 +09:00
EventAggregator eventAggregator ,
2020-03-18 23:10:15 +09:00
IPasswordValidator < ApplicationUser > passwordValidator ,
2022-06-21 12:33:20 +09:00
IRateLimitService throttleService ,
2020-03-20 14:05:23 +01:00
BTCPayServerOptions options ,
2020-12-04 08:08:05 +01:00
IAuthorizationService authorizationService ,
2021-11-22 17:16:08 +09:00
UserService userService ,
Logs logs )
2020-03-12 14:59:24 +01:00
{
2021-11-22 17:16:08 +09:00
this . Logs = logs ;
2020-03-12 14:59:24 +01:00
_userManager = userManager ;
2020-03-13 11:47:22 +01:00
_roleManager = roleManager ;
_settingsRepository = settingsRepository ;
2022-05-24 13:18:16 +09:00
PoliciesSettings = policiesSettings ;
2020-03-13 11:47:22 +01:00
_eventAggregator = eventAggregator ;
2020-03-18 20:51:50 +09:00
_passwordValidator = passwordValidator ;
2020-03-18 23:10:15 +09:00
_throttleService = throttleService ;
2020-03-19 13:30:53 +09:00
_options = options ;
2020-03-18 23:10:15 +09:00
_authorizationService = authorizationService ;
2021-03-14 12:24:32 -07:00
_userService = userService ;
2020-03-12 14:59:24 +01:00
}
2020-03-13 11:47:22 +01:00
2022-02-15 16:19:52 +01:00
[Authorize(Policy = Policies.CanViewUsers, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpGet("~/api/v1/users/{idOrEmail}")]
public async Task < IActionResult > GetUser ( string idOrEmail )
{
var user = ( await _userManager . FindByIdAsync ( idOrEmail ) ) ? ? await _userManager . FindByEmailAsync ( idOrEmail ) ;
if ( user ! = null )
{
return Ok ( await FromModel ( user ) ) ;
}
return UserNotFound ( ) ;
}
2022-04-26 14:27:35 +02:00
[Authorize(Policy = Policies.CanModifyServerSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpPost("~/api/v1/users/{idOrEmail}/lock")]
public async Task < IActionResult > LockUser ( string idOrEmail , LockUserRequest request )
{
var user = ( await _userManager . FindByIdAsync ( idOrEmail ) ) ? ? await _userManager . FindByEmailAsync ( idOrEmail ) ;
if ( user is null )
{
return UserNotFound ( ) ;
}
2022-02-15 16:19:52 +01:00
2022-04-26 14:27:35 +02:00
await _userService . ToggleUser ( user . Id , request . Locked ? DateTimeOffset . MaxValue : null ) ;
return Ok ( ) ;
}
2022-02-15 16:19:52 +01:00
[Authorize(Policy = Policies.CanViewUsers, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpGet("~/api/v1/users/")]
public async Task < ActionResult < ApplicationUserData [ ] > > GetUsers ( )
{
return Ok ( await _userService . GetUsersWithRoles ( ) ) ;
}
2020-03-23 14:23:23 +01:00
[Authorize(Policy = Policies.CanViewProfile, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
2020-03-12 14:59:24 +01:00
[HttpGet("~/api/v1/users/me")]
public async Task < ActionResult < ApplicationUserData > > GetCurrentUser ( )
{
var user = await _userManager . GetUserAsync ( User ) ;
2020-09-16 14:17:33 +02:00
return await FromModel ( user ) ;
2020-03-12 14:59:24 +01:00
}
2020-03-13 11:47:22 +01:00
2021-04-07 20:40:57 -07:00
[Authorize(Policy = Policies.CanDeleteUser, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpDelete("~/api/v1/users/me")]
2021-04-10 22:27:17 -07:00
public async Task < IActionResult > DeleteCurrentUser ( )
2021-04-07 20:40:57 -07:00
{
2021-06-04 12:20:45 +02:00
return await DeleteUser ( _userManager . GetUserId ( User ) ) ;
2021-04-07 20:40:57 -07:00
}
2020-03-18 23:10:15 +09:00
[AllowAnonymous]
2020-03-13 11:47:22 +01:00
[HttpPost("~/api/v1/users")]
2020-06-03 11:58:49 +02:00
public async Task < IActionResult > CreateUser ( CreateApplicationUserRequest request , CancellationToken cancellationToken = default )
2020-03-13 11:47:22 +01:00
{
2021-04-06 18:00:07 -07:00
if ( request . Email is null )
2020-06-03 11:58:49 +02:00
ModelState . AddModelError ( nameof ( request . Email ) , "Email is missing" ) ;
2022-06-23 13:41:52 +09:00
if ( ! string . IsNullOrEmpty ( request . Email ) & & ! MailboxAddressValidator . IsMailboxAddress ( request . Email ) )
2020-03-18 20:51:50 +09:00
{
2020-06-03 11:58:49 +02:00
ModelState . AddModelError ( nameof ( request . Email ) , "Invalid email" ) ;
2020-03-18 20:51:50 +09:00
}
2021-04-06 18:00:07 -07:00
if ( request . Password is null )
2020-06-03 11:58:49 +02:00
ModelState . AddModelError ( nameof ( request . Password ) , "Password is missing" ) ;
if ( ! ModelState . IsValid )
{
2020-06-08 23:40:58 +09:00
return this . CreateValidationError ( ModelState ) ;
2020-06-03 11:58:49 +02:00
}
2021-12-27 13:46:31 +09:00
if ( User . Identity is null )
throw new JsonHttpException ( this . StatusCode ( 401 ) ) ;
2020-03-13 11:47:22 +01:00
var anyAdmin = ( await _userManager . GetUsersInRoleAsync ( Roles . ServerAdmin ) ) . Any ( ) ;
2020-03-18 23:10:15 +09:00
var policies = await _settingsRepository . GetSettingAsync < PoliciesSettings > ( ) ? ? new PoliciesSettings ( ) ;
2022-01-14 13:05:23 +09:00
var isAuth = User . Identity . AuthenticationType = = GreenfieldConstants . AuthenticationType ;
2020-03-18 23:10:15 +09:00
// If registration are locked and that an admin exists, don't accept unauthenticated connection
if ( anyAdmin & & policies . LockSubscription & & ! isAuth )
2021-12-16 15:04:06 +01:00
return this . CreateAPIError ( 401 , "unauthenticated" , "New user creation isn't authorized to users who are not admin" ) ;
2020-03-18 23:10:15 +09:00
// Even if subscription are unlocked, it is forbidden to create admin unauthenticated
if ( anyAdmin & & request . IsAdministrator is true & & ! isAuth )
2021-12-16 15:04:06 +01:00
return this . CreateAPIError ( 401 , "unauthenticated" , "New admin creation isn't authorized to users who are not admin" ) ;
2020-03-18 23:10:15 +09:00
// You are de-facto admin if there is no other admin, else you need to be auth and pass policy requirements
2020-03-20 18:38:21 +09:00
bool isAdmin = anyAdmin ? ( await _authorizationService . AuthorizeAsync ( User , null , new PolicyRequirement ( Policies . CanModifyServerSettings ) ) ) . Succeeded
& & ( await _authorizationService . AuthorizeAsync ( User , null , new PolicyRequirement ( Policies . Unrestricted ) ) ) . Succeeded
2020-03-18 23:10:15 +09:00
& & isAuth
: true ;
// You need to be admin to create an admin
if ( request . IsAdministrator is true & & ! isAdmin )
2021-12-16 15:04:06 +01:00
return this . CreateAPIPermissionError ( Policies . Unrestricted , $"Insufficient API Permissions. Please use an API key with permission: {Policies.Unrestricted} and be an admin." ) ;
2020-03-18 23:10:15 +09:00
2022-05-24 13:18:16 +09:00
if ( ! isAdmin & & ( policies . LockSubscription | | PoliciesSettings . DisableNonAdminCreateUserApi ) )
2020-03-18 23:10:15 +09:00
{
2020-03-18 18:55:45 -05:00
// If we are not admin and subscriptions are locked, we need to check the Policies.CanCreateUser.Key permission
2020-03-20 13:44:02 +09:00
var canCreateUser = ( await _authorizationService . AuthorizeAsync ( User , null , new PolicyRequirement ( Policies . CanCreateUser ) ) ) . Succeeded ;
2020-03-18 19:01:27 -05:00
if ( ! isAuth | | ! canCreateUser )
2021-12-16 15:04:06 +01:00
return this . CreateAPIPermissionError ( Policies . CanCreateUser ) ;
2020-03-18 23:10:15 +09:00
}
2020-03-13 11:47:22 +01:00
var user = new ApplicationUser
{
UserName = request . Email ,
Email = request . Email ,
2020-10-03 14:12:55 +02:00
RequiresEmailConfirmation = policies . RequiresConfirmedEmail ,
Created = DateTimeOffset . UtcNow ,
2020-03-13 11:47:22 +01:00
} ;
2020-03-18 20:51:50 +09:00
var passwordValidation = await this . _passwordValidator . ValidateAsync ( _userManager , user , request . Password ) ;
if ( ! passwordValidation . Succeeded )
{
foreach ( var error in passwordValidation . Errors )
{
ModelState . AddModelError ( nameof ( request . Password ) , error . Description ) ;
}
2020-06-08 23:40:58 +09:00
return this . CreateValidationError ( ModelState ) ;
2020-03-18 20:51:50 +09:00
}
2020-03-18 23:10:15 +09:00
if ( ! isAdmin )
{
if ( ! await _throttleService . Throttle ( ZoneLimits . Register , this . HttpContext . Connection . RemoteIpAddress , cancellationToken ) )
return new TooManyRequestsResult ( ZoneLimits . Register ) ;
}
2020-03-18 20:51:50 +09:00
var identityResult = await _userManager . CreateAsync ( user , request . Password ) ;
2020-03-13 11:47:22 +01:00
if ( ! identityResult . Succeeded )
{
2020-03-18 20:51:50 +09:00
foreach ( var error in identityResult . Errors )
{
2020-06-08 23:40:58 +09:00
if ( error . Code = = "DuplicateUserName" )
ModelState . AddModelError ( nameof ( request . Email ) , error . Description ) ;
else
ModelState . AddModelError ( string . Empty , error . Description ) ;
2020-03-18 20:51:50 +09:00
}
2020-06-08 23:40:58 +09:00
return this . CreateValidationError ( ModelState ) ;
2020-03-13 11:47:22 +01:00
}
2020-03-18 23:10:15 +09:00
if ( request . IsAdministrator is true )
2020-03-13 11:47:22 +01:00
{
2020-03-18 23:10:15 +09:00
if ( ! anyAdmin )
{
await _roleManager . CreateAsync ( new IdentityRole ( Roles . ServerAdmin ) ) ;
}
2020-03-13 11:47:22 +01:00
await _userManager . AddToRoleAsync ( user , Roles . ServerAdmin ) ;
2020-03-18 23:10:15 +09:00
if ( ! anyAdmin )
{
2020-12-17 12:27:01 +01:00
var settings = await _settingsRepository . GetSettingAsync < ThemeSettings > ( ) ;
2021-12-31 16:59:02 +09:00
if ( settings ! = null )
{
2021-04-06 18:00:07 -07:00
settings . FirstRun = false ;
await _settingsRepository . UpdateSetting ( settings ) ;
}
2020-12-17 12:27:01 +01:00
2021-11-22 17:16:08 +09:00
await _settingsRepository . FirstAdminRegistered ( policies , _options . UpdateUrl ! = null , _options . DisableRegistration , Logs ) ;
2020-03-18 23:10:15 +09:00
}
2020-03-13 11:47:22 +01:00
}
2020-06-28 17:55:27 +09:00
_eventAggregator . Publish ( new UserRegisteredEvent ( ) { RequestUri = Request . GetAbsoluteRootUri ( ) , User = user , Admin = request . IsAdministrator is true } ) ;
2020-09-16 14:17:33 +02:00
var model = await FromModel ( user ) ;
return CreatedAtAction ( string . Empty , model ) ;
2020-03-13 11:47:22 +01:00
}
2021-03-07 16:50:55 -08:00
[HttpDelete("~/api/v1/users/{userId}")]
2021-04-10 19:33:37 -07:00
[Authorize(Policy = Policies.CanModifyServerSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
2021-04-10 22:27:17 -07:00
public async Task < IActionResult > DeleteUser ( string userId )
2021-03-07 16:50:55 -08:00
{
2021-04-10 21:15:18 -07:00
var user = await _userManager . FindByIdAsync ( userId ) ;
2021-03-07 16:50:55 -08:00
if ( user = = null )
{
2021-05-16 17:48:25 -07:00
return UserNotFound ( ) ;
2021-03-07 16:50:55 -08:00
}
// We can safely delete the user if it's not an admin user
2021-04-06 18:19:31 -07:00
if ( ! ( await _userService . IsAdminUser ( user ) ) )
2021-03-07 16:50:55 -08:00
{
2021-03-14 12:24:32 -07:00
await _userService . DeleteUserAndAssociatedData ( user ) ;
2021-03-07 16:50:55 -08:00
return Ok ( ) ;
}
// User shouldn't be deleted if it's the only admin
2022-04-26 14:27:35 +02:00
if ( await _userService . IsUserTheOnlyOneAdmin ( user ) )
2021-03-07 16:50:55 -08:00
{
return Forbid ( AuthenticationSchemes . GreenfieldBasic ) ;
}
// Ok, this user is an admin but there are other admins as well so safe to delete
2021-03-14 12:24:32 -07:00
await _userService . DeleteUserAndAssociatedData ( user ) ;
2021-03-07 16:50:55 -08:00
return Ok ( ) ;
}
2020-09-16 14:17:33 +02:00
private async Task < ApplicationUserData > FromModel ( ApplicationUser data )
2020-03-12 14:59:24 +01:00
{
2020-09-16 14:17:33 +02:00
var roles = ( await _userManager . GetRolesAsync ( data ) ) . ToArray ( ) ;
2022-02-15 16:19:52 +01:00
return UserService . FromModel ( data , roles ) ;
2020-03-12 14:59:24 +01:00
}
2021-04-07 20:40:57 -07:00
2022-04-26 14:27:35 +02:00
2021-05-16 17:48:25 -07:00
private IActionResult UserNotFound ( )
{
return this . CreateAPIError ( 404 , "user-not-found" , "The user was not found" ) ;
}
2020-03-12 14:59:24 +01:00
}
}