add: autenticacao e autorizacao
This commit is contained in:
57
Controllers/AuthController.cs
Normal file
57
Controllers/AuthController.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
|
||||
namespace CampusWorkshops.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class AuthController : ControllerBase
|
||||
{
|
||||
// Usuários de exemplo (NÃO use plaintext em produção)
|
||||
private static readonly Dictionary<string,(string Password, string[] Roles)> Users = new()
|
||||
{
|
||||
["admin@campus"] = ("admin123", new[] { "Admin", "Instructor" }),
|
||||
["inst@campus"] = ("inst123", new[] { "Instructor" }),
|
||||
["aluno@campus"] = ("aluno123", Array.Empty<string>())
|
||||
};
|
||||
|
||||
private readonly IConfiguration _cfg;
|
||||
public AuthController(IConfiguration cfg) => _cfg = cfg;
|
||||
|
||||
public record LoginRequest(string Username, string Password);
|
||||
public record TokenResponse(string AccessToken, DateTime ExpiresAt);
|
||||
|
||||
[HttpPost("login")]
|
||||
[ProducesResponseType(typeof(TokenResponse), 200)]
|
||||
[ProducesResponseType(401)]
|
||||
public IActionResult Login([FromBody] LoginRequest body)
|
||||
{
|
||||
if (!Users.TryGetValue(body.Username, out var u) || u.Password != body.Password)
|
||||
return Unauthorized();
|
||||
|
||||
var jwt = _cfg.GetSection("Jwt");
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwt["Key"]!));
|
||||
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new(JwtRegisteredClaimNames.Sub, body.Username),
|
||||
new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
|
||||
};
|
||||
claims.AddRange(u.Roles.Select(r => new Claim(ClaimTypes.Role, r)));
|
||||
|
||||
var expires = DateTime.UtcNow.AddHours(2);
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: jwt["Issuer"],
|
||||
audience: jwt["Audience"],
|
||||
claims: claims,
|
||||
expires: expires,
|
||||
signingCredentials: creds
|
||||
);
|
||||
var encoded = new JwtSecurityTokenHandler().WriteToken(token);
|
||||
return Ok(new TokenResponse(encoded, expires));
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using CampusWorkshops.Api.Dtos;
|
||||
using CampusWorkshops.Api.Models;
|
||||
using CampusWorkshops.Api.Repositories;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CampusWorkshops.Api.Controllers;
|
||||
@@ -16,10 +17,11 @@ public class WorkshopsController : ControllerBase
|
||||
|
||||
/// <summary>Lista workshops com filtros opcionais.</summary>
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> GetAll([FromQuery] DateTimeOffset? from, [FromQuery] DateTimeOffset? to, [FromQuery] string? q, CancellationToken ct)
|
||||
{
|
||||
var workshops = await _repo.GetAllAsync(from, to, q, ct);
|
||||
|
||||
|
||||
var response = workshops.Select(w => new WorkshopResponse(
|
||||
w.Id,
|
||||
w.Title,
|
||||
@@ -36,10 +38,11 @@ public class WorkshopsController : ControllerBase
|
||||
|
||||
/// <summary>Obtém um workshop por Id.</summary>
|
||||
[HttpGet("{id:guid}")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> GetById(Guid id, CancellationToken ct)
|
||||
{
|
||||
var workshop = await _repo.GetByIdAsync(id, ct);
|
||||
|
||||
|
||||
if (workshop == null)
|
||||
{
|
||||
return NotFound();
|
||||
@@ -61,6 +64,7 @@ public class WorkshopsController : ControllerBase
|
||||
|
||||
/// <summary>Cria um novo workshop.</summary>
|
||||
[HttpPost]
|
||||
[Authorize(Policy = "CanWriteWorkshops")]
|
||||
public async Task<IActionResult> Create([FromBody] CreateWorkshopRequest body, CancellationToken ct)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
@@ -119,6 +123,8 @@ public class WorkshopsController : ControllerBase
|
||||
|
||||
/// <summary>Atualiza parcialmente um workshop existente.</summary>
|
||||
[HttpPatch("{id:guid}")]
|
||||
[Authorize(Policy = "CanWriteWorkshops")]
|
||||
|
||||
public async Task<IActionResult> Patch(Guid id, [FromBody] PatchWorkshopRequest body, CancellationToken ct)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
@@ -135,10 +141,10 @@ public class WorkshopsController : ControllerBase
|
||||
// Aplicar apenas os campos fornecidos
|
||||
if (body.Title != null)
|
||||
existingWorkshop.Title = body.Title;
|
||||
|
||||
|
||||
if (body.Description != null)
|
||||
existingWorkshop.Description = body.Description;
|
||||
|
||||
|
||||
if (body.StartAt.HasValue)
|
||||
{
|
||||
if (body.StartAt.Value < DateTimeOffset.UtcNow)
|
||||
@@ -147,13 +153,13 @@ public class WorkshopsController : ControllerBase
|
||||
}
|
||||
existingWorkshop.StartAt = body.StartAt.Value;
|
||||
}
|
||||
|
||||
|
||||
if (body.EndAt.HasValue)
|
||||
existingWorkshop.EndAt = body.EndAt.Value;
|
||||
|
||||
|
||||
if (body.Location != null)
|
||||
existingWorkshop.Location = body.Location;
|
||||
|
||||
|
||||
if (body.Capacity.HasValue)
|
||||
{
|
||||
if (body.Capacity.Value < 1)
|
||||
@@ -162,7 +168,7 @@ public class WorkshopsController : ControllerBase
|
||||
}
|
||||
existingWorkshop.Capacity = body.Capacity.Value;
|
||||
}
|
||||
|
||||
|
||||
if (body.IsOnline.HasValue)
|
||||
existingWorkshop.IsOnline = body.IsOnline.Value;
|
||||
|
||||
@@ -199,6 +205,8 @@ public class WorkshopsController : ControllerBase
|
||||
|
||||
/// <summary>Remove um workshop.</summary>
|
||||
[HttpDelete("{id:guid}")]
|
||||
[Authorize(Policy = "CanDeleteWorkshops")]
|
||||
|
||||
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
|
||||
{
|
||||
var workshop = await _repo.GetByIdAsync(id, ct);
|
||||
|
||||
Reference in New Issue
Block a user