using DatabaseCore.Models.Security; using ModelTools.Enums; using ModelTools.Interfaces; using ModelTools.Models; using SecurityBusinessLogic.BusinessLogics; using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; namespace DatabaseCore { public class SecurityManager : ISecurityManager { private readonly int _countDayToBanned = 3; private readonly int _countMaxAttempt = 3; private static SecurityManager _securityManager; private static readonly object _lockObject = new(); private SecurityManager() { } /// /// Singleton для класса /// public static SecurityManager GetInstance { get { if (_securityManager == null) { lock (_lockObject) { _securityManager = new SecurityManager(); } } return _securityManager; } } public Guid? User { get; set; } public List Roles { get; set; } public string ErrorMessage { get; set; } public bool IsAuth => User != null; public bool CheckAccess(SecurityManagerCheckAccessModel model) { using var context = DatabaseManager.GetContext; Access access = null; if (model != null) { // простой просмотр возможен if (model.Model.SkipCheck && model.Type == AccessType.SimpleView) { return true; } // если не указан идентификатор пользователя, то смотрим, может он авторизован if (!model.Model.UserId.HasValue && User.HasValue) { model.Model.UserId = User.Value; } var roles = context.UserRoles.Where(x => x.UserId == model.Model.UserId).Select(x => x.Role).OrderByDescending(x => x.RolePriority).ToList(); if (roles == null) { ErrorMessage = $"Не верный пользователь"; return false; } access = context.Accesses.FirstOrDefault(a => a.AccessOperation == model.Operation && roles.Contains(a.Role)); } else if (Roles != null) { access = context.Accesses.FirstOrDefault(a => a.AccessOperation == model.Operation && Roles.Contains(a.RoleId)); } if (access != null) { if (access.AccessType >= model.Type) return true; } switch (model.Type) { case AccessType.FullView: ErrorMessage = $"Нет доступа на чтение данных по сущности '{model.Entity}'"; return false; case AccessType.Change: ErrorMessage = $"Нет доступа на изменение данных по сущности '{model.Entity}'"; return false; case AccessType.Delete: ErrorMessage = $"Нет доступа на удаление данных по сущности '{model.Entity}'"; return false; default: ErrorMessage = $"Нет доступа по сущности '{model.Entity}'"; return false; } } public void CheckStartDataSource() { using var context = DatabaseManager.GetContext; using var transaction = context.Database.BeginTransaction(); var role = context.Roles.FirstOrDefault(x => x.RoleName == "Администратор"); if (role == null) { role = new Role { RoleName = "Администратор", RolePriority = 100 }; context.Roles.Add(role); context.SaveChanges(); } var accesses = context.Accesses.Where(x => x.RoleId == role.Id); foreach (AccessOperation operation in Enum.GetValues(typeof(AccessOperation))) { if (!accesses.Any(x => x.AccessOperation == operation && x.AccessType == AccessType.Delete)) { context.Accesses.Add(new Access { AccessOperation = operation, AccessType = AccessType.Delete, RoleId = role.Id }); } } context.SaveChanges(); var md5 = new MD5CryptoServiceProvider(); var user = context.Users.FirstOrDefault(x => x.UserName == "admin"); if (user == null) { user = new User { UserName = "admin", PasswordHash = Encoding.ASCII.GetString(md5.ComputeHash(Encoding.ASCII.GetBytes("qwerty"))), CountAttempt = 0 }; context.Users.Add(user); context.SaveChanges(); } var link = context.UserRoles.FirstOrDefault(x => x.RoleId == role.Id && x.UserId == user.Id); if (link == null) { context.UserRoles.Add(new UserRole { RoleId = role.Id, UserId = user.Id }); context.SaveChanges(); } transaction.Commit(); } public async Task LoginAsync(string login, string password) { var passwordHash = GetPasswordHash(password); using var context = DatabaseManager.GetContext; var user = context.Users.FirstOrDefault(x => x.UserName == login && x.PasswordHash == passwordHash); await CheckUserAsync(login, user, context); user.DateLastVisit = DateTime.Now; user.CountAttempt = 0; await context.SaveChangesAsync(); User = user.Id; Roles = context.UserRoles.Where(x => x.UserId == user.Id).Select(x => x.RoleId).ToList(); } public async Task LogoutAsync() { await Task.Run(() => { User = null; Roles = null; }); } public async Task ChangePassword(string login, string oldPassword, string newPassword) { using var context = DatabaseManager.GetContext; var user = context.Users.FirstOrDefault(x => x.UserName == login && x.PasswordHash == GetPasswordHash(oldPassword)); await CheckUserAsync(login, user, context); user.PasswordHash = GetPasswordHash(newPassword); await context.SaveChangesAsync(); } /// /// Получение хеша пароля /// /// /// private static string GetPasswordHash(string password) => Encoding.ASCII.GetString((new MD5CryptoServiceProvider()).ComputeHash(Encoding.ASCII.GetBytes(password))); /// /// Проверка пользователя при авторизации и при смене пароля /// /// /// /// /// private async Task CheckUserAsync(string login, User user, DatabaseContext context) { if (user == null) { user = context.Users.FirstOrDefault(x => x.UserName == login); if (user != null) { user.CountAttempt++; if (user.CountAttempt > _countMaxAttempt) { user.IsBanned = true; user.DateBanned = DateTime.Now; await context.SaveChangesAsync(); throw new Exception($"Введен неверный логин/пароль? учетная запись заблоикрована на {_countDayToBanned} дней(я)"); } } throw new Exception("Введен неверный логин/пароль"); } if (user.IsBanned) { if (user.DateBanned.Value.AddDays(_countDayToBanned) > DateTime.Now) { user.IsBanned = false; await context.SaveChangesAsync(); } else { throw new Exception("Пользователь заблокирован"); } } } } }