211 lines
6.4 KiB
C#
211 lines
6.4 KiB
C#
using DatabaseCore.Models.Security;
|
||
using ModuleTools.Enums;
|
||
using ModuleTools.Interfaces;
|
||
using ModuleTools.Models;
|
||
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;
|
||
|
||
public Guid? User { get; set; }
|
||
|
||
public List<Guid> 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.Model != null)
|
||
{
|
||
// если не указан идентификатор пользователя, то смотрим, может он авторизован
|
||
if (!model.Model.UserId.HasValue && User.HasValue)
|
||
{
|
||
model.Model.UserId = User.Value;
|
||
}
|
||
|
||
var roles = context.UserRoles.Where(x => x.UserId == model.Model.UserId && !x.IsDeleted).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.View:
|
||
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 && !x.IsDeleted);
|
||
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 && !x.IsDeleted).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) && !x.IsDeleted);
|
||
await CheckUserAsync(login, user, context);
|
||
user.PasswordHash = GetPasswordHash(newPassword);
|
||
await context.SaveChangesAsync();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получение хеша пароля
|
||
/// </summary>
|
||
/// <param name="password"></param>
|
||
/// <returns></returns>
|
||
private static string GetPasswordHash(string password) => Encoding.ASCII.GetString((new MD5CryptoServiceProvider()).ComputeHash(Encoding.ASCII.GetBytes(password)));
|
||
|
||
/// <summary>
|
||
/// Проверка пользователя при авторизации и при смене пароля
|
||
/// </summary>
|
||
/// <param name="login"></param>
|
||
/// <param name="user"></param>
|
||
/// <param name="context"></param>
|
||
/// <returns></returns>
|
||
private async Task CheckUserAsync(string login, User user, DatabaseContext context)
|
||
{
|
||
if (user == null)
|
||
{
|
||
user = context.Users.FirstOrDefault(x => x.UserName == login && !x.IsDeleted);
|
||
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("Пользователь заблокирован");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} |