diff --git a/Server/Components/Account/Pages/ForgotPassword.razor b/Server/Components/Account/Pages/ForgotPassword.razor index d03ab1f27..a0af37223 100644 --- a/Server/Components/Account/Pages/ForgotPassword.razor +++ b/Server/Components/Account/Pages/ForgotPassword.razor @@ -40,7 +40,9 @@ private async Task OnValidSubmitAsync() { var user = await UserManager.FindByEmailAsync(Input.Email); - if (user is null || !(await UserManager.IsEmailConfirmedAsync(user))) + // if (user is null || !(await UserManager.IsEmailConfirmedAsync(user))) + // Confirmation is currently disabled by default. + if (user is null) { // Don't reveal that the user does not exist or is not confirmed RedirectManager.RedirectTo("Account/ForgotPasswordConfirmation"); diff --git a/Server/Components/_Imports.razor b/Server/Components/_Imports.razor index a3afb55f4..bbc5eac2e 100644 --- a/Server/Components/_Imports.razor +++ b/Server/Components/_Imports.razor @@ -25,5 +25,4 @@ @using Remotely.Server.Components.TreeView @using Remotely.Server.Auth @using Remotely.Shared.Entities -@using Remotely.Server.Models -@using Remotely.Server.Enums; \ No newline at end of file +@using Remotely.Server.Models \ No newline at end of file diff --git a/Server/Program.cs b/Server/Program.cs index d6de58a20..829d0cdfd 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -1,31 +1,31 @@ +using Immense.RemoteControl.Server.Extensions; +using Immense.SimpleMessenger; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Server.Circuits; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.RateLimiting; using Microsoft.AspNetCore.StaticFiles; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.FileProviders; using Remotely.Server.Auth; +using Remotely.Server.Components; +using Remotely.Server.Components.Account; using Remotely.Server.Data; +using Remotely.Server.Extensions; using Remotely.Server.Hubs; +using Remotely.Server.Models; +using Remotely.Server.Options; using Remotely.Server.Services; -using System.Net; -using Immense.RemoteControl.Server.Extensions; using Remotely.Server.Services.RcImplementations; +using Remotely.Server.Services.Stores; +using Remotely.Shared.Entities; using Remotely.Shared.Services; using Serilog; -using Microsoft.AspNetCore.RateLimiting; +using System.Net; using RatePolicyNames = Remotely.Server.RateLimiting.PolicyNames; -using Remotely.Shared.Entities; -using Immense.SimpleMessenger; -using Remotely.Server.Services.Stores; -using Remotely.Server.Components.Account; -using Remotely.Server.Components; -using Remotely.Server.Options; -using Remotely.Server.Extensions; -using Remotely.Server.Models; var builder = WebApplication.CreateBuilder(args); var configuration = builder.Configuration; @@ -57,19 +57,22 @@ { case "sqlite": services.AddDbContext( - contextLifetime: ServiceLifetime.Transient, + contextLifetime: ServiceLifetime.Transient, optionsLifetime: ServiceLifetime.Transient); break; + case "sqlserver": services.AddDbContext( contextLifetime: ServiceLifetime.Transient, optionsLifetime: ServiceLifetime.Transient); break; + case "postgresql": services.AddDbContext( contextLifetime: ServiceLifetime.Transient, optionsLifetime: ServiceLifetime.Transient); break; + default: throw new InvalidOperationException( $"Invalid DBProvider: {dbProvider}. Ensure a valid value " + @@ -112,7 +115,6 @@ .AddSignInManager() .AddDefaultTokenProviders(); - services.AddScoped(); services.AddScoped(); services.AddScoped(); @@ -154,7 +156,7 @@ services.AddCors(options => { - if (settings.TrustedCorsOrigins is { Count: > 0} trustedOrigins) + if (settings.TrustedCorsOrigins is { Count: > 0 } trustedOrigins) { options.AddPolicy("TrustedOriginPolicy", builder => builder .WithOrigins(trustedOrigins.ToArray()) @@ -165,7 +167,6 @@ } }); - services.Configure(options => { options.ForwardedHeaders = ForwardedHeaders.All; @@ -177,7 +178,7 @@ options.KnownProxies.Add(dockerGatewayIp); } - if (settings.KnownProxies is { Count: >0 } knownProxies) + if (settings.KnownProxies is { Count: > 0 } knownProxies) { foreach (var proxy in knownProxies) { @@ -207,7 +208,7 @@ { clOptions.QueueLimit = int.MaxValue; - clOptions.PermitLimit = + clOptions.PermitLimit = settings.MaxConcurrentUpdates <= 0 ? 10 : settings.MaxConcurrentUpdates; @@ -222,6 +223,7 @@ } else { + services.AddScoped, EmailSenderEx>(); services.AddScoped(); } services.AddSingleton(); @@ -367,13 +369,12 @@ void ConfigureSerilog(WebApplicationBuilder webAppBuilder, SettingsModel setting { try { - var dataRetentionDays = settings.DataRetentionInDays; if (dataRetentionDays <= 0) { dataRetentionDays = 7; } - + var logPath = LogsManager.DefaultLogsDirectory; void ApplySharedLoggerConfig(LoggerConfiguration loggerConfiguration) diff --git a/Server/Services/EmailSender.cs b/Server/Services/EmailSender.cs index 55d9d1b26..736ccdba1 100644 --- a/Server/Services/EmailSender.cs +++ b/Server/Services/EmailSender.cs @@ -1,21 +1,16 @@ using MailKit.Net.Smtp; -using MailKit.Security; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.Build.Framework; -using Microsoft.Extensions.Logging; -using Microsoft.Identity.Client; using MimeKit; using MimeKit.Text; -using NuGet.Configuration; -using System; -using System.Net; -using System.Threading.Tasks; +using Remotely.Shared.Entities; namespace Remotely.Server.Services; public interface IEmailSenderEx { Task SendEmailAsync(string email, string replyTo, string subject, string htmlMessage, string? organizationID = null); + Task SendEmailAsync(string email, string subject, string htmlMessage, string? organizationID = null); } @@ -34,7 +29,7 @@ public Task SendEmailAsync(string email, string subject, string htmlMessage) } } -public class EmailSenderEx : IEmailSenderEx +public class EmailSenderEx : IEmailSenderEx, IEmailSender { private readonly IDataService _dataService; private readonly ILogger _logger; @@ -46,11 +41,22 @@ public EmailSenderEx( _dataService = dataService; _logger = logger; } + + public async Task SendConfirmationLinkAsync(RemotelyUser user, string email, string confirmationLink) + { + await SendEmailAsync( + email, + "Remotely Account Confirmation", + "Please confirm your Remotely account by clicking the following link: " + + $"{confirmationLink}", + user.OrganizationID); + } + public async Task SendEmailAsync( - string toEmail, - string replyTo, - string subject, - string htmlMessage, + string toEmail, + string replyTo, + string subject, + string htmlMessage, string? organizationID = null) { try @@ -68,7 +74,7 @@ public async Task SendEmailAsync( }; using var client = new SmtpClient(); - + if (!string.IsNullOrWhiteSpace(settings.SmtpLocalDomain)) { client.LocalDomain = settings.SmtpLocalDomain; @@ -103,7 +109,28 @@ public async Task SendEmailAsync(string email, string subject, string html var settings = await _dataService.GetSettings(); return await SendEmailAsync(email, settings.SmtpEmail, subject, htmlMessage, organizationID); } + + public async Task SendPasswordResetCodeAsync(RemotelyUser user, string email, string resetCode) + { + await SendEmailAsync( + email, + "Remotely Password Reset", + $"A password reset code has been requested for your account. Reset Code: {resetCode}", + user.OrganizationID); + } + + public async Task SendPasswordResetLinkAsync(RemotelyUser user, string email, string resetLink) + { + await SendEmailAsync( + email, + "Remotely Password Reset", + "A password reset has been requested for your account. If this was not requested by you, you can ignore this email.

" + + "Otherwise, please follow this link to reset your password: " + + $"{resetLink}", + user.OrganizationID); + } } + public class EmailSenderFake(ILogger _logger) : IEmailSenderEx { public Task SendEmailAsync(string email, string replyTo, string subject, string htmlMessage, string? organizationID = null) @@ -112,8 +139,8 @@ public Task SendEmailAsync(string email, string replyTo, string subject, s "Fake EmailSender registered in dev mode. " + "Email would have been sent to {email}." + "\n\nSubject: {EmailSubject}. " + - "\n\nMessage: {EmailMessage}", - email, + "\n\nMessage: {EmailMessage}", + email, subject, htmlMessage); return Task.FromResult(true);