Убрали зависимость Security к десктопному приложению

This commit is contained in:
kotcheshir73 2021-03-28 10:23:26 +04:00
parent 525add0260
commit 8ad72cd50d
17 changed files with 168 additions and 269 deletions

View File

@ -4,20 +4,56 @@ using ModelTools.Interfaces;
using ModelTools.Models; using ModelTools.Models;
using SecurityBusinessLogic.BusinessLogics; using SecurityBusinessLogic.BusinessLogics;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Threading.Tasks;
namespace DatabaseCore namespace DatabaseCore
{ {
public class SecurityManager : ISecurityManager 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() { }
/// <summary>
/// Singleton для класса
/// </summary>
public static SecurityManager GetInstance
{
get
{
if (_securityManager == null)
{
lock (_lockObject)
{
_securityManager = new SecurityManager();
}
}
return _securityManager;
}
}
public Guid? User { get; set; }
public List<Guid> Roles { get; set; }
public string ErrorMessage { get; set; } public string ErrorMessage { get; set; }
public bool IsAuth => User != null;
public bool CheckAccess(SecurityManagerCheckAccessModel model) public bool CheckAccess(SecurityManagerCheckAccessModel model)
{ {
using var context = DatabaseManager.GetContext; using var context = DatabaseManager.GetContext;
Access access; Access access = null;
if (model != null) if (model != null)
{ {
// простой просмотр возможен // простой просмотр возможен
@ -26,9 +62,9 @@ namespace DatabaseCore
return true; return true;
} }
// если не указан идентификатор пользователя, то смотрим, может он авторизован // если не указан идентификатор пользователя, то смотрим, может он авторизован
if (!model.Model.UserId.HasValue && UserManager.GetInstance.User != null) if (!model.Model.UserId.HasValue && User.HasValue)
{ {
model.Model.UserId = UserManager.GetInstance.UserId; 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(); var roles = context.UserRoles.Where(x => x.UserId == model.Model.UserId).Select(x => x.Role).OrderByDescending(x => x.RolePriority).ToList();
@ -39,9 +75,9 @@ namespace DatabaseCore
} }
access = context.Accesses.FirstOrDefault(a => a.AccessOperation == model.Operation && roles.Contains(a.Role)); access = context.Accesses.FirstOrDefault(a => a.AccessOperation == model.Operation && roles.Contains(a.Role));
} }
else else if (Roles != null)
{ {
access = context.Accesses.FirstOrDefault(a => a.AccessOperation == model.Operation && UserManager.GetInstance.Roles.Contains(a.RoleId)); access = context.Accesses.FirstOrDefault(a => a.AccessOperation == model.Operation && Roles.Contains(a.RoleId));
} }
if (access != null) if (access != null)
{ {
@ -121,5 +157,85 @@ namespace DatabaseCore
transaction.Commit(); 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();
}
/// <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);
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("Пользователь заблокирован");
}
}
}
} }
} }

View File

@ -1,9 +1,27 @@
using ModelTools.Models; using ModelTools.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ModelTools.Interfaces namespace ModelTools.Interfaces
{ {
public interface ISecurityManager public interface ISecurityManager
{ {
/// <summary>
/// Аутентифицированный пользователь
/// </summary>
public Guid? User { get; set; }
/// <summary>
/// Список ролей аутентифицированного пользователь
/// </summary>
public List<Guid> Roles { get; set; }
/// <summary>
/// Выполнена ли аутентификация
/// </summary>
public bool IsAuth { get; }
/// <summary> /// <summary>
/// Сообщение с причиной не получения доступа /// Сообщение с причиной не получения доступа
/// </summary> /// </summary>
@ -20,5 +38,27 @@ namespace ModelTools.Interfaces
/// Проверка наличия старотвых данных для работы с ситемой /// Проверка наличия старотвых данных для работы с ситемой
/// </summary> /// </summary>
void CheckStartDataSource(); void CheckStartDataSource();
/// <summary>
/// Аутентификация пользователя
/// </summary>
/// <param name="login"></param>
/// <param name="password"></param>
/// <returns></returns>
Task LoginAsync(string login, string password);
/// <summary>
/// Выход из системы
/// </summary>
/// <returns></returns>
Task LogoutAsync();
/// <summary>
/// Смена пароля
/// </summary>
/// <param name="login"></param>
/// <param name="oldPassword"></param>
/// <param name="newPassword"></param>
Task ChangePassword(string login, string oldPassword, string newPassword);
} }
} }

View File

