using System; using System.Configuration; using System.Windows.Forms; using Npgsql; using NpgsqlTypes; namespace SimpleDBViewer { public partial class LoginForm : Form { public LoginForm() { InitializeComponent(); } private void TxtPassword_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { e.SuppressKeyPress = true; BtnLogin_Click(sender, EventArgs.Empty); } } private void BtnLogin_Click(object sender, EventArgs e) { string login = txtLogin.Text.Trim(); string password = txtPassword.Text; lblError.Text = ""; if (string.IsNullOrWhiteSpace(login)) { lblError.Text = "Введите логин."; txtLogin.Focus(); return; } if (string.IsNullOrWhiteSpace(password)) { lblError.Text = "Введите пароль."; txtPassword.Focus(); return; } try { btnLogin.Enabled = false; btnLogin.Text = "Проверка..."; Application.DoEvents(); using (NpgsqlConnection connection = new NpgsqlConnection(DbSettings.ConnectionString)) { connection.Open(); const string sql = @" select user_id, login, role::text as role, instructor_id, driver_id from akulova_vs.fn_authenticate_user(cast(@login as text), cast(@password as text))"; using (NpgsqlCommand command = new NpgsqlCommand(sql, connection)) { command.Parameters.AddWithValue("@login", login); command.Parameters.AddWithValue("@password", password); using (NpgsqlDataReader reader = command.ExecuteReader()) { if (!reader.Read()) { lblError.Text = "Неверный логин или пароль."; return; } UserSession.CurrentUser = new UserInfo { UserId = Convert.ToInt32(reader["user_id"]), Login = reader["login"].ToString(), Role = reader["role"].ToString(), InstructorId = reader["instructor_id"] == DBNull.Value ? (int?)null : Convert.ToInt32(reader["instructor_id"]), DriverId = reader["driver_id"] == DBNull.Value ? (int?)null : Convert.ToInt32(reader["driver_id"]) }; } } } using (MainForm mainForm = new MainForm()) { Hide(); mainForm.ShowDialog(); txtPassword.Clear(); Show(); txtPassword.Focus(); } } catch (Exception ex) { MessageBox.Show(AppError.ToUserMessage(ex, "Не удалось выполнить вход."), "Ошибка подключения или авторизации", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { btnLogin.Enabled = true; btnLogin.Text = "Войти"; } } private void BtnExit_Click(object sender, EventArgs e) { Application.Exit(); } private void lblInfo_Click(object sender, EventArgs e) { } } public static class DbSettings { public static string ConnectionString { get { ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings["AutoschoolDb"]; return settings.ConnectionString; } } } public class UserInfo { public int UserId { get; set; } public string Login { get; set; } public string Role { get; set; } public int? InstructorId { get; set; } public int? DriverId { get; set; } } public static class DbAppContext { public static void Apply(NpgsqlConnection connection) { if (connection == null || !UserSession.IsLoggedIn) return; using (NpgsqlCommand command = new NpgsqlCommand(@" select set_config('app.user_login', cast(@login as text), false);", connection)) { command.Parameters.AddWithValue("@login", UserSession.CurrentUser.Login ?? "system"); command.ExecuteNonQuery(); } } public static void MarkLatestAuditLogin(NpgsqlConnection connection, string tableName) { MarkLatestAuditLogin(connection, tableName, null); } public static void MarkLatestAuditLogin(NpgsqlConnection connection, string tableName, object recordId) { if (connection == null || !UserSession.IsLoggedIn || string.IsNullOrWhiteSpace(tableName)) return; int parsedRecordId; object recordIdValue = DBNull.Value; if (recordId != null && recordId != DBNull.Value && int.TryParse(recordId.ToString(), out parsedRecordId)) recordIdValue = parsedRecordId; using (NpgsqlCommand command = new NpgsqlCommand(@" update akulova_vs.audit_log set changed_by = cast(@login as varchar) where log_id = ( select log_id from akulova_vs.audit_log where table_name = cast(@table_name as text) and (cast(@record_id as integer) is null or record_id = cast(@record_id as integer)) and changed_at >= now() - interval '5 minutes' order by log_id desc limit 1 );", connection)) { command.Parameters.AddWithValue("@login", UserSession.CurrentUser.Login ?? "system"); command.Parameters.AddWithValue("@table_name", tableName); NpgsqlParameter recordParameter = command.Parameters.Add("@record_id", NpgsqlDbType.Integer); recordParameter.Value = recordIdValue; command.ExecuteNonQuery(); } } } public static class UserSession { public static UserInfo CurrentUser { get; set; } public static bool IsLoggedIn { get { return CurrentUser != null; } } public static bool IsAdmin { get { return IsLoggedIn && CurrentUser.Role == "admin"; } } public static void Logout() { CurrentUser = null; } } public static class AppError { public static string ToUserMessage(Exception ex, string actionText) { if (ex is ArgumentException) return ex.Message; PostgresException pg = ex as PostgresException; if (pg != null) { switch (pg.SqlState) { case "23505": return actionText + "\n\nТакая запись уже существует. Проверьте уникальные поля: логин, номер удостоверения или номер водительских прав."; case "23503": return actionText + "\n\nЗапись нельзя сохранить или удалить, потому что она связана с другими данными. Сначала проверьте связанные таблицы."; case "23502": return actionText + "\n\nНе заполнено обязательное поле. Проверьте форму и заполните все обязательные данные."; case "23514": return actionText + "\n\nЗначение не проходит проверку базы данных. Проверьте выбранный результат экзамена, сложность маршрута, категорию прав или дату рождения."; case "22P02": case "22007": case "22008": return actionText + "\n\nНекорректный формат данных. Проверьте числа и даты."; case "42P01": return actionText + "\n\nТаблица или представление не найдено. Проверьте, что объекты созданы в схеме akulova_vs и в строке подключения указан Search Path=akulova_vs,public."; case "42883": return actionText + "\n\nФункция базы данных не найдена. Проверьте, что функции fn_authenticate_user и fn_hash_password созданы в схеме akulova_vs."; case "42703": return actionText + "\n\nВ базе данных не найден нужный столбец. Проверьте, что структура таблиц соответствует ТЗ."; case "42501": return actionText + "\n\nНедостаточно прав для выполнения операции в базе данных."; case "3D000": return actionText + "\n\nБаза данных не найдена. Проверьте имя базы в App.config."; case "28P01": return actionText + "\n\nНеверный логин или пароль подключения к PostgreSQL. Проверьте Username и Password в App.config."; default: return actionText + "\n\nБаза данных вернула ошибку. Код: " + pg.SqlState + "."; } } if (ex is NpgsqlException) { return actionText + "\n\nНе удалось подключиться к PostgreSQL. Проверьте сервер, порт, базу данных и строку подключения в App.config."; } if (ex is FormatException || ex is InvalidCastException) { return actionText + "\n\nНекорректный формат данных. Проверьте, что числовые поля заполнены числами, а даты выбраны через календарь."; } return actionText + "\n\nПроизошла ошибка при выполнении операции. Проверьте введённые данные и попробуйте ещё раз."; } } }