Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .vs/ProjectEvaluation/todo.metadata.v7.bin
Binary file not shown.
Binary file removed .vs/ProjectEvaluation/todo.projects.v7.bin
Binary file not shown.
Binary file modified .vs/Todo/DesignTimeBuild/.dtbcache.v2
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion .vs/Todo/config/applicationhost.config
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@
</site>
<site name="Todo" id="2">
<application path="/" applicationPool="Todo AppPool">
<virtualDirectory path="/" physicalPath="C:\Users\rfcon\Source\Repos\Romulo-Queiroz\Task-Manager-Backend" />
<virtualDirectory path="/" physicalPath="C:\Users\Romulo\Documents\Projetos\Task-Manager-Csharp" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:43713:localhost" />
Expand Down
Binary file modified .vs/Todo/v17/.futdcache.v2
Binary file not shown.
Binary file modified .vs/Todo/v17/.suo
Binary file not shown.
69 changes: 42 additions & 27 deletions Controllers/LoginController.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Todo.Data;
using Todo.Domain;
using Todo.Models;
using Todo.Interfaces;
using Todo.Services;

namespace Todo.Controllers
Expand All @@ -11,56 +10,72 @@ namespace Todo.Controllers
[Route("api/[controller]")]
public class LoginController : ControllerBase
{

private readonly LoginService _loginService;
private readonly ILoginService _loginService;

public LoginController(LoginService loginService)
public LoginController(ILoginService loginService)
{
_loginService = loginService;
}

[Authorize]
[Authorize(Roles = "Admin")]
[HttpGet("ListUsers")]
public IActionResult ListUsers()
public async Task<IActionResult> ListUsers(CancellationToken ct)
{
try
{
var listUsers = _loginService.ListUsers();
return Ok(listUsers);
var users = await _loginService.ListUsersAsync(ct);
return Ok(users);
}
catch (Exception ex)
catch (Exception)
{
return BadRequest("Não foi possível listar os usuários.");
return BadRequest(new { message = "Não foi possível listar os usuários." });
}
}

[HttpPost("CreateAccount")]
public IActionResult CreateAccount([FromBody] UserEntity model)
[AllowAnonymous]
public async Task<IActionResult> CreateAccount([FromBody] UserEntity model, CancellationToken ct)
{
if (model == null)
return BadRequest(new { message = "Dados inválidos." });

try
{
var createUser = _loginService.CreateAccount(model);
return Ok("Conta de usuário criada com sucesso.");
var created = await _loginService.CreateAccountAsync(model, ct);

if (!created)
return BadRequest(new { message = "Não foi possível criar a conta de usuário." });

return Ok(new { message = "Conta de usuário criada com sucesso." });
}
catch (Exception ex)
catch (InvalidOperationException ex)
{
return BadRequest("Não foi possível criar a conta de usuário.");
// Tratamento de duplicidade de username
return Conflict(new { message = ex.Message });
}
catch (Exception)
{
return BadRequest(new { message = "Erro inesperado ao criar conta." });
}
}


[HttpPost("Authenticate")]
public IActionResult Authenticate([FromBody] UserEntity model)
[AllowAnonymous]
public async Task<IActionResult> Authenticate([FromBody] UserEntity model, CancellationToken ct)
{
try
{
var autenticateService = _loginService.Authenticate(model);
return Ok(autenticateService);
}
catch (Exception ex)
if (model is null || string.IsNullOrWhiteSpace(model.Username) || string.IsNullOrWhiteSpace(model.Password))
return BadRequest(new { message = "Informe usuário e senha." });

var result = await _loginService.AuthenticateAsync(model.Username, model.Password, ct);

if (!result.Succeeded)
return Unauthorized(new { message = "Usuário ou senha incorretos." });

return Ok(new
{
return BadRequest(new { message = "Usuário ou senha incorretos." });
}
}
token = result.Token,
user = result.User
});
}
}
}
23 changes: 23 additions & 0 deletions Dal/UserDal.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Microsoft.EntityFrameworkCore;
using Todo.Data;
using Todo.Domain;