@ -16,7 +16,6 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Common\DatabaseCore\DatabaseCore.csproj" /> <ProjectReference Include="..\Common\DatabaseCore\DatabaseCore.csproj" />
<ProjectReference Include="..\Common\DesktopTools\DesktopTools.csproj" /> <ProjectReference Include="..\Common\DesktopTools\DesktopTools.csproj" />
<ProjectReference Include="..\Security\SecurityBusinessLogic\SecurityBusinessLogic.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,5 +1,7 @@
using DesktopTools.BusinessLogics; using DesktopTools.BusinessLogics;
using ModelTools.BusinessLogics;
using ModelTools.Extensions; using ModelTools.Extensions;
using ModelTools.Interfaces;
using SecurityBusinessLogic.BusinessLogics; using SecurityBusinessLogic.BusinessLogics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -30,7 +32,8 @@ namespace DepartmentPortalDesctop
} }
try try
{ {
Task.WaitAll(Task.Run(async () => await UserManager.GetInstance.LoginAsync(textBoxLogin.Text, textBoxPassword.Text))); var securityManager = UnityContainerConfigurator.Resolve<ISecurityManager>();
Task.WaitAll(Task.Run(async () => await securityManager.LoginAsync(textBoxLogin.Text, textBoxPassword.Text)));
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;
Close(); Close();
} }

View File

@ -27,7 +27,7 @@ namespace DepartmentPortalDesctop
var form = new FormEnter(); var form = new FormEnter();
if (form.ShowDialog() == DialogResult.OK && UserManager.GetInstance.IsAuth) if (form.ShowDialog() == DialogResult.OK && securityManager.IsAuth)
{ {
Application.Run(UnityContainerConfigurator.Resolve<FormMain>()); Application.Run(UnityContainerConfigurator.Resolve<FormMain>());
} }

View File

@ -10,13 +10,6 @@ namespace SecurityBusinessLogic.BindingModels
/// </summary> /// </summary>
public class UserGetBindingModel : GetBindingModel public class UserGetBindingModel : GetBindingModel
{ {
public bool? IsBanned { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public List<Guid> LecturerIds { get; set; }
} }
/// <summary> /// <summary>

View File

@ -1,223 +0,0 @@
using ModelTools.BusinessLogics;
using SecurityBusinessLogic.BindingModels;
using SecurityBusinessLogic.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace SecurityBusinessLogic.BusinessLogics
{
/// <summary>
/// Менеджер по работе с пользователем системы
/// </summary>
public class UserManager
{
private readonly int _countDayToBanned = 3;
private readonly int _countMaxAttempt = 3;
private readonly RoleBusinessLogic _roleBusinessLogic;
private readonly UserBusinessLogic _userBusinessLogic;
private static UserManager _userManager;
private static readonly object _lockObject = new();
private UserManager()
{
_roleBusinessLogic = UnityContainerConfigurator.Resolve<RoleBusinessLogic>();
_userBusinessLogic = UnityContainerConfigurator.Resolve<UserBusinessLogic>();
}
public static UserManager GetInstance
{
get
{
if (_userManager == null)
{
lock (_lockObject)
{
_userManager = new UserManager();
}
}
return _userManager;
}
}
/// <summary>
/// Аутентифицированный пользователь
/// </summary>
public UserViewModel User { get; private set; }
/// <summary>
/// Список ролей аутентифицированного пользователь
/// </summary>
public List<Guid> Roles { get; private set; }
/// <summary>
/// Сообщение об ошибке
/// </summary>
public string ErrorMessage { get; private set; }
/// <summary>
/// Идентификатор аутентифицированного пользователь
/// </summary>
public Guid? UserId => User?.Id;
/// <summary>
/// Выполнена ли аутентификация
/// </summary>
public bool IsAuth => User != null;
/// <summary>
/// Аутентификация пользователя
/// </summary>
/// <param name="login"></param>
/// <param name="password"></param>
/// <returns></returns>
public async Task LoginAsync(string login, string password)
{
await Task.Run(() =>
{
UserSetBindingModel model;
var passwordHash = GetPasswordHash(password);
var user = _userBusinessLogic.GetElement(new UserGetBindingModel
{
Login = login,
Password = passwordHash,
SkipCheck = true
});
if (user == null)
{
if(_userBusinessLogic.Errors.Count > 0)
{
throw new Exception(_userBusinessLogic.Errors[0].Message);
}
user = _userBusinessLogic.GetElement(new UserGetBindingModel
{
Login = login,
IsBanned = true,
SkipCheck = true
});
if (user != null)
{
user.CountAttempt++;
if (user.CountAttempt > _countMaxAttempt)
{
user.IsBanned = true;
user.DateBanned = DateTime.Now;
}
model = GetSetBindingModel(user);
model.Password = passwordHash;
_userBusinessLogic.Update(model);
}
if (_userBusinessLogic.Errors.Count > 0)
{
throw new Exception(_userBusinessLogic.Errors[0].Message);
}
throw new Exception("Введен неверный логин/пароль");
}
if (user.IsBanned)
{
if (user.DateBanned.Value.AddDays(_countDayToBanned) > DateTime.Now)
{
user.IsBanned = false;
}
else
{
throw new Exception("Пользователь заблокирован");
}
}
user.DateLastVisit = DateTime.Now;
user.CountAttempt = 0;
model = GetSetBindingModel(user);
model.Password = passwordHash;
_userBusinessLogic.Update(model);
User = user;
Roles = _roleBusinessLogic.GetList(new RoleGetBindingModel { UserId = User.Id }).List.Select(x => x.Id).ToList();
});
}
/// <summary>
/// Выход из системы
/// </summary>
/// <returns></returns>
public async Task LogoutAsync()
{
await Task.Run(() =>
{
User = null;
Roles = null;
});
}
/// <summary>
/// Смена пароля
/// </summary>
/// <param name="login"></param>
/// <param name="oldPassword"></param>
/// <param name="newPassword"></param>
public void ChangePassword(string login, string oldPassword, string newPassword)
{
var user = _userBusinessLogic.GetElement(new UserGetBindingModel
{
Login = login,
Password = GetPasswordHash(oldPassword),
SkipCheck = true
});
if (user == null)
{
throw new Exception("Введен неверный логин/пароль");
}
if (user.IsBanned)
{
throw new Exception("Пользователь забаннен");
}
var model = GetSetBindingModel(user);
model.Password = GetPasswordHash(newPassword);
_userBusinessLogic.Update(model);
}
/// <summary>
/// Получение хеша пароля
/// </summary>
/// <param name="password"></param>
/// <returns></returns>
public static string GetPasswordHash(string password) => Encoding.ASCII.GetString((new MD5CryptoServiceProvider()).ComputeHash(Encoding.ASCII.GetBytes(password)));
/// <summary>
/// Получение модели для сохранения из представления
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
private static UserSetBindingModel GetSetBindingModel(UserViewModel model)
{
return new UserSetBindingModel
{
Id = model.Id,
Login = model.Login,
LecturerId = model.LecturerId,
StudentId = model.StudentId,
EmployeeId = model.EmployeeId,
Avatar = model.Avatar,
IsBanned = model.IsBanned,
DateBanned = model.DateBanned,
CountAttempt = model.CountAttempt,
DateLastVisit = model.DateLastVisit
};
}
}
}

View File

@ -2,7 +2,6 @@
using DatabaseCore.Models.Security; using DatabaseCore.Models.Security;
using ModelTools.BusinessLogics; using ModelTools.BusinessLogics;
using ModelTools.Enums; using ModelTools.Enums;
using ModelTools.Extensions;
using ModelTools.Models; using ModelTools.Models;
using SecurityBusinessLogic.BindingModels; using SecurityBusinessLogic.BindingModels;
using SecurityBusinessLogic.Interfaces; using SecurityBusinessLogic.Interfaces;
@ -99,35 +98,7 @@ namespace SecurityImplementation.Implementations
return OperationResultModel.Success(Mapper.MapToClass<User, UserViewModel>(entity)); return OperationResultModel.Success(Mapper.MapToClass<User, UserViewModel>(entity));
} }
if (model.Login.IsNotEmpty() && model.Password.IsNotEmpty())
{
var entity = context.Users.FirstOrDefault(x => x.UserName == model.Login && x.PasswordHash == model.Password);
if (entity == null)
{
return OperationResultModel.Error("Error:", "Элемент не найден", ResultServiceStatusCode.NotFound);
}
return OperationResultModel.Success(Mapper.MapToClass<User, UserViewModel>(entity));
}
if (model.Login.IsNotEmpty() && model.IsBanned.HasValue)
{
var entity = context.Users.FirstOrDefault(x => x.UserName == model.Login && x.IsBanned == model.IsBanned.Value);
if (entity == null)
{
return OperationResultModel.Error("Error:", "Элемент не найден", ResultServiceStatusCode.NotFound);
}
return OperationResultModel.Success(Mapper.MapToClass<User, UserViewModel>(entity));
}
var query = context.Users.Where(x => !x.IsDeleted).AsQueryable(); var query = context.Users.Where(x => !x.IsDeleted).AsQueryable();
if (model.IsBanned.HasValue)
{
query = query.Where(x => x.IsBanned == model.IsBanned.Value);
}
if (model.LecturerIds != null)
{
query = query.Where(x => x.LecturerId.HasValue && model.LecturerIds.Contains(x.LecturerId.Value));
}
query = query.OrderBy(x => x.UserName); query = query.OrderBy(x => x.UserName);

