using Npgsql; using System; using System.Configuration; using System.Data; using System.Windows.Forms; namespace WindowsFormsApp4 { public partial class AdminUserEditForm : Form { private readonly string connStr = ConfigurationManager.ConnectionStrings["PostgreSQL"].ConnectionString; private int? userId = null; // Конструктор: если userId передан, то редактирование, иначе – добавление нового пользователя public AdminUserEditForm(int? userId = null) { InitializeComponent(); this.userId = userId; LoadLibraries(); if (userId.HasValue) { LoadUserData(); txtUserId.Text = userId.Value.ToString(); } else { this.Text = "Добавление пользователя"; labelTitle.Text = "Добавление пользователя"; txtUserId.Text = "(новый)"; } } private string GenerateSalt() { byte[] saltBytes = new byte[16]; using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider()) rng.GetBytes(saltBytes); return Convert.ToBase64String(saltBytes); } private string HashPassword(string password, string salt) { byte[] combined = System.Text.Encoding.UTF8.GetBytes(password + salt); using (var sha256 = System.Security.Cryptography.SHA256.Create()) { byte[] hash = sha256.ComputeHash(combined); return Convert.ToBase64String(hash); } } private void LoadLibraries() { string query = "SELECT id, name FROM libraries ORDER BY name"; DataTable dt = new DataTable(); using (var conn = new NpgsqlConnection(connStr)) using (var cmd = new NpgsqlCommand(query, conn)) { conn.Open(); using (var adapter = new NpgsqlDataAdapter(cmd)) { adapter.Fill(dt); } } comboBoxLibrary.DisplayMember = "name"; comboBoxLibrary.ValueMember = "id"; comboBoxLibrary.DataSource = dt; comboBoxLibrary.SelectedIndex = -1; } private void LoadUserData() { string query = @" SELECT name, lastname, phone, email, role_name, library_id, bilet_active FROM users WHERE id = @id"; using (var conn = new NpgsqlConnection(connStr)) using (var cmd = new NpgsqlCommand(query, conn)) { cmd.Parameters.AddWithValue("id", userId.Value); conn.Open(); using (var reader = cmd.ExecuteReader()) { if (reader.Read()) { txtName.Text = reader["name"].ToString(); txtLastname.Text = reader["lastname"].ToString(); txtPhone.Text = reader["phone"].ToString(); txtEmail.Text = reader["email"].ToString(); comboBoxRole.SelectedItem = reader["role_name"].ToString(); if (reader["library_id"] != DBNull.Value) comboBoxLibrary.SelectedValue = reader["library_id"]; checkBoxActive.Checked = Convert.ToBoolean(reader["bilet_active"]); } } } } private void btnSave_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(txtName.Text) || string.IsNullOrEmpty(txtLastname.Text)) { MessageBox.Show("Имя и фамилия обязательны", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } string role = comboBoxRole.SelectedItem?.ToString(); if (string.IsNullOrEmpty(role)) { MessageBox.Show("Выберите роль", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } object libraryId = comboBoxLibrary.SelectedValue ?? DBNull.Value; // Обработка пароля (только если поле видимо и в нём что-то есть) string newPassword = txtNewPassword?.Text.Trim(); bool updatePassword = !string.IsNullOrEmpty(newPassword); if (updatePassword && newPassword != txtConfirmPassword?.Text.Trim()) { MessageBox.Show("Пароль и подтверждение не совпадают", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } if (updatePassword && newPassword.Length < 4) { MessageBox.Show("Пароль должен быть не менее 4 символов", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } try { using (var conn = new NpgsqlConnection(connStr)) { conn.Open(); if (userId.HasValue) { // 1. Обновление данных пользователя string query = @" UPDATE users SET name=@name, lastname=@lastname, phone=@phone, email=@email, role_name=@role::role, library_id=@libId, bilet_active=@active WHERE id=@id"; using (var cmd = new NpgsqlCommand(query, conn)) { cmd.Parameters.AddWithValue("name", txtName.Text); cmd.Parameters.AddWithValue("lastname", txtLastname.Text); cmd.Parameters.AddWithValue("phone", txtPhone.Text); cmd.Parameters.AddWithValue("email", txtEmail.Text); cmd.Parameters.AddWithValue("role", role); cmd.Parameters.AddWithValue("libId", libraryId == DBNull.Value ? DBNull.Value : libraryId); cmd.Parameters.AddWithValue("active", checkBoxActive.Checked); cmd.Parameters.AddWithValue("id", userId.Value); cmd.ExecuteNonQuery(); } // 2. Если задан новый пароль – обновляем user_auth if (updatePassword) { string salt = GenerateSalt(); string hash = HashPassword(newPassword, salt); string authQuery = @" UPDATE user_auth SET password = @hash, salt = @salt WHERE user_id = @userId"; using (var cmd = new NpgsqlCommand(authQuery, conn)) { cmd.Parameters.AddWithValue("hash", hash); cmd.Parameters.AddWithValue("salt", salt); cmd.Parameters.AddWithValue("userId", userId.Value); if (cmd.ExecuteNonQuery() == 0) { // Если записи ещё нет (бывает при ручной миграции), вставляем string insertAuth = @" INSERT INTO user_auth (user_id, login, password, salt) VALUES (@userId, @login, @hash, @salt)"; using (var cmd2 = new NpgsqlCommand(insertAuth, conn)) { cmd2.Parameters.AddWithValue("userId", userId.Value); cmd2.Parameters.AddWithValue("login", txtEmail.Text); cmd2.Parameters.AddWithValue("hash", hash); cmd2.Parameters.AddWithValue("salt", salt); cmd2.ExecuteNonQuery(); } } } } } else { // Добавление нового пользователя if (!updatePassword) { MessageBox.Show("Для нового пользователя необходимо задать пароль", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } // Вставка в users string insertUser = @" INSERT INTO users (name, lastname, phone, email, registration_date, role_name, library_id, bilet_active) VALUES (@name, @lastname, @phone, @email, CURRENT_DATE, @role::role, @libId, @active) RETURNING id"; int newId; using (var cmd = new NpgsqlCommand(insertUser, conn)) { cmd.Parameters.AddWithValue("name", txtName.Text); cmd.Parameters.AddWithValue("lastname", txtLastname.Text); cmd.Parameters.AddWithValue("phone", txtPhone.Text); cmd.Parameters.AddWithValue("email", txtEmail.Text); cmd.Parameters.AddWithValue("role", role); cmd.Parameters.AddWithValue("libId", libraryId == DBNull.Value ? DBNull.Value : libraryId); cmd.Parameters.AddWithValue("active", checkBoxActive.Checked); newId = (int)cmd.ExecuteScalar(); } // Вставка в user_auth string salt = GenerateSalt(); string hash = HashPassword(newPassword, salt); string insertAuth = @" INSERT INTO user_auth (user_id, login, password, salt) VALUES (@userId, @login, @hash, @salt)"; using (var cmd = new NpgsqlCommand(insertAuth, conn)) { cmd.Parameters.AddWithValue("userId", newId); cmd.Parameters.AddWithValue("login", txtEmail.Text); // логин = email (можно изменить) cmd.Parameters.AddWithValue("hash", hash); cmd.Parameters.AddWithValue("salt", salt); cmd.ExecuteNonQuery(); } MessageBox.Show($"Пользователь добавлен с ID {newId}", "Успех"); } MessageBox.Show("Сохранено", "Успех", MessageBoxButtons.OK, MessageBoxIcon.Information); this.DialogResult = DialogResult.OK; this.Close(); } } catch (Exception ex) { MessageBox.Show($"Ошибка: {ex.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void btnCancel_Click(object sender, EventArgs e) { this.DialogResult = DialogResult.Cancel; this.Close(); } private void AdminUserEditForm_Load(object sender, EventArgs e) { } private void labelConfirmPassword_Click(object sender, EventArgs e) { } } }