Changeset b373fea


Ignore:
Timestamp:
06/23/26 15:20:39 (12 days ago)
Author:
kikisrbinoska <srbinoskakristina07@…>
Branches:
main
Children:
0b502c2
Parents:
d300631
Message:

Fixes for authentication and auhtorization\

Files:
1 deleted
26 edited

Legend:

Unmodified
Added
Removed
  • ChapterX.API/Controllers/AdminsController.cs

    rd300631 rb373fea  
    4040
    4141        [HttpPost]
    42         [Authorize]
     42        [Authorize(Roles = "Admin")]
    4343        public async Task<ActionResult> Add([FromBody] AddRequest request)
    4444        {
     
    4949
    5050        [HttpPut("{id:int}")]
    51         [Authorize]
     51        [Authorize(Roles = "Admin")]
    5252        public async Task<ActionResult> Update(int id, [FromBody] UpdateRequest request)
    5353        {
     
    6363
    6464        [HttpDelete("{id:int}")]
    65         [Authorize]
     65        [Authorize(Roles = "Admin")]
    6666        public async Task<ActionResult> Delete(int id)
    6767        {
  • ChapterX.API/Controllers/ChaptersController.cs

    rd300631 rb373fea  
    55using Microsoft.AspNetCore.Mvc;
    66using Microsoft.Extensions.Logging;
     7using System.IdentityModel.Tokens.Jwt;
     8using System.Security.Claims;
    79
    810namespace ChapterX.API.Controllers
     
    4042
    4143        [HttpPost]
     44        [Authorize]
    4245        public async Task<ActionResult> Add([FromBody] AddRequest request)
    4346        {
     
    5760            }
    5861
    59             var response = await _mediator.Send(request);
     62            var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!);
     63            var response = await _mediator.Send(request with { CallerId = callerId });
    6064            return Ok(response);
    6165        }
     
    6670        {
    6771            _logger.LogInformation("Deleting chapter with ID: {ChapterId}", id);
    68             var response = await _mediator.Send(new DeleteRequest(id));
     72            var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!);
     73            var response = await _mediator.Send(new DeleteRequest(id, callerId));
    6974            return Ok(response);
    7075        }
  • ChapterX.API/Controllers/CommentsController.cs

    rd300631 rb373fea  
    66using Microsoft.AspNetCore.Mvc;
    77using Microsoft.Extensions.Logging;
     8using System.IdentityModel.Tokens.Jwt;
     9using System.Security.Claims;
    810
    911namespace ChapterX.API.Controllers
     
    6365        public async Task<ActionResult> Add([FromBody] AddRequest request)
    6466        {
     67            var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!);
    6568            _logger.LogInformation("Adding a new comment");
    66             var response = await _mediator.Send(request);
     69            var response = await _mediator.Send(request with { UserId = callerId });
    6770            return Ok(response);
    6871        }
     
    7881            }
    7982
    80             var response = await _mediator.Send(request);
     83            var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!);
     84            var response = await _mediator.Send(request with { CallerId = callerId });
    8185            return Ok(response);
    8286        }
     
    8791        {
    8892            _logger.LogInformation("Deleting comment with ID: {CommentId}", id);
    89             var response = await _mediator.Send(new DeleteRequest(id));
     93            var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!);
     94            var response = await _mediator.Send(new DeleteRequest(id, callerId));
    9095            return Ok(response);
    9196        }
  • ChapterX.API/Controllers/StoriesController.cs

    rd300631 rb373fea  
    55using Microsoft.AspNetCore.Mvc;
    66using Microsoft.Extensions.Logging;
     7using System.IdentityModel.Tokens.Jwt;
     8using System.Security.Claims;
    79
    810namespace ChapterX.API.Controllers
     
    4648        public async Task<ActionResult> Add([FromBody] AddRequest request)
    4749        {
    48             _logger.LogInformation("Adding a new story for UserId: {UserId}", request.UserId);
    49             var response = await _mediator.Send(request);
     50            var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!);
     51            _logger.LogInformation("Adding a new story for UserId: {UserId}", callerId);
     52            var response = await _mediator.Send(request with { UserId = callerId });
    5053            return Ok(response);
    5154        }
     
    6265            }
    6366
    64             var response = await _mediator.Send(request);
     67            var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!);
     68            var response = await _mediator.Send(request with { CallerId = callerId });
    6569            return Ok(response);
    6670        }
     
    7276        {
    7377            _logger.LogInformation("Deleting story with ID: {StoryId}", id);
    74             var response = await _mediator.Send(new DeleteRequest(id));
     78            var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!);
     79            var response = await _mediator.Send(new DeleteRequest(id, callerId));
    7580            return Ok(response);
    7681        }
  • ChapterX.API/Controllers/UsersController.cs

    rd300631 rb373fea  
    55using Microsoft.AspNetCore.Mvc;
    66using Microsoft.Extensions.Logging;
     7using System.IdentityModel.Tokens.Jwt;
     8using System.Security.Claims;
    79
    810namespace ChapterX.API.Controllers
     
    6769            }
    6870
     71            var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!);
     72            var isAdmin = User.IsInRole("Admin");
     73            if (callerId != id && !isAdmin)
     74                return Forbid();
     75
    6976            var response = await _mediator.Send(request);
    7077            return Ok(response);
     
    7683        {
    7784            _logger.LogInformation("Deleting user with ID: {UserId}", id);
     85            var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!);
     86            var isAdmin = User.IsInRole("Admin");
     87            if (callerId != id && !isAdmin)
     88                return Forbid();
     89
    7890            var response = await _mediator.Send(new DeleteRequest(id));
    7991            return Ok(response);
  • ChapterX.API/Program.cs

    rd300631 rb373fea  
    77
    88var builder = WebApplication.CreateBuilder(args);
     9
     10var jwtKey = builder.Configuration["Jwt:Key"];
     11if (string.IsNullOrWhiteSpace(jwtKey) || jwtKey.StartsWith("change-this"))
     12    throw new InvalidOperationException("Jwt:Key is not configured. Set it via environment variable DOTNET_Jwt__Key before starting the application.");
    913
    1014builder.Services.AddCors(options =>
     
    8589    ctx.Response.ContentType = "application/json";
    8690
     91    var logger = ctx.RequestServices.GetRequiredService<ILogger<Program>>();
     92
    8793    string message;
    8894    int status;
     
    107113            message = "A user with this email or username already exists.";
    108114        else
    109             message = "Database error: " + inner;
     115        {
     116            logger.LogError(dbEx, "Unhandled database error");
     117            message = "A database error occurred. Please try again.";
     118        }
    110119    }
    111120    else
    112121    {
     122        logger.LogError(ex, "Unhandled exception");
    113123        status = 500;
    114         message = ex?.Message ?? "An error occurred.";
     124        message = "An unexpected error occurred.";
    115125    }
    116126
  • ChapterX.Application/Auth/LoginRequest.cs

    rd300631 rb373fea  
    11using MediatR;
     2using System.ComponentModel.DataAnnotations;
    23
    34namespace ChapterX.Application.Auth
    45{
    5     public record LoginRequest(string Email, string Password) : IRequest<LoginResponse>;
     6    public record LoginRequest(
     7        [Required][EmailAddress][MaxLength(200)] string Email,
     8        [Required][MaxLength(128)] string Password
     9    ) : IRequest<LoginResponse>;
    610}
  • ChapterX.Application/Auth/RegisterRequest.cs

    rd300631 rb373fea  
    11using MediatR;
     2using System.ComponentModel.DataAnnotations;
    23
    34namespace ChapterX.Application.Auth
    45{
    5     public record RegisterRequest(string Username, string Email, string Name, string Surname, string Password) : IRequest<RegisterResponse>;
     6    public record RegisterRequest(
     7        [Required][MaxLength(50)] string Username,
     8        [Required][EmailAddress][MaxLength(200)] string Email,
     9        [Required][MaxLength(100)] string Name,
     10        [Required][MaxLength(100)] string Surname,
     11        [Required][MinLength(8)][MaxLength(128)] string Password
     12    ) : IRequest<RegisterResponse>;
    613}
  • ChapterX.Application/Chapter/Commands/AddRequest.cs

    rd300631 rb373fea  
    11using MediatR;
     2using System.ComponentModel.DataAnnotations;
    23
    34namespace ChapterX.Application.Chapter.Commands
    45{
    56    public record AddRequest(
    6         int Number,
    7         string Name,
    8         string Title,
    9         string Content,
     7        [Range(1, int.MaxValue)] int Number,
     8        [Required][MaxLength(200)] string Name,
     9        [Required][MaxLength(300)] string Title,
     10        [Required] string Content,
    1011        int StoryId
    1112    ) : IRequest<AddResponse>;
  • ChapterX.Application/Chapter/Commands/DeleteHandler.cs

    rd300631 rb373fea  
    1818        public async Task<DeleteResponse> Handle(DeleteRequest request, CancellationToken cancellationToken)
    1919        {
    20             var chapter = await _chapterRepository.GetByIdAsync(request.Id, cancellationToken);
     20            var chapter = await _chapterRepository.GetByIdWithStoryAsync(request.Id, cancellationToken);
    2121            if (chapter is null)
    2222                return new DeleteResponse(false);
     23
     24            if (chapter.Story.UserId != request.CallerId)
     25                throw new UnauthorizedAccessException("You do not own this chapter.");
    2326
    2427            await _chapterRepository.DeleteAsync(chapter, cancellationToken);
  • ChapterX.Application/Chapter/Commands/DeleteRequest.cs

    rd300631 rb373fea  
    33namespace ChapterX.Application.Chapter.Commands
    44{
    5     public record DeleteRequest(int Id) : IRequest<DeleteResponse>;
     5    public record DeleteRequest(int Id, int CallerId) : IRequest<DeleteResponse>;
    66}
  • ChapterX.Application/Chapter/Commands/UpdateHandler.cs

    rd300631 rb373fea  
    1818        public async Task<UpdateResponse> Handle(UpdateRequest request, CancellationToken cancellationToken)
    1919        {
    20             var chapter = await _chapterRepository.GetByIdAsync(request.Id, cancellationToken);
     20            var chapter = await _chapterRepository.GetByIdWithStoryAsync(request.Id, cancellationToken);
    2121            if (chapter is null)
    2222                return new UpdateResponse(false);
     23
     24            if (chapter.Story.UserId != request.CallerId)
     25                throw new UnauthorizedAccessException("You do not own this chapter.");
    2326
    2427            chapter.Number = request.Number;
  • ChapterX.Application/Chapter/Commands/UpdateRequest.cs

    rd300631 rb373fea  
    99        string Title,
    1010        string Content,
    11         int? WordCount
     11        int? WordCount,
     12        int CallerId = 0
    1213    ) : IRequest<UpdateResponse>;
    1314}
  • ChapterX.Application/Comment/Commands/AddRequest.cs

    rd300631 rb373fea  
    11using MediatR;
     2using System.ComponentModel.DataAnnotations;
    23
    34namespace ChapterX.Application.Comment.Commands
    45{
    5     public record AddRequest(string Content, int UserId, int StoryId) : IRequest<AddResponse>;
     6    public record AddRequest(
     7        [Required][MaxLength(2000)] string Content,
     8        int UserId,
     9        int StoryId
     10    ) : IRequest<AddResponse>;
    611}
  • ChapterX.Application/Comment/Commands/DeleteHandler.cs

    rd300631 rb373fea  
    2222                return new DeleteResponse(false);
    2323
     24            if (comment.UserId != request.CallerId)
     25                throw new UnauthorizedAccessException("You do not own this comment.");
     26
    2427            await _commentRepository.DeleteAsync(comment, cancellationToken);
    2528
  • ChapterX.Application/Comment/Commands/DeleteRequest.cs

    rd300631 rb373fea  
    33namespace ChapterX.Application.Comment.Commands
    44{
    5     public record DeleteRequest(int Id) : IRequest<DeleteResponse>;
     5    public record DeleteRequest(int Id, int CallerId) : IRequest<DeleteResponse>;
    66}
  • ChapterX.Application/Comment/Commands/UpdateHandler.cs

    rd300631 rb373fea  
    2222                return new UpdateResponse(false);
    2323
     24            if (comment.UserId != request.CallerId)
     25                throw new UnauthorizedAccessException("You do not own this comment.");
     26
    2427            comment.Content = request.Content;
    2528            comment.UpdatedAt = DateTime.UtcNow;
  • ChapterX.Application/Comment/Commands/UpdateRequest.cs

    rd300631 rb373fea  
    33namespace ChapterX.Application.Comment.Commands
    44{
    5     public record UpdateRequest(int Id, string Content) : IRequest<UpdateResponse>;
     5    public record UpdateRequest(int Id, string Content, int CallerId = 0) : IRequest<UpdateResponse>;
    66}
  • ChapterX.Application/Story/Commands/AddRequest.cs

    rd300631 rb373fea  
    11using MediatR;
     2using System.ComponentModel.DataAnnotations;
    23
    34namespace ChapterX.Application.Story.Commands
    45{
    5     public record AddRequest(bool MatureContent, string ShortDescription, string? Image, string Content, int UserId, List<string> Genres) : IRequest<AddResponse>;
     6    public record AddRequest(
     7        bool MatureContent,
     8        [Required][MaxLength(500)] string ShortDescription,
     9        [MaxLength(2048)] string? Image,
     10        [Required] string Content,
     11        int UserId,
     12        List<string> Genres
     13    ) : IRequest<AddResponse>;
    614}
  • ChapterX.Application/Story/Commands/DeleteHandler.cs

    rd300631 rb373fea  
    2222                return new DeleteResponse(false);
    2323
     24            if (story.UserId != request.CallerId)
     25                throw new UnauthorizedAccessException("You do not own this story.");
     26
    2427            await _storyRepository.DeleteAsync(story, cancellationToken);
    2528
  • ChapterX.Application/Story/Commands/DeleteRequest.cs

    rd300631 rb373fea  
    33namespace ChapterX.Application.Story.Commands
    44{
    5     public record DeleteRequest(int Id) : IRequest<DeleteResponse>;
     5    public record DeleteRequest(int Id, int CallerId) : IRequest<DeleteResponse>;
    66}
  • ChapterX.Application/Story/Commands/UpdateHandler.cs

    rd300631 rb373fea  
    2222                return new UpdateResponse(false);
    2323
     24            if (story.UserId != request.CallerId)
     25                throw new UnauthorizedAccessException("You do not own this story.");
     26
    2427            story.MatureContent = request.MatureContent;
    2528            story.ShortDescription = request.ShortDescription;
  • ChapterX.Application/Story/Commands/UpdateRequest.cs

    rd300631 rb373fea  
    33namespace ChapterX.Application.Story.Commands
    44{
    5     public record UpdateRequest(int Id, bool MatureContent, string ShortDescription, string? Image, string Content) : IRequest<UpdateResponse>;
     5    public record UpdateRequest(int Id, bool MatureContent, string ShortDescription, string? Image, string Content, int CallerId = 0) : IRequest<UpdateResponse>;
    66}
  • ChapterX.Domain/Repositories/IChapterRepository.cs

    rd300631 rb373fea  
    66    {
    77        Task<IEnumerable<Chapter>> GetByStoryIdAsync(int storyId, CancellationToken cancellationToken = default);
     8        Task<Chapter?> GetByIdWithStoryAsync(int id, CancellationToken cancellationToken = default);
    89    }
    910}
  • ChapterX.Infrastructure/Repositories/ChapterRepository.cs

    rd300631 rb373fea  
    2424                .ToListAsync(cancellationToken);
    2525        }
     26
     27        public async Task<Chapter?> GetByIdWithStoryAsync(int id, CancellationToken cancellationToken = default)
     28        {
     29            return await _dbSet
     30                .Include(c => c.Story)
     31                .FirstOrDefaultAsync(c => c.Id == id, cancellationToken);
     32        }
    2633    }
    2734}
  • ChapterX.Infrastructure/Services/JwtTokenService.cs

    rd300631 rb373fea  
    2323            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    2424
     25            var role = user.Admin != null ? "Admin"
     26                : user.Writer != null ? "Writer"
     27                : "RegularUser";
     28
    2529            var claims = new[]
    2630            {
     
    2832                new Claim(JwtRegisteredClaimNames.Email, user.Email),
    2933                new Claim(JwtRegisteredClaimNames.UniqueName, user.Username),
    30                 new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
     34                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
     35                new Claim(ClaimTypes.Role, role)
    3136            };
    3237
Note: See TracChangeset for help on using the changeset viewer.