View File

@ -20,7 +20,7 @@ namespace SecurityWindowsDesktop
return null; return null;
} }
if (!manager.CheckAccess(new SecurityManagerCheckAccessModel(new AccessBindingModel { UserId = UserManager.GetInstance.UserId }, if (!manager.CheckAccess(new SecurityManagerCheckAccessModel(new AccessBindingModel { UserId = manager.User },
AccessOperation.Администрирование, AccessType.SimpleView, "Администрирование"))) AccessOperation.Администрирование, AccessType.SimpleView, "Администрирование")))
{ {
return null; return null;
@ -40,7 +40,7 @@ namespace SecurityWindowsDesktop
foreach (var cntrl in _controls) foreach (var cntrl in _controls)
{ {
if (manager.CheckAccess(new SecurityManagerCheckAccessModel(new AccessBindingModel { UserId = UserManager.GetInstance.UserId }, if (manager.CheckAccess(new SecurityManagerCheckAccessModel(new AccessBindingModel { UserId = manager.User },
cntrl.AccessOperation, AccessType.SimpleView, cntrl.Title))) cntrl.AccessOperation, AccessType.SimpleView, cntrl.Title)))
{ {
list.Add(new WindowDesktopExtensionControlModel list.Add(new WindowDesktopExtensionControlModel