Expand All @@ -17,5 +18,27 @@ public void AddUser(UserEntity user)
_context.Users.Add(user);
_context.SaveChanges();
}

/// <summary>
/// Adiciona um novo usuário ao banco de dados.
/// </summary>
/// <param name="model">Entidade do usuário a ser criada.</param>
/// <param name="ct">Token de cancelamento (opcional).</param>
public async Task AddUserAsync(UserEntity model, CancellationToken ct = default)
{
if (model is null)
throw new ArgumentNullException(nameof(model), "O modelo de usuário não pode ser nulo.");

// Evita duplicidade de Username
var exists = await _context.Users
.AsNoTracking()
.AnyAsync(u => u.Username == model.Username, ct);

if (exists)
throw new InvalidOperationException($"O nome de usuário '{model.Username}' já está em uso.");

await _context.Users.AddAsync(model, ct);
await _context.SaveChangesAsync(ct);
}
}
}
9 changes: 9 additions & 0 deletions Interfaces/IJwtTokenService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Todo.Domain;

namespace Todo.Interfaces
{
public interface IJwtTokenService
{
string Generate(UserEntity user);
}
}
13 changes: 13 additions & 0 deletions Interfaces/ILoginService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Todo.Domain;
using Todo.Services;

namespace Todo.Interfaces
{

public interface ILoginService
{
Task<AuthResult> AuthenticateAsync(string username, string password, CancellationToken ct = default);
Task<bool> CreateAccountAsync(UserEntity model, CancellationToken ct = default);
Task<List<UserEntity>> ListUsersAsync(CancellationToken ct = default);
}
}
14 changes: 8 additions & 6 deletions Program.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
using Todo.Data;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.OpenApi.Models;
using Todo.Services;
using System.Text;
using Todo;
using Todo.Dal;
using Todo.Data;
using Todo.Interfaces;
using Todo.Services;

var builder = WebApplication.CreateBuilder(args);

Expand All @@ -33,11 +34,12 @@

builder.Services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer("Server=localhost\\MSSQLSERVER03;Database=TaskManagerDesenv;Trusted_Connection=True;Encrypt=true;TrustServerCertificate=True;");
options.UseSqlServer("Server=localhost\\SQLEXPRESS01;Database=TaskManagerPro;Trusted_Connection=True;TrustServerCertificate=True;");
});

builder.Services.AddScoped<TaskManagerServices>();
builder.Services.AddScoped<LoginService>();
builder.Services.AddScoped<ILoginService, LoginService>();
builder.Services.AddScoped<Todo.Interfaces.IJwtTokenService, JwtTokenService>();
builder.Services.AddScoped<CategorieTaskService>();
builder.Services.AddScoped<Todo.Dal.TaskDal>();
builder.Services.AddScoped<Todo.Dal.CategorieTaskDal>();
Expand Down
52 changes: 52 additions & 0 deletions Services/JwtTokenService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Todo.Domain;
using Todo.Interfaces;

namespace Todo.Services
{
public sealed class JwtTokenService : IJwtTokenService
{
private readonly IConfiguration _config;

public JwtTokenService(IConfiguration config)
{
_config = config;
}

public string Generate(UserEntity user)
{
// Obtém a chave secreta do appsettings ou de Settings.Secret
var secret = _config["Jwt:Secret"] ?? Settings.Secret;
var issuer = _config["Jwt:Issuer"];
var audience = _config["Jwt:Audience"];

var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secret));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Username),
new Claim(ClaimTypes.Role, user.IsAdmin ? "Admin" : "User")
};

var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddMinutes(30), // tempo padrão de expiração
SigningCredentials = creds,
Issuer = issuer,
Audience = audience
};

var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);

return tokenHandler.WriteToken(token);
}
}
}
Loading
Loading