diff --git a/BTCPayServer/Controllers/GreenField/GreenfieldUsersController.cs b/BTCPayServer/Controllers/GreenField/GreenfieldUsersController.cs index 5db16c948..877aa5dca 100644 --- a/BTCPayServer/Controllers/GreenField/GreenfieldUsersController.cs +++ b/BTCPayServer/Controllers/GreenField/GreenfieldUsersController.cs @@ -117,7 +117,7 @@ namespace BTCPayServer.Controllers.Greenfield if (user.RequiresApproval) { - var loginLink = _callbackGenerator.ForLogin(user, Request); + var loginLink = _callbackGenerator.ForLogin(user); return await _userService.SetUserApproval(user.Id, request.Approved, loginLink) ? Ok() : this.CreateAPIError("invalid-state", $"User is already {(request.Approved ? "approved" : "unapproved")}"); @@ -416,8 +416,8 @@ namespace BTCPayServer.Controllers.Greenfield var currentUser = await _userManager.GetUserAsync(User); var userEvent = currentUser switch { - { } invitedBy => await UserEvent.Invited.Create(user, invitedBy, _callbackGenerator, Request, request.SendInvitationEmail is not false), - _ => await UserEvent.Registered.Create(user, _callbackGenerator, Request) + { } invitedBy => await UserEvent.Invited.Create(user, invitedBy, _callbackGenerator, request.SendInvitationEmail is not false), + _ => await UserEvent.Registered.Create(user, _callbackGenerator) }; _eventAggregator.Publish(userEvent); diff --git a/BTCPayServer/Controllers/UIAccountController.cs b/BTCPayServer/Controllers/UIAccountController.cs index 2239559d6..d35f06b40 100644 --- a/BTCPayServer/Controllers/UIAccountController.cs +++ b/BTCPayServer/Controllers/UIAccountController.cs @@ -267,7 +267,7 @@ namespace BTCPayServer.Controllers { RememberMe = rememberMe, UserId = user.Id, - LNURLEndpoint = new Uri(callbackGenerator.ForLNUrlAuth(user, r, Request)) + LNURLEndpoint = new Uri(callbackGenerator.ForLNUrlAuth(user, r)) }; } return null; @@ -573,7 +573,7 @@ namespace BTCPayServer.Controllers RegisteredAdmin = true; } - eventAggregator.Publish(await UserEvent.Registered.Create(user, callbackGenerator, Request)); + eventAggregator.Publish(await UserEvent.Registered.Create(user, callbackGenerator)); RegisteredUserId = user.Id; TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Account created."].Value; @@ -640,7 +640,7 @@ namespace BTCPayServer.Controllers var result = await userManager.ConfirmEmailAsync(user, code); if (result.Succeeded) { - var approvalLink = callbackGenerator.ForApproval(user, Request); + var approvalLink = callbackGenerator.ForApproval(user); eventAggregator.Publish(new UserEvent.ConfirmedEmail(user, approvalLink)); var hasPassword = await userManager.HasPasswordAsync(user); @@ -686,7 +686,7 @@ namespace BTCPayServer.Controllers { return RedirectToAction(nameof(ForgotPasswordConfirmation)); } - var callbackUri = await callbackGenerator.ForPasswordReset(user, Request); + var callbackUri = await callbackGenerator.ForPasswordReset(user); eventAggregator.Publish(new UserEvent.PasswordResetRequested(user, callbackUri)); return RedirectToAction(nameof(ForgotPasswordConfirmation)); } diff --git a/BTCPayServer/Controllers/UIManageController.cs b/BTCPayServer/Controllers/UIManageController.cs index 7c1535d92..5fbcaaf4f 100644 --- a/BTCPayServer/Controllers/UIManageController.cs +++ b/BTCPayServer/Controllers/UIManageController.cs @@ -219,7 +219,7 @@ namespace BTCPayServer.Controllers throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } - var callbackUrl = await _callbackGenerator.ForEmailConfirmation(user, Request); + var callbackUrl = await _callbackGenerator.ForEmailConfirmation(user); _eventAggregator.Publish(new UserEvent.ConfirmationEmailRequested(user, callbackUrl)); TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Verification email sent. Please check your email."].Value; return RedirectToAction(nameof(Index)); diff --git a/BTCPayServer/Controllers/UIServerController.Users.cs b/BTCPayServer/Controllers/UIServerController.Users.cs index b52ac5112..e4de5a95a 100644 --- a/BTCPayServer/Controllers/UIServerController.Users.cs +++ b/BTCPayServer/Controllers/UIServerController.Users.cs @@ -69,7 +69,7 @@ namespace BTCPayServer.Controllers InvitationUrl = string.IsNullOrEmpty(blob?.InvitationToken) ? null - : _callbackGenerator.ForInvitation(u.Id, blob.InvitationToken, Request), + : _callbackGenerator.ForInvitation(u.Id, blob.InvitationToken), EmailConfirmed = u.RequiresEmailConfirmation ? u.EmailConfirmed : null, Approved = u.RequiresApproval ? u.Approved : null, Created = u.Created, @@ -95,7 +95,7 @@ namespace BTCPayServer.Controllers Id = user.Id, Email = user.Email, Name = blob?.Name, - InvitationUrl = string.IsNullOrEmpty(blob?.InvitationToken) ? null : _callbackGenerator.ForInvitation(user.Id, blob.InvitationToken, Request), + InvitationUrl = string.IsNullOrEmpty(blob?.InvitationToken) ? null : _callbackGenerator.ForInvitation(user.Id, blob.InvitationToken), 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, @@ -117,7 +117,7 @@ namespace BTCPayServer.Controllers if (user.RequiresApproval && viewModel.Approved.HasValue && user.Approved != viewModel.Approved.Value) { - var loginLink = _callbackGenerator.ForLogin(user, Request); + var loginLink = _callbackGenerator.ForLogin(user); approvalStatusChanged = await _userService.SetUserApproval(user.Id, viewModel.Approved.Value, loginLink); } if (user.RequiresEmailConfirmation && viewModel.EmailConfirmed.HasValue && user.EmailConfirmed != viewModel.EmailConfirmed) @@ -261,7 +261,7 @@ namespace BTCPayServer.Controllers var currentUser = await _UserManager.GetUserAsync(HttpContext.User); var sendEmail = model.SendInvitationEmail && ViewData["CanSendEmail"] is true; - var evt = await UserEvent.Invited.Create(user, currentUser, _callbackGenerator, Request, sendEmail); + var evt = await UserEvent.Invited.Create(user, currentUser, _callbackGenerator, sendEmail); _eventAggregator.Publish(evt); var info = sendEmail @@ -383,7 +383,7 @@ namespace BTCPayServer.Controllers if (user == null) return NotFound(); - var loginLink = _callbackGenerator.ForLogin(user, Request); + var loginLink = _callbackGenerator.ForLogin(user); await _userService.SetUserApproval(userId, approved, loginLink); TempData[WellKnownTempData.SuccessMessage] = approved @@ -411,7 +411,7 @@ namespace BTCPayServer.Controllers throw new ApplicationException($"Unable to load user with ID '{userId}'."); } - var callbackUrl = await _callbackGenerator.ForEmailConfirmation(user, Request); + var callbackUrl = await _callbackGenerator.ForEmailConfirmation(user); _eventAggregator.Publish(new UserEvent.ConfirmationEmailRequested(user, callbackUrl)); TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Verification email sent"].Value; return RedirectToAction(nameof(ListUsers)); diff --git a/BTCPayServer/Controllers/UIStoresController.Users.cs b/BTCPayServer/Controllers/UIStoresController.Users.cs index afbaa8f65..915d0e0b8 100644 --- a/BTCPayServer/Controllers/UIStoresController.Users.cs +++ b/BTCPayServer/Controllers/UIStoresController.Users.cs @@ -74,7 +74,7 @@ public partial class UIStoresController (await _userManager.CreateAsync(user)) is { Succeeded: true }) { var invitationEmail = await _emailSenderFactory.IsComplete(); - var evt = await UserEvent.Invited.Create(user!, currentUser, _callbackGenerator, Request, invitationEmail); + var evt = await UserEvent.Invited.Create(user!, currentUser, _callbackGenerator, invitationEmail); _eventAggregator.Publish(evt); inviteInfo = invitationEmail ? StringLocalizer["An invitation email has been sent.
You may alternatively share this link with them: {0}", evt.InvitationLink] diff --git a/BTCPayServer/Events/UserEvent.cs b/BTCPayServer/Events/UserEvent.cs index 140f57b54..95085711b 100644 --- a/BTCPayServer/Events/UserEvent.cs +++ b/BTCPayServer/Events/UserEvent.cs @@ -30,10 +30,10 @@ public class UserEvent(ApplicationUser user) { public string ApprovalLink { get; } = approvalLink; public string ConfirmationEmailLink { get; set; } = confirmationEmail; - public static async Task Create(ApplicationUser user, CallbackGenerator callbackGenerator, HttpRequest request) + public static async Task Create(ApplicationUser user, CallbackGenerator callbackGenerator) { - var approvalLink = callbackGenerator.ForApproval(user, request); - var confirmationEmail = await callbackGenerator.ForEmailConfirmation(user, request); + var approvalLink = callbackGenerator.ForApproval(user); + var confirmationEmail = await callbackGenerator.ForEmailConfirmation(user); return new Registered(user, approvalLink, confirmationEmail); } } @@ -43,11 +43,11 @@ public class UserEvent(ApplicationUser user) public ApplicationUser InvitedByUser { get; } = invitedBy; public string InvitationLink { get; } = invitationLink; - public static async Task Create(ApplicationUser user, ApplicationUser currentUser, CallbackGenerator callbackGenerator, HttpRequest request, bool sendEmail) + public static async Task Create(ApplicationUser user, ApplicationUser currentUser, CallbackGenerator callbackGenerator, bool sendEmail) { - var invitationLink = await callbackGenerator.ForInvitation(user, request); - var approvalLink = callbackGenerator.ForApproval(user, request); - var confirmationEmail = await callbackGenerator.ForEmailConfirmation(user, request); + var invitationLink = await callbackGenerator.ForInvitation(user); + var approvalLink = callbackGenerator.ForApproval(user); + var confirmationEmail = await callbackGenerator.ForEmailConfirmation(user); return new Invited(user, currentUser, invitationLink, approvalLink, confirmationEmail) { SendInvitationEmail = sendEmail diff --git a/BTCPayServer/Hosting/BTCPayServerServices.cs b/BTCPayServer/Hosting/BTCPayServerServices.cs index 1e0f9408b..fb1d1529a 100644 --- a/BTCPayServer/Hosting/BTCPayServerServices.cs +++ b/BTCPayServer/Hosting/BTCPayServerServices.cs @@ -87,7 +87,7 @@ namespace BTCPayServer.Hosting } public static IServiceCollection AddBTCPayServer(this IServiceCollection services, IConfiguration configuration, Logs logs) { - services.TryAddSingleton(); + services.TryAddScoped(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/BTCPayServer/Services/CallbackGenerator.cs b/BTCPayServer/Services/CallbackGenerator.cs index 2e8dbd9ae..25d10c3ff 100644 --- a/BTCPayServer/Services/CallbackGenerator.cs +++ b/BTCPayServer/Services/CallbackGenerator.cs @@ -15,118 +15,93 @@ using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Http.Extensions; using NBitcoin.DataEncoders; using System.Runtime.CompilerServices; +using BTCPayServer.Abstractions; namespace BTCPayServer.Services { - public class CallbackGenerator(LinkGenerator linkGenerator, UserManager userManager) + public class CallbackGenerator( + LinkGenerator linkGenerator, + UserManager userManager, + IHttpContextAccessor httpContextAccessor) { public LinkGenerator LinkGenerator { get; } = linkGenerator; public UserManager UserManager { get; } = userManager; - public string ForLNUrlAuth(ApplicationUser user, byte[] r, HttpRequest request) - { - return LinkGenerator.GetUriByAction( - action: nameof(UILNURLAuthController.LoginResponse), - controller: "UILNURLAuth", - values: new { userId = user.Id, action = "login", tag = "login", k1 = Encoders.Hex.EncodeData(r) }, - request.Scheme, - request.Host, - request.PathBase) ?? throw Bug(); - } + public string ForLNUrlAuth(ApplicationUser user, byte[] r) + => LinkGenerator.GetUriByAction( + action: nameof(UILNURLAuthController.LoginResponse), + controller: "UILNURLAuth", + values: new { userId = user.Id, action = "login", tag = "login", k1 = Encoders.Hex.EncodeData(r) }, + GetRequestBaseUrl()); - public string StoreUsersLink(string storeId, HttpRequest request) - { - return LinkGenerator.GetUriByAction(nameof(UIStoresController.StoreUsers), "UIStores", - new { storeId }, request.Scheme, request.Host, request.PathBase) ?? throw Bug(); - } + private RequestBaseUrl GetRequestBaseUrl() + => httpContextAccessor.HttpContext?.Request.GetRequestBaseUrl() ?? throw new InvalidOperationException($"You should be in a HttpContext to call this method"); - public async Task ForEmailConfirmation(ApplicationUser user, HttpRequest request) + public string StoreUsersLink(string storeId) + => LinkGenerator.GetUriByAction(nameof(UIStoresController.StoreUsers), "UIStores", + new { storeId }, GetRequestBaseUrl()); + + public async Task ForEmailConfirmation(ApplicationUser user) { var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); return LinkGenerator.GetUriByAction(nameof(UIAccountController.ConfirmEmail), "UIAccount", - new { userId = user.Id, code }, request.Scheme, request.Host, request.PathBase) ?? throw Bug(); + new { userId = user.Id, code }, GetRequestBaseUrl()); } - public async Task ForInvitation(ApplicationUser user, HttpRequest request) + + public async Task ForInvitation(ApplicationUser user) { - var code = await UserManager.GenerateInvitationTokenAsync(user.Id) ?? throw Bug(); - return ForInvitation(user.Id, code, request); + var code = await UserManager.GenerateInvitationTokenAsync(user.Id); + return ForInvitation(user.Id, code ?? "???"); } - public string ForInvitation(string userId, string code, HttpRequest request) - { - return LinkGenerator.GetUriByAction(nameof(UIAccountController.AcceptInvite), "UIAccount", - new { userId, code }, request.Scheme, request.Host, request.PathBase) ?? throw Bug(); - } - public async Task ForPasswordReset(ApplicationUser user, HttpRequest request) + + public string ForInvitation(string userId, string code) + => LinkGenerator.GetUriByAction(nameof(UIAccountController.AcceptInvite), "UIAccount", + new { userId, code }, GetRequestBaseUrl()); + + public async Task ForPasswordReset(ApplicationUser user) { var code = await UserManager.GeneratePasswordResetTokenAsync(user); return LinkGenerator.GetUriByAction( action: nameof(UIAccountController.SetPassword), controller: "UIAccount", values: new { userId = user.Id, code }, - scheme: request.Scheme, - host: request.Host, - pathBase: request.PathBase - ) ?? throw Bug(); + GetRequestBaseUrl()); } - public string ForApproval(ApplicationUser user, HttpRequest request) - { - return LinkGenerator.GetUriByAction(nameof(UIServerController.User), "UIServer", - new { userId = user.Id }, request.Scheme, request.Host, request.PathBase) ?? throw Bug(); - } - public string ForLogin(ApplicationUser user, HttpRequest request) - { - return LinkGenerator.GetUriByAction(nameof(UIAccountController.Login), "UIAccount", new { email = user.Email }, request.Scheme, request.Host, request.PathBase) ?? throw Bug(); - } + public string ForApproval(ApplicationUser user) + => LinkGenerator.GetUriByAction(nameof(UIServerController.User), "UIServer", + new { userId = user.Id }, GetRequestBaseUrl()); - private Exception Bug([CallerMemberName] string? name = null) => new InvalidOperationException($"Error generating link for {name} (Report this bug to BTCPay Server github repository)"); + public string ForLogin(ApplicationUser user) + => LinkGenerator.GetUriByAction(nameof(UIAccountController.Login), "UIAccount", + new { email = user.Email }, GetRequestBaseUrl()); - public string WalletTransactionsLink(WalletId walletId, HttpRequest request) - { - return LinkGenerator.GetUriByAction( - action: nameof(UIWalletsController.WalletTransactions), - controller: "UIWallets", - values: new { walletId = walletId.ToString() }, - scheme: request.Scheme, - host: request.Host, - pathBase: request.PathBase - ) ?? throw Bug(); - } + public string WalletTransactionsLink(WalletId walletId) + => LinkGenerator.GetUriByAction( + action: nameof(UIWalletsController.WalletTransactions), + controller: "UIWallets", + values: new { walletId = walletId.ToString() }, + GetRequestBaseUrl()); - public string PaymentRequestByIdLink(string payReqId, HttpRequest request) - { - return LinkGenerator.GetUriByAction( - action: nameof(UIPaymentRequestController.ViewPaymentRequest), - controller: "UIPaymentRequest", - values: new { payReqId }, - scheme: request.Scheme, - host: request.Host, - pathBase: request.PathBase - ) ?? throw Bug(); - } + public string PaymentRequestByIdLink(string payReqId) + => LinkGenerator.GetUriByAction( + action: nameof(UIPaymentRequestController.ViewPaymentRequest), + controller: "UIPaymentRequest", + values: new { payReqId }, + GetRequestBaseUrl()); - public string PaymentRequestListLink(string storeId, HttpRequest request) - { - return LinkGenerator.GetUriByAction( - action: nameof(UIPaymentRequestController.GetPaymentRequests), - controller: "UIPaymentRequest", - values: new { storeId }, - scheme: request.Scheme, - host: request.Host, - pathBase: request.PathBase - ) ?? throw Bug(); - } + public string PaymentRequestListLink(string storeId) + => LinkGenerator.GetUriByAction( + action: nameof(UIPaymentRequestController.GetPaymentRequests), + controller: "UIPaymentRequest", + values: new { storeId }, + GetRequestBaseUrl()); - public string InvoiceLink(string invoiceId, HttpRequest request) - { - return LinkGenerator.GetUriByAction( - action: nameof(UIInvoiceController.Invoice), - controller: "UIInvoice", - values: new { invoiceId }, - scheme: request.Scheme, - host: request.Host, - pathBase: request.PathBase - ) ?? throw Bug(); - } + public string InvoiceLink(string invoiceId) + => LinkGenerator.GetUriByAction( + action: nameof(UIInvoiceController.Invoice), + controller: "UIInvoice", + values: new { invoiceId }, + GetRequestBaseUrl()); } } diff --git a/BTCPayServer/Services/UserService.cs b/BTCPayServer/Services/UserService.cs index bda7b749a..16cae10eb 100644 --- a/BTCPayServer/Services/UserService.cs +++ b/BTCPayServer/Services/UserService.cs @@ -90,7 +90,7 @@ namespace BTCPayServer.Services ? null : await uriResolver.Resolve(request.GetAbsoluteRootUri(), UnresolvedUri.Create(blob.ImageUrl)), InvitationUrl = string.IsNullOrEmpty(blob.InvitationToken) ? null - : callbackGenerator.ForInvitation(data.Id, blob.InvitationToken, request) + : callbackGenerator.ForInvitation(data.Id, blob.InvitationToken) }; } diff --git a/BTCPayServer/Views/UIPaymentRequest/GetPaymentRequests.cshtml b/BTCPayServer/Views/UIPaymentRequest/GetPaymentRequests.cshtml index 1795fa572..7fdd1bff6 100644 --- a/BTCPayServer/Views/UIPaymentRequest/GetPaymentRequests.cshtml +++ b/BTCPayServer/Views/UIPaymentRequest/GetPaymentRequests.cshtml @@ -123,7 +123,7 @@