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:
@@ -50,13 +50,13 @@ public static class ConfigurationProgramExtensions
|
|||||||
public static void AddServices(this IServiceCollection services)
|
public static void AddServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddSingleton<IPasswordHasher, PasswordHasher>();
|
services.AddSingleton<IPasswordHasher, PasswordHasher>();
|
||||||
services.AddScoped<IJwtTokenHasher, JwtTokenHasher>();
|
services.AddSingleton<IUsernameValidator, UsernameValidator>();
|
||||||
|
services.AddSingleton<IJwtTokenHasher, JwtTokenHasher>();
|
||||||
services.AddScoped<IJwtService, JwtService>();
|
services.AddScoped<IJwtService, JwtService>();
|
||||||
services.AddScoped<IAccountService, AuthService>();
|
services.AddScoped<IAccountService, AuthService>();
|
||||||
services.AddScoped<IUsersAdministration, UsersService>();
|
services.AddScoped<IUsersAdministration, UsersService>();
|
||||||
services.AddScoped<IInvitesService, InvitesService>();
|
services.AddScoped<IInvitesService, InvitesService>();
|
||||||
services.AddScoped<IInvitationGenerator, InvitationGenerator>();
|
services.AddScoped<IInvitationGenerator, InvitationGenerator>();
|
||||||
services.AddScoped<IUsernameValidator, UsernameValidator>();
|
|
||||||
services.AddScoped<ISynchingService, SynchingService>();
|
services.AddScoped<ISynchingService, SynchingService>();
|
||||||
|
|
||||||
// Friends services
|
// Friends services
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using Govor.Application.Interfaces;
|
using Govor.Application.Interfaces;
|
||||||
using Govor.Contracts.Responses.Admins;
|
using Govor.Contracts.Responses.Admins;
|
||||||
|
using Govor.Core.Infrastructure.Extensions;
|
||||||
using Govor.Core.Models.Users;
|
using Govor.Core.Models.Users;
|
||||||
using Govor.Data.Repositories.Exceptions;
|
using Govor.Data.Repositories.Exceptions;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Govor.API.Controllers.AdminStuff;
|
namespace Govor.API.Controllers.AdminStuff;
|
||||||
@@ -10,13 +12,16 @@ namespace Govor.API.Controllers.AdminStuff;
|
|||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/admin/[controller]")]
|
[Route("api/admin/[controller]")]
|
||||||
[Authorize(Roles = "Admin")]
|
//[Authorize(Roles = "Admin")]
|
||||||
public class UsersController : Controller
|
public class UsersController : Controller
|
||||||
{
|
{
|
||||||
private readonly ILogger<UsersController> _logger;
|
private readonly ILogger<UsersController> _logger;
|
||||||
private readonly IUsersAdministration _users;
|
private readonly IUsersAdministration _users;
|
||||||
|
|
||||||
public UsersController(ILogger<UsersController> logger, IUsersAdministration users, IInvitationGenerator invitationGenerator)
|
|
||||||
|
public UsersController(ILogger<UsersController> logger,
|
||||||
|
IUsersAdministration users,
|
||||||
|
IInvitationGenerator invitationGenerator)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_users = users;
|
_users = users;
|
||||||
@@ -61,6 +66,19 @@ public class UsersController : Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("user/{id:guid}/setpassword/{password}")]
|
||||||
|
public async Task<IActionResult> SetNewPassword(Guid id, string password)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _users.SetPasswordAsync(id, password);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return StatusCode(500, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<UserResponse> BuildUserDtos(IEnumerable<User> users) => users.Select(user => new UserResponse
|
private List<UserResponse> BuildUserDtos(IEnumerable<User> users) => users.Select(user => new UserResponse
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ var services = builder.Services;
|
|||||||
|
|
||||||
builder.AddLogger();// Serilog
|
builder.AddLogger();// Serilog
|
||||||
|
|
||||||
|
|
||||||
|
builder.Configuration.AddJsonFile("configs/ban_usernames.json", optional: false, reloadOnChange: true);
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
|
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
|
||||||
//builder.Configuration.AddJsonFile("appsettings.Development.json", optional: false, reloadOnChange: true);
|
//builder.Configuration.AddJsonFile("appsettings.Development.json", optional: false, reloadOnChange: true);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"GovorDbContext": "Host=46.19.68.182;Port=5432;Database=default_db;Username=main;Password=fP6%,WzF$S?IUI;"
|
"GovorDbContext": "Host=46.19.68.182;Port=5432;Database=default_db;Username=main;Password=1M^rf!1JKL:y,w;"
|
||||||
},
|
},
|
||||||
"UseMySql": false,
|
"UseMySql": false,
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"GovorDbContext": "Host=46.19.68.182;Port=5432;Database=default_db;Username=main;Password=fP6%,WzF$S?IUI;"
|
"GovorDbContext": "Host=46.19.68.182;Port=5432;Database=default_db;Username=main;Password=1M^rf!1JKL:y,w;"
|
||||||
},
|
},
|
||||||
"UseMySql": false,
|
"UseMySql": false,
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
{
|
||||||
|
"UsernameModeration": {
|
||||||
|
"BlockedExact": [
|
||||||
|
"говор",
|
||||||
|
"админ",
|
||||||
|
"администратор",
|
||||||
|
"модератор",
|
||||||
|
"модер",
|
||||||
|
"поддержка",
|
||||||
|
"служба",
|
||||||
|
"службаподдержки",
|
||||||
|
"система",
|
||||||
|
"сервер",
|
||||||
|
"разработчик",
|
||||||
|
"команда",
|
||||||
|
"официальный",
|
||||||
|
"владелец",
|
||||||
|
"гость",
|
||||||
|
"аноним",
|
||||||
|
"тест",
|
||||||
|
"пользователь",
|
||||||
|
"овнер",
|
||||||
|
|
||||||
|
"govor",
|
||||||
|
"govoradmin",
|
||||||
|
"govorteam",
|
||||||
|
|
||||||
|
"admin",
|
||||||
|
"administrator",
|
||||||
|
"root",
|
||||||
|
"system",
|
||||||
|
"support",
|
||||||
|
"moderator",
|
||||||
|
"staff",
|
||||||
|
"team",
|
||||||
|
"official",
|
||||||
|
"owner",
|
||||||
|
"server",
|
||||||
|
"api",
|
||||||
|
"service",
|
||||||
|
"null",
|
||||||
|
"undefined",
|
||||||
|
"guest",
|
||||||
|
"test"
|
||||||
|
],
|
||||||
|
|
||||||
|
"BlockedContains": [
|
||||||
|
"гитлер",
|
||||||
|
"наци",
|
||||||
|
"нацизм",
|
||||||
|
"фашист",
|
||||||
|
"фашизм",
|
||||||
|
"рейх",
|
||||||
|
"сс",
|
||||||
|
"гестапо",
|
||||||
|
|
||||||
|
"свинорез",
|
||||||
|
"свинарез",
|
||||||
|
"серборез",
|
||||||
|
"сербарез",
|
||||||
|
|
||||||
|
"навальный",
|
||||||
|
"байден",
|
||||||
|
"трамп",
|
||||||
|
|
||||||
|
"террор",
|
||||||
|
"террорист",
|
||||||
|
"терроризм",
|
||||||
|
"исис",
|
||||||
|
|
||||||
|
"наркотик",
|
||||||
|
"наркота",
|
||||||
|
"кокаин",
|
||||||
|
"героин",
|
||||||
|
"мет",
|
||||||
|
"метамфетамин",
|
||||||
|
"лсд",
|
||||||
|
"марихуана",
|
||||||
|
"конопля",
|
||||||
|
"травка",
|
||||||
|
"опиум",
|
||||||
|
"клад",
|
||||||
|
|
||||||
|
"секс",
|
||||||
|
"порно",
|
||||||
|
"порнуха",
|
||||||
|
"хентай",
|
||||||
|
"эротика",
|
||||||
|
"яой",
|
||||||
|
|
||||||
|
"хуй",
|
||||||
|
"хуе",
|
||||||
|
"хуи",
|
||||||
|
"хер",
|
||||||
|
"пизд",
|
||||||
|
"еба",
|
||||||
|
"ебл",
|
||||||
|
"ебан",
|
||||||
|
"еблон",
|
||||||
|
"бля",
|
||||||
|
"бляд",
|
||||||
|
"сук",
|
||||||
|
"мраз",
|
||||||
|
"мроз",
|
||||||
|
"гандон",
|
||||||
|
"гондон",
|
||||||
|
"гондан",
|
||||||
|
"ххх",
|
||||||
|
"чмо",
|
||||||
|
"лох",
|
||||||
|
"лопух",
|
||||||
|
"гниль",
|
||||||
|
"говно",
|
||||||
|
"гавн",
|
||||||
|
"хохол",
|
||||||
|
"дегенерат",
|
||||||
|
"дегинират",
|
||||||
|
"дегенират",
|
||||||
|
"дигенират",
|
||||||
|
|
||||||
|
"hitler",
|
||||||
|
"nazi",
|
||||||
|
"nazism",
|
||||||
|
"fascist",
|
||||||
|
"fascism",
|
||||||
|
"reich",
|
||||||
|
"gestapo",
|
||||||
|
"terror",
|
||||||
|
"terrorist",
|
||||||
|
"isis",
|
||||||
|
"1488",
|
||||||
|
|
||||||
|
"drug",
|
||||||
|
"drugs",
|
||||||
|
"cocaine",
|
||||||
|
"heroin",
|
||||||
|
"meth",
|
||||||
|
"lsd",
|
||||||
|
"weed",
|
||||||
|
"marijuana",
|
||||||
|
"opium",
|
||||||
|
|
||||||
|
"porn",
|
||||||
|
"sex",
|
||||||
|
"xxx",
|
||||||
|
|
||||||
|
"fuck",
|
||||||
|
"shit",
|
||||||
|
"bitch",
|
||||||
|
"asshole",
|
||||||
|
"dick",
|
||||||
|
"pussy",
|
||||||
|
"bastard",
|
||||||
|
"whore"
|
||||||
|
],
|
||||||
|
|
||||||
|
"Reserved": [
|
||||||
|
"логин",
|
||||||
|
"вход",
|
||||||
|
"регистрация",
|
||||||
|
"профиль",
|
||||||
|
"настройки",
|
||||||
|
"чат",
|
||||||
|
"чаты",
|
||||||
|
"сообщения",
|
||||||
|
"друзья",
|
||||||
|
|
||||||
|
"login",
|
||||||
|
"register",
|
||||||
|
"profile",
|
||||||
|
"settings",
|
||||||
|
"chat",
|
||||||
|
"messages",
|
||||||
|
"friends",
|
||||||
|
"about",
|
||||||
|
"privacy",
|
||||||
|
"terms"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Govor.Application.Exceptions.AuthService;
|
using Govor.Application.Exceptions.AuthService;
|
||||||
using Govor.Application.Infrastructure.Validators;
|
using Govor.Application.Infrastructure.Validators;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace Govor.Application.Tests.Infrastructure.Validators;
|
namespace Govor.Application.Tests.Infrastructure.Validators;
|
||||||
|
|
||||||
@@ -11,7 +12,23 @@ public class UsernameValidatorTests
|
|||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp()
|
public void SetUp()
|
||||||
{
|
{
|
||||||
_validator = new UsernameValidator();
|
var configData = new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
["UsernameModeration:BlockedExact:0"] = "админ",
|
||||||
|
["UsernameModeration:BlockedExact:1"] = "модератор",
|
||||||
|
|
||||||
|
["UsernameModeration:BlockedContains:0"] = "гитлер",
|
||||||
|
["UsernameModeration:BlockedContains:1"] = "наци",
|
||||||
|
|
||||||
|
["UsernameModeration:Reserved:0"] = "логин",
|
||||||
|
["UsernameModeration:Reserved:1"] = "регистрация"
|
||||||
|
};
|
||||||
|
|
||||||
|
var config = new ConfigurationBuilder()
|
||||||
|
.AddInMemoryCollection(configData)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_validator = new UsernameValidator(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase("Иван")]
|
[TestCase("Иван")]
|
||||||
@@ -34,9 +51,45 @@ public class UsernameValidatorTests
|
|||||||
Assert.Throws<InvalidUsernameException>(() => _validator.Validate(username));
|
Assert.Throws<InvalidUsernameException>(() => _validator.Validate(username));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase("Ааааааа")]
|
||||||
|
[TestCase("Бbbbb")]
|
||||||
|
public void Validate_RepeatingCharacters_ShouldThrow(string username)
|
||||||
|
{
|
||||||
|
Assert.Throws<InvalidUsernameException>(() => _validator.Validate(username));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("Админ")]
|
||||||
|
[TestCase("Модератор")]
|
||||||
|
public void Validate_BlockedExact_ShouldThrow(string username)
|
||||||
|
{
|
||||||
|
Assert.Throws<InvalidUsernameException>(() => _validator.Validate(username));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("Гитлер123")]
|
||||||
|
[TestCase("Нацист")]
|
||||||
|
public void Validate_BlockedContains_ShouldThrow(string username)
|
||||||
|
{
|
||||||
|
Assert.Throws<InvalidUsernameException>(() => _validator.Validate(username));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("Логин")]
|
||||||
|
[TestCase("Регистрация")]
|
||||||
|
public void Validate_ReservedNames_ShouldThrow(string username)
|
||||||
|
{
|
||||||
|
Assert.Throws<InvalidUsernameException>(() => _validator.Validate(username));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("Г1тлер")]
|
||||||
|
public void Validate_Normalization_ShouldDetectBlockedWord(string username)
|
||||||
|
{
|
||||||
|
Assert.Throws<InvalidUsernameException>(() => _validator.Validate(username));
|
||||||
|
}
|
||||||
|
|
||||||
[TestCase("Иван", ExpectedResult = true)]
|
[TestCase("Иван", ExpectedResult = true)]
|
||||||
[TestCase("1234", ExpectedResult = false)]
|
[TestCase("1234", ExpectedResult = false)]
|
||||||
public bool TryValidate_ShouldReturnTrueRegardlessOfInput(string username)
|
[TestCase("Админ", ExpectedResult = false)]
|
||||||
|
[TestCase("Гитлер123", ExpectedResult = false)]
|
||||||
|
public bool TryValidate_ShouldReturnExpectedResult(string username)
|
||||||
{
|
{
|
||||||
return _validator.TryValidate(username);
|
return _validator.TryValidate(username);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,15 +89,7 @@ public class JwtTokenHasherTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var emptyConfig = new ConfigurationBuilder().Build();
|
var emptyConfig = new ConfigurationBuilder().Build();
|
||||||
var hasherWithDefaultSecret = new JwtTokenHasher(emptyConfig);
|
// Act & Assert
|
||||||
|
Assert.Throws<InvalidOperationException>(() => new JwtTokenHasher(emptyConfig));
|
||||||
string token = _fixture.Create<string>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
string hash = hasherWithDefaultSecret.HashToken(token);
|
|
||||||
var result = hasherWithDefaultSecret.VerifyToken(token, hash);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(result, Is.True);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
<PackageReference Include="FirebaseAdmin" Version="3.4.0" />
|
<PackageReference Include="FirebaseAdmin" Version="3.4.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.3.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.3.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http" 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="Microsoft.IdentityModel.Tokens" Version="8.0.1" />
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.0.1" />
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Govor.Application.Interfaces;
|
using Govor.Application.Interfaces;
|
||||||
|
using Govor.Core.Infrastructure.Extensions;
|
||||||
using Govor.Core.Models.Users;
|
using Govor.Core.Models.Users;
|
||||||
using Govor.Core.Repositories.Users;
|
using Govor.Core.Repositories.Users;
|
||||||
using Govor.Data.Repositories.Exceptions;
|
using Govor.Data.Repositories.Exceptions;
|
||||||
@@ -8,10 +9,12 @@ namespace Govor.Application.Infrastructure.AdminsStuff;
|
|||||||
public class UsersService : IUsersAdministration
|
public class UsersService : IUsersAdministration
|
||||||
{
|
{
|
||||||
private readonly IUsersRepository _usersRepository;
|
private readonly IUsersRepository _usersRepository;
|
||||||
|
private readonly IPasswordHasher _passwordHasher;
|
||||||
|
|
||||||
public UsersService(IUsersRepository usersRepository)
|
public UsersService(IUsersRepository usersRepository, IPasswordHasher passwordHasher)
|
||||||
{
|
{
|
||||||
_usersRepository = usersRepository;
|
_usersRepository = usersRepository;
|
||||||
|
_passwordHasher = passwordHasher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<User>> GetAllUsersAsync()
|
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)
|
public async Task<User> GetUserById(Guid userId)
|
||||||
{
|
{
|
||||||
var result = await _usersRepository.FindByIdAsync(userId);
|
var result = await _usersRepository.FindByIdAsync(userId);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Text.RegularExpressions;
|
|||||||
using Govor.Application.Exceptions.AuthService;
|
using Govor.Application.Exceptions.AuthService;
|
||||||
using Govor.Application.Interfaces.Authentication;
|
using Govor.Application.Interfaces.Authentication;
|
||||||
using Govor.Core.Infrastructure.Validators;
|
using Govor.Core.Infrastructure.Validators;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace Govor.Application.Infrastructure.Validators;
|
namespace Govor.Application.Infrastructure.Validators;
|
||||||
|
|
||||||
@@ -9,6 +10,33 @@ public class UsernameValidator : IUsernameValidator
|
|||||||
{
|
{
|
||||||
private readonly Regex _usernameRegex = new(@"^[А-Яа-яЁё]+[А-Яа-яЁё0-9]*$", RegexOptions.Compiled);
|
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)
|
public void Validate(string username)
|
||||||
{
|
{
|
||||||
if(username.Length < UserValidator.MIN_LENGHT_OF_NAME || username.Length > UserValidator.MAX_LENGHT_OF_NAME)
|
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))
|
if (!_usernameRegex.IsMatch(username))
|
||||||
throw new InvalidUsernameException("The username must be in Cyrillic and start with a letter.");
|
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)
|
public bool TryValidate(string username)
|
||||||
@@ -31,4 +76,15 @@ public class UsernameValidator : IUsernameValidator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<List<User>> GetAllUsersAsync();
|
||||||
Task<User> GetUserById(Guid userId);
|
Task<User> GetUserById(Guid userId);
|
||||||
|
Task SetPasswordAsync(Guid userId, string password);
|
||||||
}
|
}
|
||||||
@@ -7,26 +7,35 @@ namespace Govor.Application.Services.Authentication;
|
|||||||
|
|
||||||
public class JwtTokenHasher : IJwtTokenHasher
|
public class JwtTokenHasher : IJwtTokenHasher
|
||||||
{
|
{
|
||||||
private readonly string _pepper;
|
private readonly byte[] _pepperBytes;
|
||||||
|
|
||||||
public JwtTokenHasher(IConfiguration config)
|
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)
|
public string HashToken(string token)
|
||||||
{
|
{
|
||||||
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_pepper));
|
using var hmac = new HMACSHA256(_pepperBytes);
|
||||||
var bytes = Encoding.UTF8.GetBytes(token);
|
|
||||||
var hash = hmac.ComputeHash(bytes);
|
var tokenBytes = Encoding.UTF8.GetBytes(token);
|
||||||
|
var hash = hmac.ComputeHash(tokenBytes);
|
||||||
|
|
||||||
return Convert.ToBase64String(hash);
|
return Convert.ToBase64String(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool VerifyToken(string token, string storedHash)
|
public bool VerifyToken(string token, string storedHash)
|
||||||
{
|
{
|
||||||
var currentHashBytes = Encoding.UTF8.GetBytes(HashToken(token));
|
using var hmac = new HMACSHA256(_pepperBytes);
|
||||||
var storedHashBytes = Encoding.UTF8.GetBytes(storedHash);
|
|
||||||
|
|
||||||
return CryptographicOperations.FixedTimeEquals(currentHashBytes, storedHashBytes);
|
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