using System; using System.Windows; using System.Windows.Controls; using System.Text.RegularExpressions; using MySql.Data.MySqlClient; using BCrypt.Net; namespace SkyTeam { public partial class RegistrationPage : Page { public RegistrationPage() { // Quelle: Im Unterricht gemacht // Standard-Initialisierung der WPF Komponenten InitializeComponent(); } private void RegisterButton_Click(object sender, RoutedEventArgs e) { // Quelle: Im Unterricht gemacht // Grundlegende Validierung, ob die Pflichtfelder ausgefüllt wurden if (string.IsNullOrWhiteSpace(EmailTextBox.Text) || string.IsNullOrWhiteSpace(PasswordBox.Password)) { MessageBox.Show("Bitte geben Sie Email und Passwort ein."); return; } if (!IsValidEmail(EmailTextBox.Text)) { MessageBox.Show("Bitte geben Sie eine gültige E-Mail-Adresse ein (z.B. name@domain.com)."); return; } string emailToCheck = EmailTextBox.Text; try { using (MySqlConnection conn = new MySqlConnection(DatenbankServices.GetConnection())) { conn.Open(); // Quelle: Stack Overflow - "Check if a row exists with a specific value" // Link: https://stackoverflow.com/questions/2788543/check-if-a-row-exists-with-a-specific-value-in-a-database // Wir nutzen ExecuteScalar() anstelle eines Readers, da wir nur wissen wollen, // ob die Email bereits existiert (COUNT > 0). Das ist wesentlich performanter string checkQuery = "SELECT COUNT(*) FROM users WHERE Email = @email"; using (MySqlCommand checkCmd = new MySqlCommand(checkQuery, conn)) { checkCmd.Parameters.AddWithValue("@email", emailToCheck); long userCount = (long)checkCmd.ExecuteScalar(); if (userCount > 0) { MessageBox.Show("Sie haben bereits ein Konto mit dieser E-Mail. Bitte löschen Sie es, bevor Sie ein neues erstellen.", "Konto existiert bereits", MessageBoxButton.OK, MessageBoxImage.Error); return; } } // Quelle: Reddit - r/csharp "How should I store passwords in my database?" // Link: https://www.reddit.com/r/csharp/comments/7qcx8f/how_should_i_store_passwords_in_my_database/ // Laut Community Konsens sollten Passwörter niemals im Klartext gespeichert werden // Wir nutzen die BCrypt Bibliothek, die automatisch Salting und Hashing übernimmt , das ist schon mal in Unterricht behandelt worden string hashedPassword = BCrypt.Net.BCrypt.HashPassword(PasswordBox.Password); string insertQuery = "INSERT INTO users (Vorname, Nachname, Email, PasswortHash, Rolle, Stadt, Anrede, Geburtsdatum) " + "VALUES (@vorname, @nachname, @email, @password, 'User', @stadt, @anrede, @geburtsdatum)"; using (MySqlCommand cmd = new MySqlCommand(insertQuery, conn)) { string selectedAnrede = (SalutationComboBox.SelectedItem as ComboBoxItem)?.Content.ToString(); DateTime? selectedDate = BirthDatePicker.SelectedDate; // Quelle: Im Unterricht gemacht // Kommentar: Standard Parameter-Binding zum Schutz vor SQL-Injection cmd.Parameters.AddWithValue("@vorname", FirstNameTextBox.Text); cmd.Parameters.AddWithValue("@nachname", LastNameTextBox.Text); cmd.Parameters.AddWithValue("@email", emailToCheck); cmd.Parameters.AddWithValue("@password", hashedPassword); cmd.Parameters.AddWithValue("@stadt", CityTextBox.Text); // Quelle: Microsoft Learn - "DBNull.Value Field" // Link: https://learn.microsoft.com/en-us/dotnet/api/system.dbnull.value // Wenn optionale Felder (wie Anrede oder Geburtsdatum) leer bleiben, // können wir nicht einfach 'null' in C# übergeben. Die Datenbank erwartet explizit // das Objekt 'DBNull.Value', damit die Spalte korrekt als NULL markiert wird cmd.Parameters.AddWithValue("@anrede", selectedAnrede ?? (object)DBNull.Value); cmd.Parameters.AddWithValue("@geburtsdatum", selectedDate.HasValue ? selectedDate.Value : (object)DBNull.Value); cmd.ExecuteNonQuery(); } } MessageBox.Show("Dein Konto wurde erfolgreich angelegt!"); // Quelle: AI Assistant (Gemini) // Idee: Navigation aus einer Page heraus, die in einem Frame gehostet wird. // Kommentar: Die KI hat darauf hingewiesen, dass eine Page (wie die RegistrationPage) // nicht direkt navigieren sollte, wenn sie im MainFrame des MainWindows liegt. // Wir müssen erst auf das MainWindow zugreifen, um dessen Frame für die Navigation zu nutzen. if (Application.Current.MainWindow is MainWindow mainWindow) { mainWindow.MainFrame.Navigate(new LogInPage()); } } catch (Exception ex) { MessageBox.Show("Etwas ist schief gelaufen: " + ex.Message); } } private void CancelButton_Click(object sender, RoutedEventArgs e) { if (Application.Current.MainWindow is MainWindow mainWindow) { mainWindow.MainFrame.Navigate(new LogInPage()); } } private bool IsValidEmail(string email) { if (string.IsNullOrWhiteSpace(email)) return false; try { // Quelle: Microsoft Learn - "How to verify that strings are in valid email format" // Link: https://learn.microsoft.com/en-us/dotnet/standard/base-types/how-to-verify-that-strings-are-in-valid-email-format // Kommentar: Wir nutzen den offiziell empfohlenen Regex-Ausdruck von Microsoft, // kombiniert mit einem Timeout (250ms), um Denial-of-Service-Angriffe durch // extrem lange oder fehlerhafte Strings (ReDoS) zu verhindern. return Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250)); } catch (RegexMatchTimeoutException) { return false; } } } }