mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-01-18 13:26:47 +01:00
00cc16455c
* App: Add events which the app subscribes to Various events, which are relevant for the app to react to changes made on the server. * Refactor events * Do not extend NewBlockEvent * Refactoring events * Add store role events * Refactoring: Rename StoreUserEvent * Fix: Subscribe to UserEvent.Invited --------- Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
143 lines
6.4 KiB
C#
143 lines
6.4 KiB
C#
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using BTCPayServer.Data;
|
|
using BTCPayServer.Events;
|
|
using BTCPayServer.Logging;
|
|
using BTCPayServer.Services;
|
|
using BTCPayServer.Services.Mails;
|
|
using BTCPayServer.Services.Notifications;
|
|
using BTCPayServer.Services.Notifications.Blobs;
|
|
using BTCPayServer.Services.Stores;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace BTCPayServer.HostedServices;
|
|
|
|
public class UserEventHostedService(
|
|
EventAggregator eventAggregator,
|
|
UserManager<ApplicationUser> userManager,
|
|
CallbackGenerator callbackGenerator,
|
|
EmailSenderFactory emailSenderFactory,
|
|
NotificationSender notificationSender,
|
|
StoreRepository storeRepository,
|
|
Logs logs)
|
|
: EventHostedServiceBase(eventAggregator, logs)
|
|
{
|
|
public UserManager<ApplicationUser> UserManager { get; } = userManager;
|
|
public CallbackGenerator CallbackGenerator { get; } = callbackGenerator;
|
|
|
|
protected override void SubscribeToEvents()
|
|
{
|
|
Subscribe<UserEvent.Registered>();
|
|
Subscribe<UserEvent.Invited>();
|
|
Subscribe<UserEvent.Approved>();
|
|
Subscribe<UserEvent.ConfirmedEmail>();
|
|
Subscribe<UserEvent.PasswordResetRequested>();
|
|
Subscribe<UserEvent.InviteAccepted>();
|
|
}
|
|
|
|
protected override async Task ProcessEvent(object evt, CancellationToken cancellationToken)
|
|
{
|
|
ApplicationUser user = (evt as UserEvent).User;
|
|
IEmailSender emailSender;
|
|
switch (evt)
|
|
{
|
|
case UserEvent.Registered ev:
|
|
// can be either a self-registration or by invite from another user
|
|
var type = await UserManager.IsInRoleAsync(user, Roles.ServerAdmin) ? "admin" : "user";
|
|
var info = ev switch
|
|
{
|
|
UserEvent.Invited { InvitedByUser: { } invitedBy } => $"invited by {invitedBy.Email}",
|
|
UserEvent.Invited => "invited",
|
|
_ => "registered"
|
|
};
|
|
var requiresApproval = user.RequiresApproval && !user.Approved;
|
|
var requiresEmailConfirmation = user.RequiresEmailConfirmation && !user.EmailConfirmed;
|
|
|
|
// log registration info
|
|
var newUserInfo = $"New {type} {user.Email} {info}";
|
|
Logs.PayServer.LogInformation(newUserInfo);
|
|
|
|
// send notification if the user does not require email confirmation.
|
|
// inform admins only about qualified users and not annoy them with bot registrations.
|
|
if (requiresApproval && !requiresEmailConfirmation)
|
|
{
|
|
await NotifyAdminsAboutUserRequiringApproval(user, ev.ApprovalLink, newUserInfo);
|
|
}
|
|
|
|
// set callback result and send email to user
|
|
emailSender = await emailSenderFactory.GetEmailSender();
|
|
if (ev is UserEvent.Invited invited)
|
|
{
|
|
if (invited.SendInvitationEmail)
|
|
emailSender.SendInvitation(user.GetMailboxAddress(), invited.InvitationLink);
|
|
}
|
|
else if (requiresEmailConfirmation)
|
|
{
|
|
emailSender.SendEmailConfirmation(user.GetMailboxAddress(), ev.ConfirmationEmailLink);
|
|
}
|
|
break;
|
|
|
|
case UserEvent.PasswordResetRequested pwResetEvent:
|
|
Logs.PayServer.LogInformation("User {Email} requested a password reset", user.Email);
|
|
emailSender = await emailSenderFactory.GetEmailSender();
|
|
emailSender.SendResetPassword(user.GetMailboxAddress(), pwResetEvent.ResetLink);
|
|
break;
|
|
|
|
case UserEvent.Approved approvedEvent:
|
|
if (!user.Approved) break;
|
|
emailSender = await emailSenderFactory.GetEmailSender();
|
|
emailSender.SendApprovalConfirmation(user.GetMailboxAddress(), approvedEvent.LoginLink);
|
|
break;
|
|
|
|
case UserEvent.ConfirmedEmail confirmedEvent:
|
|
if (!user.EmailConfirmed) break;
|
|
var confirmedUserInfo = $"User {user.Email} confirmed their email address";
|
|
Logs.PayServer.LogInformation(confirmedUserInfo);
|
|
await NotifyAdminsAboutUserRequiringApproval(user, confirmedEvent.ApprovalLink, confirmedUserInfo);
|
|
break;
|
|
|
|
case UserEvent.InviteAccepted inviteAcceptedEvent:
|
|
Logs.PayServer.LogInformation("User {Email} accepted the invite", user.Email);
|
|
await NotifyAboutUserAcceptingInvite(user, inviteAcceptedEvent.StoreUsersLink);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private async Task NotifyAdminsAboutUserRequiringApproval(ApplicationUser user, string approvalLink, string newUserInfo)
|
|
{
|
|
if (!user.RequiresApproval || user.Approved) return;
|
|
// notification
|
|
await notificationSender.SendNotification(new AdminScope(), new NewUserRequiresApprovalNotification(user));
|
|
// email
|
|
var admins = await UserManager.GetUsersInRoleAsync(Roles.ServerAdmin);
|
|
var emailSender = await emailSenderFactory.GetEmailSender();
|
|
foreach (var admin in admins)
|
|
{
|
|
emailSender.SendNewUserInfo(admin.GetMailboxAddress(), newUserInfo, approvalLink);
|
|
}
|
|
}
|
|
|
|
private async Task NotifyAboutUserAcceptingInvite(ApplicationUser user, string storeUsersLink)
|
|
{
|
|
var stores = await storeRepository.GetStoresByUserId(user.Id);
|
|
var notifyRoles = new[] { StoreRoleId.Owner, StoreRoleId.Manager };
|
|
foreach (var store in stores)
|
|
{
|
|
// notification
|
|
await notificationSender.SendNotification(new StoreScope(store.Id, notifyRoles), new InviteAcceptedNotification(user, store));
|
|
// email
|
|
var notifyUsers = await storeRepository.GetStoreUsers(store.Id, notifyRoles);
|
|
var link = string.Format(storeUsersLink, store.Id);
|
|
var emailSender = await emailSenderFactory.GetEmailSender(store.Id);
|
|
foreach (var storeUser in notifyUsers)
|
|
{
|
|
if (storeUser.Id == user.Id) continue; // do not notify the user itself (if they were added as owner or manager)
|
|
var notifyUser = await UserManager.FindByIdOrEmail(storeUser.Id);
|
|
var info = $"User {user.Email} accepted the invite to {store.StoreName}";
|
|
emailSender.SendUserInviteAcceptedInfo(notifyUser.GetMailboxAddress(), info, link);
|
|
}
|
|
}
|
|
}
|
|
}
|