LoginForm.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. using System;
  2. using System.Configuration;
  3. using System.Windows.Forms;
  4. using Npgsql;
  5. using NpgsqlTypes;
  6. namespace SimpleDBViewer
  7. {
  8. public partial class LoginForm : Form
  9. {
  10. public LoginForm()
  11. {
  12. InitializeComponent();
  13. }
  14. private void TxtPassword_KeyDown(object sender, KeyEventArgs e)
  15. {
  16. if (e.KeyCode == Keys.Enter)
  17. {
  18. e.SuppressKeyPress = true;
  19. BtnLogin_Click(sender, EventArgs.Empty);
  20. }
  21. }
  22. private void BtnLogin_Click(object sender, EventArgs e)
  23. {
  24. string login = txtLogin.Text.Trim();
  25. string password = txtPassword.Text;
  26. lblError.Text = "";
  27. if (string.IsNullOrWhiteSpace(login))
  28. {
  29. lblError.Text = "Введите логин.";
  30. txtLogin.Focus();
  31. return;
  32. }
  33. if (string.IsNullOrWhiteSpace(password))
  34. {
  35. lblError.Text = "Введите пароль.";
  36. txtPassword.Focus();
  37. return;
  38. }
  39. try
  40. {
  41. btnLogin.Enabled = false;
  42. btnLogin.Text = "Проверка...";
  43. Application.DoEvents();
  44. using (NpgsqlConnection connection = new NpgsqlConnection(DbSettings.ConnectionString))
  45. {
  46. connection.Open();
  47. const string sql = @"
  48. select user_id, login, role::text as role, instructor_id, driver_id
  49. from akulova_vs.fn_authenticate_user(cast(@login as text), cast(@password as text))";
  50. using (NpgsqlCommand command = new NpgsqlCommand(sql, connection))
  51. {
  52. command.Parameters.AddWithValue("@login", login);
  53. command.Parameters.AddWithValue("@password", password);
  54. using (NpgsqlDataReader reader = command.ExecuteReader())
  55. {
  56. if (!reader.Read())
  57. {
  58. lblError.Text = "Неверный логин или пароль.";
  59. return;
  60. }
  61. UserSession.CurrentUser = new UserInfo
  62. {
  63. UserId = Convert.ToInt32(reader["user_id"]),
  64. Login = reader["login"].ToString(),
  65. Role = reader["role"].ToString(),
  66. InstructorId = reader["instructor_id"] == DBNull.Value
  67. ? (int?)null
  68. : Convert.ToInt32(reader["instructor_id"]),
  69. DriverId = reader["driver_id"] == DBNull.Value
  70. ? (int?)null
  71. : Convert.ToInt32(reader["driver_id"])
  72. };
  73. }
  74. }
  75. }
  76. using (MainForm mainForm = new MainForm())
  77. {
  78. Hide();
  79. mainForm.ShowDialog();
  80. txtPassword.Clear();
  81. Show();
  82. txtPassword.Focus();
  83. }
  84. }
  85. catch (Exception ex)
  86. {
  87. MessageBox.Show(AppError.ToUserMessage(ex, "Не удалось выполнить вход."),
  88. "Ошибка подключения или авторизации", MessageBoxButtons.OK, MessageBoxIcon.Error);
  89. }
  90. finally
  91. {
  92. btnLogin.Enabled = true;
  93. btnLogin.Text = "Войти";
  94. }
  95. }
  96. private void BtnExit_Click(object sender, EventArgs e)
  97. {
  98. Application.Exit();
  99. }
  100. private void lblInfo_Click(object sender, EventArgs e)
  101. {
  102. }
  103. }
  104. public static class DbSettings
  105. {
  106. public static string ConnectionString
  107. {
  108. get
  109. {
  110. ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings["AutoschoolDb"];
  111. return settings.ConnectionString;
  112. }
  113. }
  114. }
  115. public class UserInfo
  116. {
  117. public int UserId { get; set; }
  118. public string Login { get; set; }
  119. public string Role { get; set; }
  120. public int? InstructorId { get; set; }
  121. public int? DriverId { get; set; }
  122. }
  123. public static class DbAppContext
  124. {
  125. public static void Apply(NpgsqlConnection connection)
  126. {
  127. if (connection == null || !UserSession.IsLoggedIn)
  128. return;
  129. using (NpgsqlCommand command = new NpgsqlCommand(@"
  130. select set_config('app.user_login', cast(@login as text), false);", connection))
  131. {
  132. command.Parameters.AddWithValue("@login", UserSession.CurrentUser.Login ?? "system");
  133. command.ExecuteNonQuery();
  134. }
  135. }
  136. public static void MarkLatestAuditLogin(NpgsqlConnection connection, string tableName)
  137. {
  138. MarkLatestAuditLogin(connection, tableName, null);
  139. }
  140. public static void MarkLatestAuditLogin(NpgsqlConnection connection, string tableName, object recordId)
  141. {
  142. if (connection == null || !UserSession.IsLoggedIn || string.IsNullOrWhiteSpace(tableName))
  143. return;
  144. int parsedRecordId;
  145. object recordIdValue = DBNull.Value;
  146. if (recordId != null && recordId != DBNull.Value && int.TryParse(recordId.ToString(), out parsedRecordId))
  147. recordIdValue = parsedRecordId;
  148. using (NpgsqlCommand command = new NpgsqlCommand(@"
  149. update akulova_vs.audit_log
  150. set changed_by = cast(@login as varchar)
  151. where log_id = (
  152. select log_id
  153. from akulova_vs.audit_log
  154. where table_name = cast(@table_name as text)
  155. and (cast(@record_id as integer) is null or record_id = cast(@record_id as integer))
  156. and changed_at >= now() - interval '5 minutes'
  157. order by log_id desc
  158. limit 1
  159. );", connection))
  160. {
  161. command.Parameters.AddWithValue("@login", UserSession.CurrentUser.Login ?? "system");
  162. command.Parameters.AddWithValue("@table_name", tableName);
  163. NpgsqlParameter recordParameter = command.Parameters.Add("@record_id", NpgsqlDbType.Integer);
  164. recordParameter.Value = recordIdValue;
  165. command.ExecuteNonQuery();
  166. }
  167. }
  168. }
  169. public static class UserSession
  170. {
  171. public static UserInfo CurrentUser { get; set; }
  172. public static bool IsLoggedIn
  173. {
  174. get { return CurrentUser != null; }
  175. }
  176. public static bool IsAdmin
  177. {
  178. get { return IsLoggedIn && CurrentUser.Role == "admin"; }
  179. }
  180. public static void Logout()
  181. {
  182. CurrentUser = null;
  183. }
  184. }
  185. public static class AppError
  186. {
  187. public static string ToUserMessage(Exception ex, string actionText)
  188. {
  189. if (ex is ArgumentException)
  190. return ex.Message;
  191. PostgresException pg = ex as PostgresException;
  192. if (pg != null)
  193. {
  194. switch (pg.SqlState)
  195. {
  196. case "23505":
  197. return actionText + "\n\nТакая запись уже существует. Проверьте уникальные поля: логин, номер удостоверения или номер водительских прав.";
  198. case "23503":
  199. return actionText + "\n\nЗапись нельзя сохранить или удалить, потому что она связана с другими данными. Сначала проверьте связанные таблицы.";
  200. case "23502":
  201. return actionText + "\n\nНе заполнено обязательное поле. Проверьте форму и заполните все обязательные данные.";
  202. case "23514":
  203. return actionText + "\n\nЗначение не проходит проверку базы данных. Проверьте выбранный результат экзамена, сложность маршрута, категорию прав или дату рождения.";
  204. case "22P02":
  205. case "22007":
  206. case "22008":
  207. return actionText + "\n\nНекорректный формат данных. Проверьте числа и даты.";
  208. case "42P01":
  209. return actionText + "\n\nТаблица или представление не найдено. Проверьте, что объекты созданы в схеме akulova_vs и в строке подключения указан Search Path=akulova_vs,public.";
  210. case "42883":
  211. return actionText + "\n\nФункция базы данных не найдена. Проверьте, что функции fn_authenticate_user и fn_hash_password созданы в схеме akulova_vs.";
  212. case "42703":
  213. return actionText + "\n\nВ базе данных не найден нужный столбец. Проверьте, что структура таблиц соответствует ТЗ.";
  214. case "42501":
  215. return actionText + "\n\nНедостаточно прав для выполнения операции в базе данных.";
  216. case "3D000":
  217. return actionText + "\n\nБаза данных не найдена. Проверьте имя базы в App.config.";
  218. case "28P01":
  219. return actionText + "\n\nНеверный логин или пароль подключения к PostgreSQL. Проверьте Username и Password в App.config.";
  220. default:
  221. return actionText + "\n\nБаза данных вернула ошибку. Код: " + pg.SqlState + ".";
  222. }
  223. }
  224. if (ex is NpgsqlException)
  225. {
  226. return actionText + "\n\nНе удалось подключиться к PostgreSQL. Проверьте сервер, порт, базу данных и строку подключения в App.config.";
  227. }
  228. if (ex is FormatException || ex is InvalidCastException)
  229. {
  230. return actionText + "\n\nНекорректный формат данных. Проверьте, что числовые поля заполнены числами, а даты выбраны через календарь.";
  231. }
  232. return actionText + "\n\nПроизошла ошибка при выполнении операции. Проверьте введённые данные и попробуйте ещё раз.";
  233. }
  234. }
  235. }