mirror of
https://github.com/Govor-team/Govor.git
synced 2026-07-21 11:44:56 +00:00
AuthController work
This commit is contained in:
@@ -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()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -13,8 +14,9 @@ public class InviteController : ControllerBase
|
|||||||
{
|
{
|
||||||
_groupService = groupService;
|
_groupService = groupService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[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);
|
||||||
|
|||||||
@@ -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
@@ -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,27 +10,56 @@ 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)
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -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
|
||||||
{
|
{
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user