AuthController work

This commit is contained in:
Artemy
2025-06-18 16:55:19 +07:00
parent 2c2c2b805c
commit 7d518c0882
26 changed files with 397 additions and 63 deletions
@@ -53,7 +53,7 @@ public class UsersRepositoryTests
Assert.That(result, Is.Not.Null); Assert.That(result, Is.Not.Null);
Assert.That(result.Count, Is.EqualTo(users.Count)); Assert.That(result.Count, Is.EqualTo(users.Count));
Assert.That(result.Select(u => u.Id), Is.EquivalentTo(users.Select(u => u.Id))); Assert.That(result.Select(u => u.Id), Is.EquivalentTo(users.Select(u => u.Id)));
Assert.That(result.Select(u => u.Name), Is.EquivalentTo(users.Select(u => u.Name))); Assert.That(result.Select(u => u.Username), Is.EquivalentTo(users.Select(u => u.Username)));
} }
[Test] [Test]
@@ -72,7 +72,7 @@ public class UsersRepositoryTests
// Assert // Assert
Assert.That(result, Is.Not.Null); Assert.That(result, Is.Not.Null);
Assert.That(result.Name, Is.EqualTo(user.Name)); Assert.That(result.Username, Is.EqualTo(user.Username));
Assert.That(result.Id, Is.EqualTo(user.Id)); Assert.That(result.Id, Is.EqualTo(user.Id));
} }
@@ -109,7 +109,7 @@ public class UsersRepositoryTests
Assert.That(result, Is.Not.Null); Assert.That(result, Is.Not.Null);
Assert.That(result.Count, Is.EqualTo(users.Count)); Assert.That(result.Count, Is.EqualTo(users.Count));
Assert.That(result.Select(r => r.Id), Is.EquivalentTo(users.Select(u => u.Id))); Assert.That(result.Select(r => r.Id), Is.EquivalentTo(users.Select(u => u.Id)));
Assert.That(result.Select(u => u.Name), Is.EquivalentTo(users.Select(u => u.Name))); Assert.That(result.Select(u => u.Username), Is.EquivalentTo(users.Select(u => u.Username)));
} }
[Test] [Test]
@@ -140,10 +140,10 @@ public class UsersRepositoryTests
await context.SaveChangesAsync(); await context.SaveChangesAsync();
// Act // Act
var result = await userRepository.FindByUsernameAsync(user.Name); var result = await userRepository.FindByUsernameAsync(user.Username);
// Assert // Assert
Assert.That(result, Is.Not.Null); Assert.That(result, Is.Not.Null);
Assert.That(result.Name, Is.EqualTo(user.Name)); Assert.That(result.Username, Is.EqualTo(user.Username));
Assert.That(result.Id, Is.EqualTo(user.Id)); Assert.That(result.Id, Is.EqualTo(user.Id));
} }
@@ -174,11 +174,11 @@ public class UsersRepositoryTests
await context.SaveChangesAsync(); await context.SaveChangesAsync();
// Act // Act
var result = await userRepository.FindByRangeUsernamesAsync(users.Select(u => u.Name)); var result = await userRepository.FindByRangeUsernamesAsync(users.Select(u => u.Username));
// Assert // Assert
Assert.That(result, Is.Not.Null); Assert.That(result, Is.Not.Null);
Assert.That(result.Select(u => u.Name), Is.EquivalentTo(users.Select(u => u.Name))); Assert.That(result.Select(u => u.Username), Is.EquivalentTo(users.Select(u => u.Username)));
Assert.That(result.Select(u => u.Id), Is.EquivalentTo(users.Select(u => u.Id))); Assert.That(result.Select(u => u.Id), Is.EquivalentTo(users.Select(u => u.Id)));
} }
@@ -254,7 +254,7 @@ public class UsersRepositoryTests
// Assert // Assert
Assert.That(res, Is.Not.Null); Assert.That(res, Is.Not.Null);
Assert.That(res.Name, Is.EqualTo(user.Name)); Assert.That(res.Username, Is.EqualTo(user.Username));
Assert.That(res.Id, Is.EqualTo(user.Id)); Assert.That(res.Id, Is.EqualTo(user.Id));
} }
@@ -262,7 +262,7 @@ public class UsersRepositoryTests
public async Task Given_InvalidUser_When_AddUser_Should_Throw_AdditionUserException() public async Task Given_InvalidUser_When_AddUser_Should_Throw_AdditionUserException()
{ {
var user = _fixture.Create<User>(); var user = _fixture.Create<User>();
user.Name = string.Empty; user.Username = string.Empty;
await using var context = new GovorDbContext(_options); await using var context = new GovorDbContext(_options);
var userRepository = new UsersRepository(context, _userValidator); var userRepository = new UsersRepository(context, _userValidator);
@@ -361,7 +361,7 @@ public class UsersRepositoryTests
await context.SaveChangesAsync(); await context.SaveChangesAsync();
// Act // Act
var res = await userRepository.ExistsUsernameAsync(user.Name); var res = await userRepository.ExistsUsernameAsync(user.Username);
// Assert // Assert
Assert.That(res, Is.True); Assert.That(res, Is.True);
@@ -378,7 +378,7 @@ public class UsersRepositoryTests
// Act // Act
var res = await userRepository.ExistsUsernameAsync(user.Name); var res = await userRepository.ExistsUsernameAsync(user.Username);
// Assert // Assert
Assert.That(res, Is.False); Assert.That(res, Is.False);
@@ -46,7 +46,7 @@ public class UserValidatorTests
User user = new User() User user = new User()
{ {
PasswordHash = _fixture.Create<string>(), PasswordHash = _fixture.Create<string>(),
Name = _fixture.Create<string>(), Username = _fixture.Create<string>(),
Id = Guid.Empty, Id = Guid.Empty,
CreatedOn = DateOnly.FromDateTime(DateTime.Now), CreatedOn = DateOnly.FromDateTime(DateTime.Now),
}; };
@@ -62,7 +62,7 @@ public class UserValidatorTests
User user = new User() User user = new User()
{ {
PasswordHash = _fixture.Create<string>(), PasswordHash = _fixture.Create<string>(),
Name = _fixture.Create<string>(), Username = _fixture.Create<string>(),
Id = Guid.Empty, Id = Guid.Empty,
CreatedOn = DateOnly.FromDateTime(DateTime.Now), CreatedOn = DateOnly.FromDateTime(DateTime.Now),
}; };
@@ -78,7 +78,7 @@ public class UserValidatorTests
User user = new User() User user = new User()
{ {
PasswordHash = string.Empty, PasswordHash = string.Empty,
Name = _fixture.Create<string>(), Username = _fixture.Create<string>(),
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
CreatedOn = DateOnly.FromDateTime(DateTime.Now), CreatedOn = DateOnly.FromDateTime(DateTime.Now),
}; };
@@ -94,7 +94,7 @@ public class UserValidatorTests
User user = new User() User user = new User()
{ {
PasswordHash = string.Empty, PasswordHash = string.Empty,
Name = _fixture.Create<string>(), Username = _fixture.Create<string>(),
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
CreatedOn = DateOnly.FromDateTime(DateTime.Now), CreatedOn = DateOnly.FromDateTime(DateTime.Now),
}; };
@@ -110,7 +110,7 @@ public class UserValidatorTests
User user = new User() User user = new User()
{ {
PasswordHash = _fixture.Create<string>(), PasswordHash = _fixture.Create<string>(),
Name = String.Empty, Username = String.Empty,
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
CreatedOn = DateOnly.FromDateTime(DateTime.Now), CreatedOn = DateOnly.FromDateTime(DateTime.Now),
}; };
@@ -126,7 +126,7 @@ public class UserValidatorTests
User user = new User() User user = new User()
{ {
PasswordHash = _fixture.Create<string>(), PasswordHash = _fixture.Create<string>(),
Name = String.Empty, Username = String.Empty,
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
CreatedOn = DateOnly.FromDateTime(DateTime.Now), CreatedOn = DateOnly.FromDateTime(DateTime.Now),
}; };
@@ -142,7 +142,7 @@ public class UserValidatorTests
User user = new User() User user = new User()
{ {
PasswordHash = _fixture.Create<string>(), PasswordHash = _fixture.Create<string>(),
Name = _fixture.Create<string>(), Username = _fixture.Create<string>(),
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
CreatedOn = DateOnly.FromDateTime(DateTime.Now), CreatedOn = DateOnly.FromDateTime(DateTime.Now),
}; };
@@ -158,7 +158,7 @@ public class UserValidatorTests
User user = new User() User user = new User()
{ {
PasswordHash = _fixture.Create<string>(), PasswordHash = _fixture.Create<string>(),
Name = _fixture.Create<string>(), Username = _fixture.Create<string>(),
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
CreatedOn = DateOnly.FromDateTime(DateTime.Now), CreatedOn = DateOnly.FromDateTime(DateTime.Now),
}; };
@@ -0,0 +1,11 @@
namespace Govor.API.Tests.UnitTests.Services;
[TestFixture]
public class AuthServiceTests
{
public void SetUp()
{
}
}
+9 -7
View File
@@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc;
namespace Govor.API.Controllers; namespace Govor.API.Controllers;
[ApiController] [ApiController]
[Route("[controller]")] [Route("api/[controller]")]
public class AuthController : Controller public class AuthController : Controller
{ {
private IAccountService _accountService; private IAccountService _accountService;
@@ -18,7 +18,7 @@ public class AuthController : Controller
_logger = logger; _logger = logger;
} }
[HttpPost("register")] [HttpPost("register")]// api/auth/register
[RequireHttps] [RequireHttps]
public async Task<IActionResult> Register([FromBody] UserDto userDto) public async Task<IActionResult> Register([FromBody] UserDto userDto)
{ {
@@ -29,8 +29,9 @@ public class AuthController : Controller
return BadRequest(ModelState); return BadRequest(ModelState);
} }
await _accountService.RegistrationAsync(userDto.Name, userDto.Password); var token = await _accountService.RegistrationAsync(userDto.Name, userDto.Password);
return Ok(new { Message = "User registered successfully" }); _logger.LogInformation($"Register request for {userDto.Name}");
return Ok(new { token });
} }
catch (UserAlreadyExistException ex) catch (UserAlreadyExistException ex)
{ {
@@ -44,7 +45,7 @@ public class AuthController : Controller
} }
} }
[HttpPost("login")] [HttpPost("login")]// api/auth/login
[RequireHttps] [RequireHttps]
public async Task<IActionResult> Login([FromBody] UserDto userDto) public async Task<IActionResult> Login([FromBody] UserDto userDto)
{ {
@@ -55,8 +56,9 @@ public class AuthController : Controller
return BadRequest(ModelState); return BadRequest(ModelState);
} }
await _accountService.LoginAsync(userDto.Name, userDto.Password); var token = await _accountService.LoginAsync(userDto.Name, userDto.Password);
return Ok(new { Message = "User login successfully" }); _logger.LogInformation($"Login request for {userDto.Name}");
return Ok(new { token });
} }
catch (UserNotRegisteredException ex) catch (UserNotRegisteredException ex)
{ {
@@ -1,4 +1,5 @@
using Govor.Core.Services; using Govor.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace Govor.API.Controllers; namespace Govor.API.Controllers;
@@ -15,6 +16,7 @@ public class InviteController : ControllerBase
} }
[HttpGet("{code}")] [HttpGet("{code}")]
[Authorize]
public IActionResult JoinGroup(string code) public IActionResult JoinGroup(string code)
{ {
var group = _groupService.GetGroupByInvite(code); var group = _groupService.GetGroupByInvite(code);
+3
View File
@@ -8,6 +8,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" /> <PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.6" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.6"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.6">
@@ -15,6 +16,8 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageReference Include="NSwag.AspNetCore" Version="14.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+38 -5
View File
@@ -1,16 +1,41 @@
using Govor.API.Services;
using Govor.API.Services.Authentication;
using Govor.Core.Infrastructure.Extensions;
using Govor.Core.Infrastructure.Validators;
using Govor.Core.Models;
using Govor.Core.Repositories;
using Govor.Core.Repositories.Users;
using Govor.Core.Services;
using Govor.Data; using Govor.Data;
using Govor.Data.Repositories;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;
var services = builder.Services;
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
builder.Services.Configure<JwtOption>(configuration.GetSection(nameof(JwtOption)));
// Add services // Add services
builder.Services.AddSignalR(); builder.Services.AddSignalR();
builder.Services.AddAuthorization();
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer();
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddScoped<IPasswordHasher, PasswordHasher>();
builder.Services.AddScoped<IJwtService, JwtService>();
builder.Services.AddScoped<IAccountService, AuthService>();
builder.Services.AddScoped<IUsersRepository, UsersRepository>();
builder.Services.AddScoped<IObjectValidator<User>, UserValidator>();
builder.Services.AddDbContext<GovorDbContext>( builder.Services.AddDbContext<GovorDbContext>(
options => options =>
{ {
@@ -19,22 +44,30 @@ builder.Services.AddDbContext<GovorDbContext>(
); );
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApi(); builder.Services.AddSwaggerGen();
//builder.Services.AddOpenApi();
builder.Services.AddAuthorization();
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {
app.MapOpenApi(); //app.MapOpenApi();
app.UseSwagger();
app.UseSwaggerUI();
} }
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
app.MapControllers(); app.MapControllers();
app.Map("/hello", [Authorize]() => "Hello World!");
app.Map("/", () => "Not for browser");
app.Run(); app.Run();
@@ -1,5 +1,8 @@
using Govor.Core;
using Govor.Core.Infrastructure.Extensions; using Govor.Core.Infrastructure.Extensions;
using Govor.Core.Models;
using Govor.Core.Repositories; using Govor.Core.Repositories;
using Govor.Core.Repositories.Users;
using Govor.Core.Services; using Govor.Core.Services;
namespace Govor.API.Services.Authentication; namespace Govor.API.Services.Authentication;
@@ -7,16 +10,38 @@ namespace Govor.API.Services.Authentication;
public class AuthService : IAccountService public class AuthService : IAccountService
{ {
private readonly IPasswordHasher _passwordHasher; private readonly IPasswordHasher _passwordHasher;
private readonly IJwtService _jwtService;
private readonly IUsersRepository _usersRepository; private readonly IUsersRepository _usersRepository;
public AuthService(IUsersRepository usersRepository, IPasswordHasher passwordHasher) public AuthService(IUsersRepository usersRepository, IJwtService jwtService, IPasswordHasher passwordHasher)
{ {
_usersRepository = usersRepository; _usersRepository = usersRepository;
_jwtService = jwtService;
_passwordHasher = passwordHasher; _passwordHasher = passwordHasher;
} }
public Task RegistrationAsync(string name, string password)
public async Task<string> RegistrationAsync(string name, string password)
{ {
throw new NotImplementedException(); if (await _usersRepository.ExistsUsernameAsync(name))
throw new UserAlreadyExistException(name);
var passwordHash = _passwordHasher.Hash(password);
var user = new User
{
Id = Guid.NewGuid(),
Username = name,
Description = string.Empty,
PasswordHash = passwordHash,
CreatedOn = DateOnly.FromDateTime(DateTime.Now),
IconId = Guid.NewGuid(),
WasOnline = DateTime.UtcNow
//Role = role == "Admin" ? "Admin" : "User" // Ограничение ролей
};
//await _usersRepository.AddAsync(user);
return _jwtService.GenerateJwtToken(user);
} }
public async Task<string> LoginAsync(string name, string password) public async Task<string> LoginAsync(string name, string password)
@@ -24,10 +49,17 @@ public class AuthService : IAccountService
if (await _usersRepository.ExistsUsernameAsync(name) == false) if (await _usersRepository.ExistsUsernameAsync(name) == false)
throw new UserNotRegisteredException(name); throw new UserNotRegisteredException(name);
throw new NotImplementedException(); var user = await _usersRepository.FindByUsernameAsync(name);
if (_passwordHasher.Verify(password, user.PasswordHash) == false)
throw new LoginUserException();
return _jwtService.GenerateJwtToken(user);
} }
} }
public class UserAlreadyExistException(string username) : Exception($"{username} is already exists!") { } public class LoginUserException : GovorCoreException { }
public class UserNotRegisteredException(string username) : Exception($"{username} is not registered!") { } public class UserAlreadyExistException(string username) : GovorCoreException($"{username} is already exists!") { }
public class UserNotRegisteredException(string username) : GovorCoreException($"{username} is not registered!") { }
@@ -0,0 +1,7 @@
namespace Govor.API.Services.Authentication;
public class JwtOption
{
public string SecretKeу {get; set;}
public int Hours { get; set; }
}
@@ -0,0 +1,32 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Govor.Core.Models;
using Govor.Core.Services;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
namespace Govor.API.Services.Authentication;
public class JwtService : IJwtService
{
private JwtOption _jwtOption;
public JwtService(IOptions<JwtOption> options)
{
_jwtOption = options.Value;
}
public string GenerateJwtToken(User user)
{
Claim[] claims = [new("userID", user.Id.ToString())];
var singing = new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOption.SecretKeу)),
SecurityAlgorithms.HmacSha256Signature);
var token = new JwtSecurityToken(signingCredentials: singing,
expires: DateTime.UtcNow.AddHours(_jwtOption.Hours));
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
+5 -1
View File
@@ -8,5 +8,9 @@
"ConnectionStrings": { "ConnectionStrings": {
"GovorDbContext": "Host=localhost;Port=5432;Database=DbGovor;Username=postgres;Password=stalcker;" "GovorDbContext": "Host=localhost;Port=5432;Database=DbGovor;Username=postgres;Password=stalcker;"
}, },
"AllowedHosts": "*" "AllowedHosts": "*",
"JwtOption": {
"SecretKeу": "MY VERY SECRET KEY asdasdpafjhasofafpajsfj",
"Hours": "24"
}
} }
+11
View File
@@ -1,3 +1,14 @@
// See https://aka.ms/new-console-template for more information // See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!"); Console.WriteLine("Hello, World!");
/*====================================
*Горелые Петушки Чат
*====================================
*
* [16:30] Егор >> "Привет, Питухам!"
* [16:31] Anon >> "Иди нахуй"
*
*
* >> "Привет, Егор!"
*/
@@ -16,10 +16,10 @@ public class UserValidator : IObjectValidator<User>
throw new ArgumentNullException(nameof(user)); throw new ArgumentNullException(nameof(user));
if(user.Id == Guid.Empty) if(user.Id == Guid.Empty)
throw new ArgumentException("User ID cannot be empty", nameof(user.Id)); throw new ArgumentException("User ID cannot be empty", nameof(user.Id));
if(user.Name is null if(user.Username is null
|| user.Name.Length < MIN_LENGHT_OF_NAME || user.Username.Length < MIN_LENGHT_OF_NAME
|| user.Name.Length > MAX_LENGHT_OF_NAME) || user.Username.Length > MAX_LENGHT_OF_NAME)
throw new ArgumentException($"Username cannot be empty or less then {MIN_LENGHT_OF_NAME} chars or more then {MAX_LENGHT_OF_NAME}", nameof(user.Name)); throw new ArgumentException($"Username cannot be empty or less then {MIN_LENGHT_OF_NAME} chars or more then {MAX_LENGHT_OF_NAME}", nameof(user.Username));
if(user.PasswordHash is null || user.PasswordHash == string.Empty) if(user.PasswordHash is null || user.PasswordHash == string.Empty)
throw new ArgumentException("Password cannot be empty", nameof(user.PasswordHash)); throw new ArgumentException("Password cannot be empty", nameof(user.PasswordHash));
if(user.CreatedOn == DateOnly.MinValue) if(user.CreatedOn == DateOnly.MinValue)
+1 -1
View File
@@ -5,7 +5,7 @@ namespace Govor.Core.Models;
public class User public class User
{ {
public Guid Id {get; set;} public Guid Id {get; set;}
public string Name {get; set;} public string Username {get; set;}
public string Description {get; set;} public string Description {get; set;}
public string PasswordHash {get; set;} public string PasswordHash {get; set;}
public Guid IconId {get; set;} public Guid IconId {get; set;}
@@ -0,0 +1,6 @@
namespace Govor.Core.Repositories.Groups;
public class IGroupsReader
{
}
@@ -1,6 +1,6 @@
using Govor.Core.Models; using Govor.Core.Models;
namespace Govor.Core.Repositories; namespace Govor.Core.Repositories.Users;
public interface IUsersExist public interface IUsersExist
{ {
@@ -1,6 +1,6 @@
using Govor.Core.Models; using Govor.Core.Models;
namespace Govor.Core.Repositories; namespace Govor.Core.Repositories.Users;
public interface IUsersReader public interface IUsersReader
{ {
@@ -1,4 +1,4 @@
namespace Govor.Core.Repositories; namespace Govor.Core.Repositories.Users;
public interface IUsersRepository : IUsersReader, IUsersWriter, IUsersExist public interface IUsersRepository : IUsersReader, IUsersWriter, IUsersExist
{ {
@@ -1,6 +1,6 @@
using Govor.Core.Models; using Govor.Core.Models;
namespace Govor.Core.Repositories; namespace Govor.Core.Repositories.Users;
public interface IUsersWriter public interface IUsersWriter
{ {
+3 -1
View File
@@ -1,7 +1,9 @@
using Govor.Core.Models;
namespace Govor.Core.Services; namespace Govor.Core.Services;
public interface IAccountService public interface IAccountService
{ {
public Task RegistrationAsync(string name, string password); public Task<string> RegistrationAsync(string name, string password);
public Task<string> LoginAsync(string name, string password); public Task<string> LoginAsync(string name, string password);
} }
+8
View File
@@ -0,0 +1,8 @@
using Govor.Core.Models;
namespace Govor.Core.Services;
public interface IJwtService
{
string GenerateJwtToken(User user);
}
@@ -0,0 +1,129 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Govor.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Govor.Data.Migrations
{
[DbContext(typeof(GovorDbContext))]
[Migration("20250618082839_AddPasswordHashColumn")]
partial class AddPasswordHashColumn
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.6")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Govor.Core.Models.ChatGroup", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.PrimitiveCollection<List<Guid>>("Admins")
.IsRequired()
.HasColumnType("uuid[]");
b.PrimitiveCollection<List<string>>("InviteCode")
.IsRequired()
.HasColumnType("text[]");
b.Property<bool>("IsChannel")
.HasColumnType("boolean");
b.Property<bool>("IsPrivate")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("ChatGroups");
});
modelBuilder.Entity("Govor.Core.Models.GroupAdmins", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("GroupId")
.HasColumnType("uuid");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("GroupAdmins");
});
modelBuilder.Entity("Govor.Core.Models.GroupMembership", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("GroupId")
.HasColumnType("uuid");
b.Property<bool>("IsBanned")
.HasColumnType("boolean");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("GroupMemberships");
});
modelBuilder.Entity("Govor.Core.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateOnly>("CreatedOn")
.HasColumnType("date");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("IconId")
.HasColumnType("uuid");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("WasOnline")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("Users");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,45 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Govor.Data.Migrations
{
/// <inheritdoc />
public partial class AddPasswordHashColumn : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "HashPassword",
table: "Users",
newName: "PasswordHash");
migrationBuilder.AlterColumn<DateOnly>(
name: "CreatedOn",
table: "Users",
type: "date",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "PasswordHash",
table: "Users",
newName: "HashPassword");
migrationBuilder.AlterColumn<DateTime>(
name: "CreatedOn",
table: "Users",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateOnly),
oldType: "date");
}
}
}
@@ -95,20 +95,20 @@ namespace Govor.Data.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("uuid"); .HasColumnType("uuid");
b.Property<DateTime>("CreatedOn") b.Property<DateOnly>("CreatedOn")
.HasColumnType("timestamp with time zone"); .HasColumnType("date");
b.Property<string>("Description") b.Property<string>("Description")
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
b.Property<string>("HashPassword")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("IconId") b.Property<Guid>("IconId")
.HasColumnType("uuid"); .HasColumnType("uuid");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Username") b.Property<string>("Username")
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
@@ -0,0 +1,6 @@
namespace Govor.Data.Repositories;
public class GroupRepository
{
}
+6 -5
View File
@@ -2,6 +2,7 @@ using Govor.Core.Infrastructure.Extensions;
using Govor.Core.Infrastructure.Validators; using Govor.Core.Infrastructure.Validators;
using Govor.Core.Models; using Govor.Core.Models;
using Govor.Core.Repositories; using Govor.Core.Repositories;
using Govor.Core.Repositories.Users;
using Govor.Data.Repositories.Exceptions; using Govor.Data.Repositories.Exceptions;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -54,7 +55,7 @@ public class UsersRepository : IUsersRepository
return await _context.Users return await _context.Users
.AsNoTracking() .AsNoTracking()
.FirstOrDefaultAsync(x => x.Name == username) .FirstOrDefaultAsync(x => x.Username == username)
?? throw new NotFoundByKeyException<string>(username, "User with given username does not exist"); ?? throw new NotFoundByKeyException<string>(username, "User with given username does not exist");
} }
@@ -65,7 +66,7 @@ public class UsersRepository : IUsersRepository
return await _context.Users return await _context.Users
.AsNoTracking() .AsNoTracking()
.Where(x => usernames.Contains(x.Name)) .Where(x => usernames.Contains(x.Username))
.ToListOrThrowIfEmpty(new NotFoundByKeyException<IEnumerable<string>>(usernames, "Users with given usernames not found")); .ToListOrThrowIfEmpty(new NotFoundByKeyException<IEnumerable<string>>(usernames, "Users with given usernames not found"));
} }
@@ -109,7 +110,7 @@ public class UsersRepository : IUsersRepository
var rowsAffected = await _context.Users var rowsAffected = await _context.Users
.Where(u => u.Id == user.Id) .Where(u => u.Id == user.Id)
.ExecuteUpdateAsync(u => u .ExecuteUpdateAsync(u => u
.SetProperty(a => a.Name, user.Name) .SetProperty(a => a.Username, user.Username)
.SetProperty(u => u.IconId, user.IconId) .SetProperty(u => u.IconId, user.IconId)
.SetProperty(u => u.Description, user.Description) .SetProperty(u => u.Description, user.Description)
.SetProperty(u => u.CreatedOn, user.CreatedOn) .SetProperty(u => u.CreatedOn, user.CreatedOn)
@@ -168,7 +169,7 @@ public class UsersRepository : IUsersRepository
return _context.Users.AnyAsync(u => return _context.Users.AnyAsync(u =>
u.Id == user.Id && u.Id == user.Id &&
u.Name == user.Name && u.Username == user.Username &&
u.PasswordHash == user.PasswordHash u.PasswordHash == user.PasswordHash
); );
} }
@@ -180,7 +181,7 @@ public class UsersRepository : IUsersRepository
public Task<bool> ExistsUsernameAsync(string username) public Task<bool> ExistsUsernameAsync(string username)
{ {
return _context.Users.AnyAsync(u => u.Name == username); return _context.Users.AnyAsync(u => u.Username == username);
} }
} }