Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions backend/Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,28 @@
return new NotificationProvider(secret!);
});

// TODO: Update in production to E-mail
builder.Services.AddScoped<IEmailProvider, DiscordWebhookProvider>(sp =>
builder.Services.AddScoped<IEmailProvider>(sp =>
{
var config = sp.GetRequiredService<IConfiguration>();

var host = config["Smtp:Host"];
if (!string.IsNullOrWhiteSpace(host))
{
var port = int.TryParse(config["Smtp:Port"], out var parsedPort) ? parsedPort : 587;
var enableSsl = bool.TryParse(config["Smtp:EnableSsl"], out var parsedEnableSsl) ? parsedEnableSsl : true;
var username = config["Smtp:Username"];
var password = config["Smtp:Password"];
var fromEmail = config["Smtp:FromEmail"] ?? username ?? throw new InvalidOperationException("Missing SMTP FromEmail.");
var fromName = config["Smtp:FromName"];

return new SmtpEmailProvider(host, port, enableSsl, username, password, fromEmail, fromName);
}

var url = config["DiscordWebhook"];
return new DiscordWebhookProvider(url!);
if (string.IsNullOrWhiteSpace(url))
throw new InvalidOperationException("Missing email provider config. Set Smtp:* or DiscordWebhook.");

return new DiscordWebhookProvider(url);
});

builder.Services.AddScoped<TenantFilter>();
Expand Down
35 changes: 22 additions & 13 deletions backend/Api/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "server=localhost;user=root;password=;database=yaspo"
},
"Jwt": {
"Secret": "test_test_test_test_test_test_test_test_test_test_test_test"
}
}
}
},
"ConnectionStrings": {
"DefaultConnection": "server=localhost;user=root;password=;database=yaspo"
},
"Smtp": {
"Host": "smtp.gmail.com",
"Port": 587,
"EnableSsl": true,
"Username": "[email protected]",
"Password": "lttxpnxdyauelekl",
"FromEmail": "[email protected]",
"FromName": "Yaspo"
},
"Jwt": {
"Secret": "test_test_test_test_test_test_test_test_test_test_test_test"
}
}
45 changes: 27 additions & 18 deletions backend/Api/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"DiscordWebhook": "https://discord.com/api/webhooks/1424138993427025922/KISsDXSVhRaOtjNvFN3FXwS_1WTOvFpuUA7LzUZxyYcG0-_mA_yPUNOXq4-96ih7Vqip",
"Firebase": {
"ServiceAccount": "{\n \"type\": \"service_account\",\n \"project_id\": \"twoaxis-yaspo\",\n \"private_key_id\": \"4c06954b742ed1b2adb1ea44949b5a00ef0b31b5\",\n \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDFI2vMaNmv0p/e\\n2FPq6ILZvMnSe5uFzawGtvikNbOXblQsBCrdjMBOlzaElP6d1jPDoXm+Sp5mRVhd\\nlF+uHZVaknkdtQnkdgom+ye6/e7wsgtSx12sfELmJetARmPDUpRPlh2+dmBjMPyX\\nN6vIwk1suwbhhXcEegqEOghzb1HeTLqwpje6QqqPhhmcOoi1OehBJDPWpevforaY\\nigH1OJfWSu1H5/SiSpJuu0lmkyzOvqt8XIKSj4caBQHAaA/mfVST0tHhXNTuL/0v\\nuXPrE/mVQVm9zJl9tsl2cOrfBstGDkGV21QvhVxODiFpELUXeOMyD4OOnesKU2lD\\nHFQDVH6TAgMBAAECggEAJGvi4qUMcttrBfTZrDDA3BFxCB4mIZZaDfOIBG6Xe4a6\\nrlcmxD/Y16xAOkZ8tBR7A821fF603Rg6ty9ObhCxI0akccScy0Szo3gLYxS/LGD4\\njpaey9s5UOP4jqtyiweatwl4/VNJg4ntYNK8Ph8HxurW3puV3ZHel4dRQGJ0sakP\\nraRUoeDH8VAyixFMXXjvIcUVmNsOMOsCGs4sJrhDlLwT6gn9agBcygPWD6K5v2zo\\nKxR0l/m6yAsjoDeqgQfYIju9cdrFArEbYEnXRjm5VG1Qjb6blS9LR+AfDQVx9xrz\\n+7vlEpaQHxsXx9qksDVbDCZ+NnNi1Vsa2rlMsUDOYQKBgQD/61j2tC6pz9zXJ0Eu\\nLRHQfjQwIThA7nPzLJ1sSSgBuACayC9qt/gy6KO0it2crOakT9DmdiPLocl4qp86\\nRAYg3TLN4h+DlbIzT1fJ4VVSZIDaK9mUgyu9ZMUIz5oSA4ldTCxP75OHJi/k65HX\\nyUGxuSVwyP/z5b2E+/vLlHwHnQKBgQDFM1R6p8ARnmvc/GCg1lvA7ukXy73cwXVm\\n3HvfOybzCsLokd0V4elcLNrxDZcWeNAAk+EegyB9RYiAz4UehWhgk8iznqdPievb\\nm23vuWFLd/2J6GZZDD4TnQ8b4isIUrzQL/nuNWF+xbJIRJyfVqEtjNc/k0NG4Piq\\n5jRAJ4T/7wKBgFxsLjDKWZ/SBbtd3dhDRo+5RDeth5dLhmxnzGJy+RPF09ISQa2U\\nyvn3skddbzNJ7fGhMZxJRhwgTZZaaC6uqlfsCpD3/NGUmkqVeOrbiei7ix0/gNB1\\nnrJDVjUcZFPlGxKqk8Y6i1jfnaRavszjTepVJwRYUrdOXOdN6V1YPoKxAoGAHnXG\\nR8ffkBkjWhgVqOJTe+KrWISdCBtN0hyeeaW+Z24zGw9k0ft6O0Wmb/OEcfEpV91/\\nuWHRw+fYpOTpEATVUY2iQ7Ru7YdFSj+g5JWC5NBc9k9Sd9C/SfJJoin2MavuBE5U\\n0amnLJjG6AjJFIeiD9IgcW6CxtD7OBKk4X/5ODsCgYBf8j8CQevEcQTt2tCMfk7t\\ncuVGrOAkiUbIshDBojJjaYAGd5YRpmVyi6eSuAUzIwmdpB6JFGkoue/xufgV17v/\\n3ks6Qovt6ObNrWLlKlHGV5m8btQNS4L5P4FQVGW5+r7VXc/dKK0S6xPiijUUn6x7\\nspneKUMPLyGMX11I4R9Rlw==\\n-----END PRIVATE KEY-----\\n\",\n \"client_email\": \"[email protected]\",\n \"client_id\": \"109655795699936403272\",\n \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n \"token_uri\": \"https://oauth2.googleapis.com/token\",\n \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fbsvc%40twoaxis-yaspo.iam.gserviceaccount.com\",\n \"universe_domain\": \"googleapis.com\"\n}\n"
},
"AWS": {
"AccessKey": "AKIA4XYMC6GGWDYJLOHO",
"SecretKey": "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",
"Region": "eu-central-1",
"BucketName": "yaspo-staging-bucket"
}
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"DiscordWebhook": "https://discord.com/api/webhooks/1424138993427025922/KISsDXSVhRaOtjNvFN3FXwS_1WTOvFpuUA7LzUZxyYcG0-_mA_yPUNOXq4-96ih7Vqip",
"Smtp": {
"Host": "smtp.gmail.com",
"Port": 587,
"EnableSsl": true,
"Username": "[email protected]",
"Password": "lttxpnxdyauelekl",
"FromEmail": "[email protected]",
"FromName": "Yaspo"
},
"Firebase": {
"ServiceAccount": "{\n \"type\": \"service_account\",\n \"project_id\": \"twoaxis-yaspo\",\n \"private_key_id\": \"4c06954b742ed1b2adb1ea44949b5a00ef0b31b5\",\n \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDFI2vMaNmv0p/e\\n2FPq6ILZvMnSe5uFzawGtvikNbOXblQsBCrdjMBOlzaElP6d1jPDoXm+Sp5mRVhd\\nlF+uHZVaknkdtQnkdgom+ye6/e7wsgtSx12sfELmJetARmPDUpRPlh2+dmBjMPyX\\nN6vIwk1suwbhhXcEegqEOghzb1HeTLqwpje6QqqPhhmcOoi1OehBJDPWpevforaY\\nigH1OJfWSu1H5/SiSpJuu0lmkyzOvqt8XIKSj4caBQHAaA/mfVST0tHhXNTuL/0v\\nuXPrE/mVQVm9zJl9tsl2cOrfBstGDkGV21QvhVxODiFpELUXeOMyD4OOnesKU2lD\\nHFQDVH6TAgMBAAECggEAJGvi4qUMcttrBfTZrDDA3BFxCB4mIZZaDfOIBG6Xe4a6\\nrlcmxD/Y16xAOkZ8tBR7A821fF603Rg6ty9ObhCxI0akccScy0Szo3gLYxS/LGD4\\njpaey9s5UOP4jqtyiweatwl4/VNJg4ntYNK8Ph8HxurW3puV3ZHel4dRQGJ0sakP\\nraRUoeDH8VAyixFMXXjvIcUVmNsOMOsCGs4sJrhDlLwT6gn9agBcygPWD6K5v2zo\\nKxR0l/m6yAsjoDeqgQfYIju9cdrFArEbYEnXRjm5VG1Qjb6blS9LR+AfDQVx9xrz\\n+7vlEpaQHxsXx9qksDVbDCZ+NnNi1Vsa2rlMsUDOYQKBgQD/61j2tC6pz9zXJ0Eu\\nLRHQfjQwIThA7nPzLJ1sSSgBuACayC9qt/gy6KO0it2crOakT9DmdiPLocl4qp86\\nRAYg3TLN4h+DlbIzT1fJ4VVSZIDaK9mUgyu9ZMUIz5oSA4ldTCxP75OHJi/k65HX\\nyUGxuSVwyP/z5b2E+/vLlHwHnQKBgQDFM1R6p8ARnmvc/GCg1lvA7ukXy73cwXVm\\n3HvfOybzCsLokd0V4elcLNrxDZcWeNAAk+EegyB9RYiAz4UehWhgk8iznqdPievb\\nm23vuWFLd/2J6GZZDD4TnQ8b4isIUrzQL/nuNWF+xbJIRJyfVqEtjNc/k0NG4Piq\\n5jRAJ4T/7wKBgFxsLjDKWZ/SBbtd3dhDRo+5RDeth5dLhmxnzGJy+RPF09ISQa2U\\nyvn3skddbzNJ7fGhMZxJRhwgTZZaaC6uqlfsCpD3/NGUmkqVeOrbiei7ix0/gNB1\\nnrJDVjUcZFPlGxKqk8Y6i1jfnaRavszjTepVJwRYUrdOXOdN6V1YPoKxAoGAHnXG\\nR8ffkBkjWhgVqOJTe+KrWISdCBtN0hyeeaW+Z24zGw9k0ft6O0Wmb/OEcfEpV91/\\nuWHRw+fYpOTpEATVUY2iQ7Ru7YdFSj+g5JWC5NBc9k9Sd9C/SfJJoin2MavuBE5U\\n0amnLJjG6AjJFIeiD9IgcW6CxtD7OBKk4X/5ODsCgYBf8j8CQevEcQTt2tCMfk7t\\ncuVGrOAkiUbIshDBojJjaYAGd5YRpmVyi6eSuAUzIwmdpB6JFGkoue/xufgV17v/\\n3ks6Qovt6ObNrWLlKlHGV5m8btQNS4L5P4FQVGW5+r7VXc/dKK0S6xPiijUUn6x7\\nspneKUMPLyGMX11I4R9Rlw==\\n-----END PRIVATE KEY-----\\n\",\n \"client_email\": \"[email protected]\",\n \"client_id\": \"109655795699936403272\",\n \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n \"token_uri\": \"https://oauth2.googleapis.com/token\",\n \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fbsvc%40twoaxis-yaspo.iam.gserviceaccount.com\",\n \"universe_domain\": \"googleapis.com\"\n}\n"
},
"AWS": {
"AccessKey": "AKIA4XYMC6GGWDYJLOHO",
"SecretKey": "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",
"Region": "eu-central-1",
"BucketName": "yaspo-staging-bucket"
}
}
41 changes: 31 additions & 10 deletions backend/Application/Services/AuthService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,24 +148,45 @@

