Onboarding: Invite new users (#5714)

* Server Users: More precise message when inviting users

This lets the admin who invited a new user know whether or not an email has been sent. If the SMTP server hasn't been set up, they need to share the invite link with the user.

* Onboarding: Invite new users

- Separates the user self-registration and invite cases
- Adds invitation email for users created by the admin
- Adds invitation tokens to verify user was invited
- Adds handler action for invite links
- Refactors `UserEventHostedService`

* Remove duplicate status message from views that use the wizard layout

* Auto-approve users created by an admin

* Notify admins via email if a new account requires approval

* Update wording

* Fix update user error

* Fix redirect to email confirmation in invite action

* Fix precondition checks after signup

* Improve admin notification

Send notification only if the user does not require email confirmation or when they confirmed their email address. Rationale: We want to inform admins only about qualified users and not annoy them with bot registrations.

* Allow approval alongside resending confirm email

* Use user email in log messages instead of ID

* Prevent unnecessary notification after email confirmation

* Use ApplicationUser type explicitly

* Fix after rebase

* Refactoring: Do not subclass UserRegisteredEvent
This commit is contained in:
d11n
2024-02-28 12:43:18 +01:00
committed by GitHub
parent 8b446e2791
commit e43b4ed540
24 changed files with 394 additions and 237 deletions

View File

@@ -185,7 +185,7 @@ namespace BTCPayServer.Controllers.Greenfield
RequiresEmailConfirmation = policies.RequiresConfirmedEmail,
RequiresApproval = policies.RequiresUserApproval,
Created = DateTimeOffset.UtcNow,
Approved = !anyAdmin && isAdmin // auto-approve first admin
Approved = isAdmin // auto-approve first admin and users created by an admin
};
var passwordValidation = await this._passwordValidator.ValidateAsync(_userManager, user, request.Password);
if (!passwordValidation.Succeeded)
@@ -214,7 +214,8 @@ namespace BTCPayServer.Controllers.Greenfield
return this.CreateValidationError(ModelState);
}
if (request.IsAdministrator is true)
var isNewAdmin = request.IsAdministrator is true;
if (isNewAdmin)
{
if (!anyAdmin)
{
@@ -233,7 +234,21 @@ namespace BTCPayServer.Controllers.Greenfield
await _settingsRepository.FirstAdminRegistered(policies, _options.UpdateUrl != null, _options.DisableRegistration, Logs);
}
}
_eventAggregator.Publish(new UserRegisteredEvent() { RequestUri = Request.GetAbsoluteRootUri(), User = user, Admin = request.IsAdministrator is true });
var currentUser = await _userManager.GetUserAsync(User);
var userEvent = new UserRegisteredEvent
{
RequestUri = Request.GetAbsoluteRootUri(),
Admin = isNewAdmin,
User = user
};
if (currentUser is not null)
{
userEvent.Kind = UserRegisteredEventKind.Invite;
userEvent.InvitedByUser = currentUser;
};
_eventAggregator.Publish(userEvent);
var model = await FromModel(user);
return CreatedAtAction(string.Empty, model);
}