mirror of
https://github.com/Govor-team/Govor.git
synced 2026-07-21 11:44:56 +00:00
was added new rules to usernames
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
<PackageReference Include="FirebaseAdmin" Version="3.4.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="11.0.0-preview.1.26104.118" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.0.1" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Govor.Application.Interfaces;
|
||||
using Govor.Core.Infrastructure.Extensions;
|
||||
using Govor.Core.Models.Users;
|
||||
using Govor.Core.Repositories.Users;
|
||||
using Govor.Data.Repositories.Exceptions;
|
||||
@@ -8,10 +9,12 @@ namespace Govor.Application.Infrastructure.AdminsStuff;
|
||||
public class UsersService : IUsersAdministration
|
||||
{
|
||||
private readonly IUsersRepository _usersRepository;
|
||||
|
||||
public UsersService(IUsersRepository usersRepository)
|
||||
private readonly IPasswordHasher _passwordHasher;
|
||||
|
||||
public UsersService(IUsersRepository usersRepository, IPasswordHasher passwordHasher)
|
||||
{
|
||||
_usersRepository = usersRepository;
|
||||
_passwordHasher = passwordHasher;
|
||||
}
|
||||
|
||||
public async Task<List<User>> GetAllUsersAsync()
|
||||
@@ -27,6 +30,22 @@ public class UsersService : IUsersAdministration
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SetPasswordAsync(Guid userId, string password)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _usersRepository.FindByIdAsync(userId);
|
||||
|
||||
user.PasswordHash = _passwordHasher.Hash(password);
|
||||
|
||||
await _usersRepository.UpdateAsync(user);
|
||||
}
|
||||
catch (NotFoundException ex)
|
||||
{
|
||||
throw new NotFoundException(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<User> GetUserById(Guid userId)
|
||||
{
|
||||
var result = await _usersRepository.FindByIdAsync(userId);
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Text.RegularExpressions;
|
||||
using Govor.Application.Exceptions.AuthService;
|
||||
using Govor.Application.Interfaces.Authentication;
|
||||
using Govor.Core.Infrastructure.Validators;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Govor.Application.Infrastructure.Validators;
|
||||
|
||||
@@ -9,6 +10,33 @@ public class UsernameValidator : IUsernameValidator
|
||||
{
|
||||
private readonly Regex _usernameRegex = new(@"^[А-Яа-яЁё]+[А-Яа-яЁё0-9]*$", RegexOptions.Compiled);
|
||||
|
||||
private readonly HashSet<string> _blockedExact;
|
||||
private readonly List<string> _blockedContains;
|
||||
private readonly HashSet<string> _reserved;
|
||||
|
||||
public UsernameValidator(IConfiguration config)
|
||||
{
|
||||
_blockedExact = config.GetSection("UsernameModeration:BlockedExact")
|
||||
.Get<string[]>()?
|
||||
.Select(Normalize)
|
||||
.ToHashSet()
|
||||
?? throw new InvalidOperationException("BlockedExact not set");
|
||||
|
||||
_blockedContains = config
|
||||
.GetSection("UsernameModeration:BlockedContains")
|
||||
.Get<string[]>()?
|
||||
.Select(Normalize)
|
||||
.ToList()
|
||||
?? throw new InvalidOperationException("BlockedContains not set");
|
||||
|
||||
_reserved = config
|
||||
.GetSection("UsernameModeration:Reserved")
|
||||
.Get<string[]>()?
|
||||
.Select(Normalize)
|
||||
.ToHashSet()
|
||||
?? throw new InvalidOperationException("Reserved not set");
|
||||
}
|
||||
|
||||
public void Validate(string username)
|
||||
{
|
||||
if(username.Length < UserValidator.MIN_LENGHT_OF_NAME || username.Length > UserValidator.MAX_LENGHT_OF_NAME)
|
||||
@@ -16,6 +44,23 @@ public class UsernameValidator : IUsernameValidator
|
||||
|
||||
if (!_usernameRegex.IsMatch(username))
|
||||
throw new InvalidUsernameException("The username must be in Cyrillic and start with a letter.");
|
||||
|
||||
if (Regex.IsMatch(username, @"(.)\1{4,}"))
|
||||
throw new InvalidUsernameException("Too many repeating characters.");
|
||||
|
||||
var normalized = Normalize(username);
|
||||
|
||||
if (_reserved.Contains(normalized))
|
||||
throw new InvalidUsernameException("This username is reserved.");
|
||||
|
||||
if (_blockedExact.Contains(normalized))
|
||||
throw new InvalidUsernameException("This username is not allowed.");
|
||||
|
||||
foreach (var banned in _blockedContains)
|
||||
{
|
||||
if (normalized.Contains(banned))
|
||||
throw new InvalidUsernameException("Username contains prohibited content.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryValidate(string username)
|
||||
@@ -30,5 +75,16 @@ public class UsernameValidator : IUsernameValidator
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static string Normalize(string username)
|
||||
{
|
||||
return username
|
||||
.ToLower()
|
||||
.Replace("0", "о")
|
||||
.Replace("1", "и")
|
||||
.Replace("3", "е")
|
||||
.Replace("4", "а")
|
||||
.Replace("6", "б")
|
||||
.Replace("8", "в");
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,5 @@ public interface IUsersAdministration
|
||||
{
|
||||
Task<List<User>> GetAllUsersAsync();
|
||||
Task<User> GetUserById(Guid userId);
|
||||
Task SetPasswordAsync(Guid userId, string password);
|
||||
}
|
||||
@@ -7,26 +7,35 @@ namespace Govor.Application.Services.Authentication;
|
||||
|
||||
public class JwtTokenHasher : IJwtTokenHasher
|
||||
{
|
||||
private readonly string _pepper;
|
||||
private readonly byte[] _pepperBytes;
|
||||
|
||||
public JwtTokenHasher(IConfiguration config)
|
||||
{
|
||||
_pepper = config["EncryptionOption:Secret"] ?? "D1fault%Lxng%Randxm^Secret^Key(123!";
|
||||
var pepper = config["EncryptionOption:Secret"]
|
||||
?? throw new InvalidOperationException("Pepper is missing");
|
||||
|
||||
_pepperBytes = Encoding.UTF8.GetBytes(pepper);
|
||||
}
|
||||
|
||||
public string HashToken(string token)
|
||||
{
|
||||
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_pepper));
|
||||
var bytes = Encoding.UTF8.GetBytes(token);
|
||||
var hash = hmac.ComputeHash(bytes);
|
||||
using var hmac = new HMACSHA256(_pepperBytes);
|
||||
|
||||
var tokenBytes = Encoding.UTF8.GetBytes(token);
|
||||
var hash = hmac.ComputeHash(tokenBytes);
|
||||
|
||||
return Convert.ToBase64String(hash);
|
||||
}
|
||||
|
||||
public bool VerifyToken(string token, string storedHash)
|
||||
{
|
||||
var currentHashBytes = Encoding.UTF8.GetBytes(HashToken(token));
|
||||
var storedHashBytes = Encoding.UTF8.GetBytes(storedHash);
|
||||
|
||||
return CryptographicOperations.FixedTimeEquals(currentHashBytes, storedHashBytes);
|
||||
using var hmac = new HMACSHA256(_pepperBytes);
|
||||
|
||||
var tokenBytes = Encoding.UTF8.GetBytes(token);
|
||||
var computedHash = hmac.ComputeHash(tokenBytes);
|
||||
|
||||
var storedHashBytes = Convert.FromBase64String(storedHash);
|
||||
|
||||
return CryptographicOperations.FixedTimeEquals(computedHash, storedHashBytes);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user