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("Пользователь заблокирован");
}
}
}
}
}