diff --git a/Govor.API.Tests/IntegrationTests/EF/Repositories/UsersRepositoryTests.cs b/Govor.API.Tests/IntegrationTests/EF/Repositories/UsersRepositoryTests.cs index fba9a8f..80f997a 100644 --- a/Govor.API.Tests/IntegrationTests/EF/Repositories/UsersRepositoryTests.cs +++ b/Govor.API.Tests/IntegrationTests/EF/Repositories/UsersRepositoryTests.cs @@ -53,7 +53,7 @@ public class UsersRepositoryTests Assert.That(result, Is.Not.Null); 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.Name), Is.EquivalentTo(users.Select(u => u.Name))); + Assert.That(result.Select(u => u.Username), Is.EquivalentTo(users.Select(u => u.Username))); } [Test] @@ -72,7 +72,7 @@ public class UsersRepositoryTests // Assert 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)); } @@ -109,7 +109,7 @@ public class UsersRepositoryTests Assert.That(result, Is.Not.Null); 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(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] @@ -140,10 +140,10 @@ public class UsersRepositoryTests await context.SaveChangesAsync(); // Act - var result = await userRepository.FindByUsernameAsync(user.Name); + var result = await userRepository.FindByUsernameAsync(user.Username); // Assert 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)); } @@ -174,11 +174,11 @@ public class UsersRepositoryTests await context.SaveChangesAsync(); // Act - var result = await userRepository.FindByRangeUsernamesAsync(users.Select(u => u.Name)); + var result = await userRepository.FindByRangeUsernamesAsync(users.Select(u => u.Username)); // Assert 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))); } @@ -254,7 +254,7 @@ public class UsersRepositoryTests // Assert 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)); } @@ -262,7 +262,7 @@ public class UsersRepositoryTests public async Task Given_InvalidUser_When_AddUser_Should_Throw_AdditionUserException() { var user = _fixture.Create(); - user.Name = string.Empty; + user.Username = string.Empty; await using var context = new GovorDbContext(_options); var userRepository = new UsersRepository(context, _userValidator); @@ -361,7 +361,7 @@ public class UsersRepositoryTests await context.SaveChangesAsync(); // Act - var res = await userRepository.ExistsUsernameAsync(user.Name); + var res = await userRepository.ExistsUsernameAsync(user.Username); // Assert Assert.That(res, Is.True); @@ -378,7 +378,7 @@ public class UsersRepositoryTests // Act - var res = await userRepository.ExistsUsernameAsync(user.Name); + var res = await userRepository.ExistsUsernameAsync(user.Username); // Assert Assert.That(res, Is.False); diff --git a/Govor.API.Tests/UnitTests/Infrastructure/Validators/UserValidatorTests.cs b/Govor.API.Tests/UnitTests/Infrastructure/Validators/UserValidatorTests.cs index 8a5eee5..6c1e228 100644 --- a/Govor.API.Tests/UnitTests/Infrastructure/Validators/UserValidatorTests.cs +++ b/Govor.API.Tests/UnitTests/Infrastructure/Validators/UserValidatorTests.cs @@ -46,7 +46,7 @@ public class UserValidatorTests User user = new User() { PasswordHash = _fixture.Create(), - Name = _fixture.Create(), + Username = _fixture.Create(), Id = Guid.Empty, CreatedOn = DateOnly.FromDateTime(DateTime.Now), }; @@ -62,7 +62,7 @@ public class UserValidatorTests User user = new User() { PasswordHash = _fixture.Create(), - Name = _fixture.Create(), + Username = _fixture.Create(), Id = Guid.Empty, CreatedOn = DateOnly.FromDateTime(DateTime.Now), }; @@ -78,7 +78,7 @@ public class UserValidatorTests User user = new User() { PasswordHash = string.Empty, - Name = _fixture.Create(), + Username = _fixture.Create(), Id = Guid.NewGuid(), CreatedOn = DateOnly.FromDateTime(DateTime.Now), }; @@ -94,7 +94,7 @@ public class UserValidatorTests User user = new User() { PasswordHash = string.Empty, - Name = _fixture.Create(), + Username = _fixture.Create(), Id = Guid.NewGuid(), CreatedOn = DateOnly.FromDateTime(DateTime.Now), }; @@ -110,7 +110,7 @@ public class UserValidatorTests User user = new User() { PasswordHash = _fixture.Create(), - Name = String.Empty, + Username = String.Empty, Id = Guid.NewGuid(), CreatedOn = DateOnly.FromDateTime(DateTime.Now), }; @@ -126,7 +126,7 @@ public class UserValidatorTests User user = new User() { PasswordHash = _fixture.Create(), - Name = String.Empty, + Username = String.Empty, Id = Guid.NewGuid(), CreatedOn = DateOnly.FromDateTime(DateTime.Now), }; @@ -142,7 +142,7 @@ public class UserValidatorTests User user = new User() { PasswordHash = _fixture.Create(), - Name = _fixture.Create(), + Username = _fixture.Create(), Id = Guid.NewGuid(), CreatedOn = DateOnly.FromDateTime(DateTime.Now), }; @@ -158,7 +158,7 @@ public class UserValidatorTests User user = new User() { PasswordHash = _fixture.Create(), - Name = _fixture.Create(), + Username = _fixture.Create(), Id = Guid.NewGuid(), CreatedOn = DateOnly.FromDateTime(DateTime.Now), }; diff --git a/Govor.API.Tests/UnitTests/Services/AuthServiceTests.cs b/Govor.API.Tests/UnitTests/Services/AuthServiceTests.cs new file mode 100644 index 0000000..5328f2c --- /dev/null +++ b/Govor.API.Tests/UnitTests/Services/AuthServiceTests.cs @@ -0,0 +1,11 @@ +namespace Govor.API.Tests.UnitTests.Services; + +[TestFixture] +public class AuthServiceTests +{ + + public void SetUp() + { + + } +} \ No newline at end of file diff --git a/Govor.API/Controllers/AuthController.cs b/Govor.API/Controllers/AuthController.cs index a72284e..ca7928b 100644 --- a/Govor.API/Controllers/AuthController.cs +++ b/Govor.API/Controllers/AuthController.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc; namespace Govor.API.Controllers; [ApiController] -[Route("[controller]")] +[Route("api/[controller]")] public class AuthController : Controller { private IAccountService _accountService; @@ -18,7 +18,7 @@ public class AuthController : Controller _logger = logger; } - [HttpPost("register")] + [HttpPost("register")]// api/auth/register [RequireHttps] public async Task Register([FromBody] UserDto userDto) { @@ -29,8 +29,9 @@ public class AuthController : Controller return BadRequest(ModelState); } - await _accountService.RegistrationAsync(userDto.Name, userDto.Password); - return Ok(new { Message = "User registered successfully" }); + var token = await _accountService.RegistrationAsync(userDto.Name, userDto.Password); + _logger.LogInformation($"Register request for {userDto.Name}"); + return Ok(new { token }); } catch (UserAlreadyExistException ex) { @@ -44,7 +45,7 @@ public class AuthController : Controller } } - [HttpPost("login")] + [HttpPost("login")]// api/auth/login [RequireHttps] public async Task Login([FromBody] UserDto userDto) { @@ -55,8 +56,9 @@ public class AuthController : Controller return BadRequest(ModelState); } - await _accountService.LoginAsync(userDto.Name, userDto.Password); - return Ok(new { Message = "User login successfully" }); + var token = await _accountService.LoginAsync(userDto.Name, userDto.Password); + _logger.LogInformation($"Login request for {userDto.Name}"); + return Ok(new { token }); } catch (UserNotRegisteredException ex) { diff --git a/Govor.API/Controllers/InviteController.cs b/Govor.API/Controllers/InviteController.cs index f56d75e..ff4d808 100644 --- a/Govor.API/Controllers/InviteController.cs +++ b/Govor.API/Controllers/InviteController.cs @@ -1,4 +1,5 @@ using Govor.Core.Services; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Govor.API.Controllers; @@ -13,8 +14,9 @@ public class InviteController : ControllerBase { _groupService = groupService; } - + [HttpGet("{code}")] + [Authorize] public IActionResult JoinGroup(string code) { var group = _groupService.GetGroupByInvite(code); diff --git a/Govor.API/Govor.API.csproj b/Govor.API/Govor.API.csproj index 96dd6da..94bf430 100644 --- a/Govor.API/Govor.API.csproj +++ b/Govor.API/Govor.API.csproj @@ -8,6 +8,7 @@ + @@ -15,6 +16,8 @@ all + + diff --git a/Govor.API/Program.cs b/Govor.API/Program.cs index b4ea7f7..7b2c958 100644 --- a/Govor.API/Program.cs +++ b/Govor.API/Program.cs @@ -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.Repositories; using Microsoft.EntityFrameworkCore; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; var builder = WebApplication.CreateBuilder(args); +var configuration = builder.Configuration; +var services = builder.Services; + builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); +builder.Services.Configure(configuration.GetSection(nameof(JwtOption))); // Add services - builder.Services.AddSignalR(); +builder.Services.AddAuthorization(); +builder.Services.AddAuthentication("Bearer") + .AddJwtBearer(); + + builder.Services.AddControllers(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped, UserValidator>(); + builder.Services.AddDbContext( options => { @@ -19,22 +44,30 @@ builder.Services.AddDbContext( ); builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddOpenApi(); - -builder.Services.AddAuthorization(); +builder.Services.AddSwaggerGen(); +//builder.Services.AddOpenApi(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { - app.MapOpenApi(); + //app.MapOpenApi(); + + app.UseSwagger(); + app.UseSwaggerUI(); } app.UseHttpsRedirection(); +app.UseRouting(); + +app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); +app.Map("/hello", [Authorize]() => "Hello World!"); +app.Map("/", () => "Not for browser"); + app.Run(); \ No newline at end of file diff --git a/Govor.API/Services/Authentication/AuthService.cs b/Govor.API/Services/Authentication/AuthService.cs index ea7bdf6..1b3b8f4 100644 --- a/Govor.API/Services/Authentication/AuthService.cs +++ b/Govor.API/Services/Authentication/AuthService.cs @@ -1,5 +1,8 @@ +using Govor.Core; using Govor.Core.Infrastructure.Extensions; +using Govor.Core.Models; using Govor.Core.Repositories; +using Govor.Core.Repositories.Users; using Govor.Core.Services; namespace Govor.API.Services.Authentication; @@ -7,27 +10,56 @@ namespace Govor.API.Services.Authentication; public class AuthService : IAccountService { private readonly IPasswordHasher _passwordHasher; + private readonly IJwtService _jwtService; private readonly IUsersRepository _usersRepository; - - public AuthService(IUsersRepository usersRepository, IPasswordHasher passwordHasher) + + public AuthService(IUsersRepository usersRepository, IJwtService jwtService, IPasswordHasher passwordHasher) { _usersRepository = usersRepository; + _jwtService = jwtService; _passwordHasher = passwordHasher; } - public Task RegistrationAsync(string name, string password) + + public async Task 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 LoginAsync(string name, string password) { if (await _usersRepository.ExistsUsernameAsync(name) == false) 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!") { } \ No newline at end of file +public class UserAlreadyExistException(string username) : GovorCoreException($"{username} is already exists!") { } + +public class UserNotRegisteredException(string username) : GovorCoreException($"{username} is not registered!") { } \ No newline at end of file diff --git a/Govor.API/Services/Authentication/JwtOption.cs b/Govor.API/Services/Authentication/JwtOption.cs new file mode 100644 index 0000000..6a5c91f --- /dev/null +++ b/Govor.API/Services/Authentication/JwtOption.cs @@ -0,0 +1,7 @@ +namespace Govor.API.Services.Authentication; + +public class JwtOption +{ + public string SecretKeу {get; set;} + public int Hours { get; set; } +} \ No newline at end of file diff --git a/Govor.API/Services/Authentication/JwtService.cs b/Govor.API/Services/Authentication/JwtService.cs new file mode 100644 index 0000000..fcdc578 --- /dev/null +++ b/Govor.API/Services/Authentication/JwtService.cs @@ -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 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); + } +} \ No newline at end of file diff --git a/Govor.API/appsettings.json b/Govor.API/appsettings.json index 8e04af3..be2ac70 100644 --- a/Govor.API/appsettings.json +++ b/Govor.API/appsettings.json @@ -8,5 +8,9 @@ "ConnectionStrings": { "GovorDbContext": "Host=localhost;Port=5432;Database=DbGovor;Username=postgres;Password=stalcker;" }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "JwtOption": { + "SecretKeу": "MY VERY SECRET KEY asdasdpafjhasofafpajsfj", + "Hours": "24" + } } diff --git a/Govor.Console/Program.cs b/Govor.Console/Program.cs index e5dff12..df99ecf 100644 --- a/Govor.Console/Program.cs +++ b/Govor.Console/Program.cs @@ -1,3 +1,14 @@ // See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); \ No newline at end of file +Console.WriteLine("Hello, World!"); + +/*==================================== + *Горелые Петушки Чат + *==================================== + * + * [16:30] Егор >> "Привет, Питухам!" + * [16:31] Anon >> "Иди нахуй" + * + * + * >> "Привет, Егор!" +*/ \ No newline at end of file diff --git a/Govor.Core/Infrastructure/Validators/UserValidator.cs b/Govor.Core/Infrastructure/Validators/UserValidator.cs index 9772176..e91f577 100644 --- a/Govor.Core/Infrastructure/Validators/UserValidator.cs +++ b/Govor.Core/Infrastructure/Validators/UserValidator.cs @@ -16,10 +16,10 @@ public class UserValidator : IObjectValidator throw new ArgumentNullException(nameof(user)); if(user.Id == Guid.Empty) throw new ArgumentException("User ID cannot be empty", nameof(user.Id)); - if(user.Name is null - || user.Name.Length < MIN_LENGHT_OF_NAME - || user.Name.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)); + if(user.Username is null + || user.Username.Length < MIN_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.Username)); if(user.PasswordHash is null || user.PasswordHash == string.Empty) throw new ArgumentException("Password cannot be empty", nameof(user.PasswordHash)); if(user.CreatedOn == DateOnly.MinValue) diff --git a/Govor.Core/Models/User.cs b/Govor.Core/Models/User.cs index 3f00a4d..762a4a7 100644 --- a/Govor.Core/Models/User.cs +++ b/Govor.Core/Models/User.cs @@ -5,7 +5,7 @@ namespace Govor.Core.Models; public class User { public Guid Id {get; set;} - public string Name {get; set;} + public string Username {get; set;} public string Description {get; set;} public string PasswordHash {get; set;} public Guid IconId {get; set;} diff --git a/Govor.Core/Repositories/Groups/IGroupsReader.cs b/Govor.Core/Repositories/Groups/IGroupsReader.cs new file mode 100644 index 0000000..41f1924 --- /dev/null +++ b/Govor.Core/Repositories/Groups/IGroupsReader.cs @@ -0,0 +1,6 @@ +namespace Govor.Core.Repositories.Groups; + +public class IGroupsReader +{ + +} \ No newline at end of file diff --git a/Govor.Core/Repositories/IUsersExist.cs b/Govor.Core/Repositories/Users/IUsersExist.cs similarity index 83% rename from Govor.Core/Repositories/IUsersExist.cs rename to Govor.Core/Repositories/Users/IUsersExist.cs index bf004aa..19f2038 100644 --- a/Govor.Core/Repositories/IUsersExist.cs +++ b/Govor.Core/Repositories/Users/IUsersExist.cs @@ -1,6 +1,6 @@ using Govor.Core.Models; -namespace Govor.Core.Repositories; +namespace Govor.Core.Repositories.Users; public interface IUsersExist { diff --git a/Govor.Core/Repositories/IUsersReader.cs b/Govor.Core/Repositories/Users/IUsersReader.cs similarity index 91% rename from Govor.Core/Repositories/IUsersReader.cs rename to Govor.Core/Repositories/Users/IUsersReader.cs index 74ec0ff..472772a 100644 --- a/Govor.Core/Repositories/IUsersReader.cs +++ b/Govor.Core/Repositories/Users/IUsersReader.cs @@ -1,6 +1,6 @@ using Govor.Core.Models; -namespace Govor.Core.Repositories; +namespace Govor.Core.Repositories.Users; public interface IUsersReader { diff --git a/Govor.Core/Repositories/IUsersRepository.cs b/Govor.Core/Repositories/Users/IUsersRepository.cs similarity index 66% rename from Govor.Core/Repositories/IUsersRepository.cs rename to Govor.Core/Repositories/Users/IUsersRepository.cs index 15f0d41..8609807 100644 --- a/Govor.Core/Repositories/IUsersRepository.cs +++ b/Govor.Core/Repositories/Users/IUsersRepository.cs @@ -1,4 +1,4 @@ -namespace Govor.Core.Repositories; +namespace Govor.Core.Repositories.Users; public interface IUsersRepository : IUsersReader, IUsersWriter, IUsersExist { diff --git a/Govor.Core/Repositories/IUsersWriter.cs b/Govor.Core/Repositories/Users/IUsersWriter.cs similarity index 83% rename from Govor.Core/Repositories/IUsersWriter.cs rename to Govor.Core/Repositories/Users/IUsersWriter.cs index 9637d39..edfcbe9 100644 --- a/Govor.Core/Repositories/IUsersWriter.cs +++ b/Govor.Core/Repositories/Users/IUsersWriter.cs @@ -1,6 +1,6 @@ using Govor.Core.Models; -namespace Govor.Core.Repositories; +namespace Govor.Core.Repositories.Users; public interface IUsersWriter { diff --git a/Govor.Core/Services/IAuthService.cs b/Govor.Core/Services/IAuthService.cs index 25d3697..98c3217 100644 --- a/Govor.Core/Services/IAuthService.cs +++ b/Govor.Core/Services/IAuthService.cs @@ -1,7 +1,9 @@ +using Govor.Core.Models; + namespace Govor.Core.Services; public interface IAccountService { - public Task RegistrationAsync(string name, string password); + public Task RegistrationAsync(string name, string password); public Task LoginAsync(string name, string password); } \ No newline at end of file diff --git a/Govor.Core/Services/IJwtService.cs b/Govor.Core/Services/IJwtService.cs new file mode 100644 index 0000000..65bf53e --- /dev/null +++ b/Govor.Core/Services/IJwtService.cs @@ -0,0 +1,8 @@ +using Govor.Core.Models; + +namespace Govor.Core.Services; + +public interface IJwtService +{ + string GenerateJwtToken(User user); +} \ No newline at end of file diff --git a/Govor.Data/Migrations/20250618082839_AddPasswordHashColumn.Designer.cs b/Govor.Data/Migrations/20250618082839_AddPasswordHashColumn.Designer.cs new file mode 100644 index 0000000..a20edbd --- /dev/null +++ b/Govor.Data/Migrations/20250618082839_AddPasswordHashColumn.Designer.cs @@ -0,0 +1,129 @@ +// +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 + { + /// + 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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.PrimitiveCollection>("Admins") + .IsRequired() + .HasColumnType("uuid[]"); + + b.PrimitiveCollection>("InviteCode") + .IsRequired() + .HasColumnType("text[]"); + + b.Property("IsChannel") + .HasColumnType("boolean"); + + b.Property("IsPrivate") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("ChatGroups"); + }); + + modelBuilder.Entity("Govor.Core.Models.GroupAdmins", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("GroupAdmins"); + }); + + modelBuilder.Entity("Govor.Core.Models.GroupMembership", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("IsBanned") + .HasColumnType("boolean"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("GroupMemberships"); + }); + + modelBuilder.Entity("Govor.Core.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedOn") + .HasColumnType("date"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("IconId") + .HasColumnType("uuid"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.Property("WasOnline") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Govor.Data/Migrations/20250618082839_AddPasswordHashColumn.cs b/Govor.Data/Migrations/20250618082839_AddPasswordHashColumn.cs new file mode 100644 index 0000000..82beab1 --- /dev/null +++ b/Govor.Data/Migrations/20250618082839_AddPasswordHashColumn.cs @@ -0,0 +1,45 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Govor.Data.Migrations +{ + /// + public partial class AddPasswordHashColumn : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "HashPassword", + table: "Users", + newName: "PasswordHash"); + + migrationBuilder.AlterColumn( + name: "CreatedOn", + table: "Users", + type: "date", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "PasswordHash", + table: "Users", + newName: "HashPassword"); + + migrationBuilder.AlterColumn( + name: "CreatedOn", + table: "Users", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateOnly), + oldType: "date"); + } + } +} diff --git a/Govor.Data/Migrations/GovorDbContextModelSnapshot.cs b/Govor.Data/Migrations/GovorDbContextModelSnapshot.cs index ddb88b9..a49aac5 100644 --- a/Govor.Data/Migrations/GovorDbContextModelSnapshot.cs +++ b/Govor.Data/Migrations/GovorDbContextModelSnapshot.cs @@ -95,20 +95,20 @@ namespace Govor.Data.Migrations .ValueGeneratedOnAdd() .HasColumnType("uuid"); - b.Property("CreatedOn") - .HasColumnType("timestamp with time zone"); + b.Property("CreatedOn") + .HasColumnType("date"); b.Property("Description") .IsRequired() .HasColumnType("text"); - b.Property("HashPassword") - .IsRequired() - .HasColumnType("text"); - b.Property("IconId") .HasColumnType("uuid"); + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + b.Property("Username") .IsRequired() .HasColumnType("text"); diff --git a/Govor.Data/Repositories/GroupRepository.cs b/Govor.Data/Repositories/GroupRepository.cs new file mode 100644 index 0000000..18b2dbb --- /dev/null +++ b/Govor.Data/Repositories/GroupRepository.cs @@ -0,0 +1,6 @@ +namespace Govor.Data.Repositories; + +public class GroupRepository +{ + +} \ No newline at end of file diff --git a/Govor.Data/Repositories/UsersRepository.cs b/Govor.Data/Repositories/UsersRepository.cs index 18556f7..97d5b5a 100644 --- a/Govor.Data/Repositories/UsersRepository.cs +++ b/Govor.Data/Repositories/UsersRepository.cs @@ -2,6 +2,7 @@ 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.Data.Repositories.Exceptions; using Microsoft.EntityFrameworkCore; @@ -54,7 +55,7 @@ public class UsersRepository : IUsersRepository return await _context.Users .AsNoTracking() - .FirstOrDefaultAsync(x => x.Name == username) + .FirstOrDefaultAsync(x => x.Username == username) ?? throw new NotFoundByKeyException(username, "User with given username does not exist"); } @@ -65,7 +66,7 @@ public class UsersRepository : IUsersRepository return await _context.Users .AsNoTracking() - .Where(x => usernames.Contains(x.Name)) + .Where(x => usernames.Contains(x.Username)) .ToListOrThrowIfEmpty(new NotFoundByKeyException>(usernames, "Users with given usernames not found")); } @@ -109,7 +110,7 @@ public class UsersRepository : IUsersRepository var rowsAffected = await _context.Users .Where(u => u.Id == user.Id) .ExecuteUpdateAsync(u => u - .SetProperty(a => a.Name, user.Name) + .SetProperty(a => a.Username, user.Username) .SetProperty(u => u.IconId, user.IconId) .SetProperty(u => u.Description, user.Description) .SetProperty(u => u.CreatedOn, user.CreatedOn) @@ -168,7 +169,7 @@ public class UsersRepository : IUsersRepository return _context.Users.AnyAsync(u => u.Id == user.Id && - u.Name == user.Name && + u.Username == user.Username && u.PasswordHash == user.PasswordHash ); } @@ -180,7 +181,7 @@ public class UsersRepository : IUsersRepository public Task ExistsUsernameAsync(string username) { - return _context.Users.AnyAsync(u => u.Name == username); + return _context.Users.AnyAsync(u => u.Username == username); } }