using AsbCloudApp.Data; using AsbCloudApp.Services; using AsbCloudDb.Model; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services { public class AuthService : IAuthService { private readonly IAsbCloudDbContext db; public const string issuer = "a"; public const string audience = "a"; public static readonly SymmetricSecurityKey securityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("супер секретный ключ для шифрования")); public const string algorithms = SecurityAlgorithms.HmacSha256; private static readonly TimeSpan expiresTimespan = TimeSpan.FromDays(365.25); private static readonly Encoding encoding = Encoding.UTF8; private const int PasswordSaltLength = 5; private const string claimIdUser = "id"; private const string claimNameidCompany = "idCompany"; private readonly HashAlgorithm hashAlgoritm; private readonly Random rnd; public AuthService(IAsbCloudDbContext db) { this.db = db; hashAlgoritm = SHA384.Create(); rnd = new Random((int)(DateTime.Now.Ticks % 2147480161)); } public async Task LoginAsync(string login, string password, CancellationToken token = default) { var identity = await GetClaimsUserAsync(login, password, token) .ConfigureAwait(false); if (identity == default) return null; return new UserTokenDto { Id = identity.User.Id, Name = identity.User.Name, CompanyName = identity.User.Company.Caption, Level = identity.User.Level, Login = identity.User.Login, Patronymic = identity.User.Patronymic, RoleName = identity.User.Role.Caption, Surname = identity.User.Surname, Token = MakeToken(identity.Identity.Claims), }; } public string Refresh(ClaimsPrincipal user) { return MakeToken(user.Claims); } public int Register(UserDto userDto) { if (userDto.Login.Length < 3) return -1; if (userDto.Password.Length < 3) return -2; var salt = GenerateSalt(); var user = new User { IdCompany = userDto.IdCompany, IdRole = userDto.IdRole, Name = userDto.Name, Surname = userDto.Surname, Patronymic = userDto.Patronymic, Level = userDto.Level, Login = userDto.Login, PasswordHash = salt + ComputeHash(salt, userDto.Password) }; db.Users.Add(user); try { db.SaveChanges(); } catch //(Exception ex) { return -6; } return 0; } public int ChangePassword(string userLogin, string newPassword) { var user = db.Users.AsNoTracking().FirstOrDefault(u => u.Login == userLogin); if (user == null) return -1; var salt = GenerateSalt(); user.PasswordHash = salt + ComputeHash(salt, newPassword); db.SaveChanges(); return 0; } public int ChangePassword(int idUser, string newPassword) { var user = db.Users.FirstOrDefault(u => u.Id == idUser); if (user == null) return -1; var salt = GenerateSalt(); user.PasswordHash = salt + ComputeHash(salt, newPassword); db.SaveChanges(); return 0; } private static string MakeToken(IEnumerable claims) { var now = DateTime.Now; var jwt = new JwtSecurityToken( issuer: issuer, audience: audience, notBefore: now, claims: claims, expires: now.Add(expiresTimespan), signingCredentials: new SigningCredentials(securityKey, algorithms)); return new JwtSecurityTokenHandler().WriteToken(jwt); } private async Task<(ClaimsIdentity Identity, User User)> GetClaimsUserAsync(string login, string password, CancellationToken token = default) { var user = await db .GetUsersByLogin(login) .AsNoTracking() .FirstOrDefaultAsync(token) .ConfigureAwait(false); if (user is null) return default; if (!CheckPassword(user.PasswordHash, password)) return default; var claims = new List { new Claim(claimIdUser, user.Id.ToString()), new Claim(ClaimsIdentity.DefaultNameClaimType, user.Login), new Claim(ClaimsIdentity.DefaultRoleClaimType, user.Role?.Caption??"GUEST"), new Claim(claimNameidCompany, user.IdCompany.ToString()), }; var claimsIdentity = new ClaimsIdentity(claims, "Token", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType); return (claimsIdentity, user); } private bool CheckPassword(string passwordHash, string password) { if (passwordHash.Length == 0 && password.Length == 0) return true; if (passwordHash.Length < PasswordSaltLength) return false; var salt = passwordHash[0..PasswordSaltLength]; var hashDb = passwordHash[PasswordSaltLength..]; return hashDb == ComputeHash(salt, password); } private string ComputeHash(string salt, string password) { var hashBytes = hashAlgoritm.ComputeHash(encoding.GetBytes(salt + password)); var hashString = BitConverter.ToString(hashBytes) .Replace("-", "") .ToLower(); return hashString; } public string GenerateSalt() { const string saltChars = "sHwiaX7kZT1QRp0cPILGUuK2Sz=9q8lmejDNfoYCE3B_WtgyVv6M5OxAJ4Frbhnd"; string salt = ""; for (int i = 0; i < PasswordSaltLength - 1; i++) salt += saltChars[rnd.Next(0, saltChars.Length)]; salt += "|"; return salt; } } }