public async Task VerifyPasswordReset(string email, string otp)
{
var user = await _userRepository.GetUserByEmail(email) ?? throw new UserNotFoundException();
try
{
var user = await _userRepository.GetUserByEmail(email) ?? throw new UserNotFoundException();

var token = await _passwordResetTokenRepository.GetLatestByUserId(user.Id) ?? throw new InvalidTokenException();
var token = await _passwordResetTokenRepository.GetLatestByUserId(user.Id) ?? throw new InvalidTokenException();

if (token.ExpiredAt <= DateTime.UtcNow)
throw new InvalidCredentialsException();
if (token.ExpiredAt <= DateTime.UtcNow)
throw new InvalidCredentialsException();

if (token.Code != otp)
throw new InvalidTokenException();
if (token.Code != otp)
throw new InvalidTokenException();
}
catch (Exception ex) when (
ex is UserNotFoundException ||
ex is InvalidTokenException ||
ex is InvalidCredentialsException)
{
throw new ResetPasswordVerifyException();

Check failure on line 168 in backend/Application/Services/AuthService.cs

View workflow job for this annotation

GitHub Actions / test

The type or namespace name 'ResetPasswordVerifyException' could not be found (are you missing a using directive or an assembly reference?)
}
}

public async Task SubmitPasswordReset(string email, string otp, string newPassword)
{
await VerifyPasswordReset(email, otp);
try
{
await VerifyPasswordReset(email, otp);

var user = await _userRepository.GetUserByEmail(email) ?? throw new UserNotFoundException();
var user = await _userRepository.GetUserByEmail(email) ?? throw new UserNotFoundException();

var hashed = BCrypt.Net.BCrypt.HashPassword(newPassword);
await _userRepository.SetUserPassword(user.Id, hashed);
var hashed = BCrypt.Net.BCrypt.HashPassword(newPassword);
await _userRepository.SetUserPassword(user.Id, hashed);
}
catch (ResetPasswordVerifyException)

Check failure on line 183 in backend/Application/Services/AuthService.cs

View workflow job for this annotation

GitHub Actions / test

The type or namespace name 'ResetPasswordVerifyException' could not be found (are you missing a using directive or an assembly reference?)
{
throw new SubmitNewPasswordException();

Check failure on line 185 in backend/Application/Services/AuthService.cs

View workflow job for this annotation

GitHub Actions / test

The type or namespace name 'SubmitNewPasswordException' could not be found (are you missing a using directive or an assembly reference?)
}
catch (UserNotFoundException)
{
throw new SubmitNewPasswordException();

Check failure on line 189 in backend/Application/Services/AuthService.cs

View workflow job for this annotation

GitHub Actions / test

The type or namespace name 'SubmitNewPasswordException' could not be found (are you missing a using directive or an assembly reference?)
}
}
}
53 changes: 53 additions & 0 deletions backend/Infrastructure/SmtpEmailProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Domain.Interfaces;
using System.Net;
using System.Net.Mail;

namespace Infrastructure;

public class SmtpEmailProvider(
string host,
int port,
bool enableSsl,
string? username,
string? password,
string fromEmail,
string? fromName) : IEmailProvider
{
private readonly string _host = host;
private readonly int _port = port;
private readonly bool _enableSsl = enableSsl;
private readonly string? _username = username;
private readonly string? _password = password;
private readonly string _fromEmail = fromEmail;
private readonly string? _fromName = fromName;

public void SendEmail(string to, string subject, string body)
{
using var message = new MailMessage
{
From = string.IsNullOrWhiteSpace(_fromName)
? new MailAddress(_fromEmail)
: new MailAddress(_fromEmail, _fromName),
Subject = subject,
Body = body,
IsBodyHtml = false
};

message.To.Add(new MailAddress(to));

using var client = new SmtpClient(_host, _port)
{
EnableSsl = _enableSsl,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
};

if (!string.IsNullOrWhiteSpace(_username))
{
client.Credentials = new NetworkCredential(_username, _password);
}

client.Send(message);
}
}

Loading