diff --git a/apps/server-net/src/Host/Controllers/AdminController.cs b/apps/server-net/src/Host/Controllers/AdminController.cs index e547116..bf31bfb 100644 --- a/apps/server-net/src/Host/Controllers/AdminController.cs +++ b/apps/server-net/src/Host/Controllers/AdminController.cs @@ -11,8 +11,11 @@ using MediatR; using Host.Application.Admin.Queries; using Host.Application.Admin.Commands; +using Microsoft.AspNetCore.Authorization; + namespace Host.Controllers; +[Authorize] [ApiController] [Route("api/[controller]")] public class AdminController : ControllerBase diff --git a/apps/server-net/src/Host/Controllers/ChatsController.cs b/apps/server-net/src/Host/Controllers/ChatsController.cs index 582ebb8..6ec8d0d 100644 --- a/apps/server-net/src/Host/Controllers/ChatsController.cs +++ b/apps/server-net/src/Host/Controllers/ChatsController.cs @@ -52,7 +52,7 @@ public sealed class ChatsController : ControllerBase var result = await _sender.Send(command, ct); if (result.IsFailure) return BadRequest(result.Error.Description); - var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); + var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct); return Ok(chatResult.Value); } @@ -63,7 +63,7 @@ public sealed class ChatsController : ControllerBase var result = await _sender.Send(command, ct); if (result.IsFailure) return BadRequest(result.Error.Description); - var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); + var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct); return Ok(chatResult.Value); } @@ -81,7 +81,7 @@ public sealed class ChatsController : ControllerBase var result = await _sender.Send(command, ct); if (result.IsFailure) return BadRequest(result.Error.Description); - var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); + var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct); return Ok(chatResult.Value); } @@ -91,7 +91,7 @@ public sealed class ChatsController : ControllerBase var result = await _sender.Send(new GetOrCreateFavoritesCommand(_userContext.UserId), ct); if (result.IsFailure) return BadRequest(result.Error.Description); - var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); + var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct); return Ok(chatResult.Value); } @@ -101,7 +101,7 @@ public sealed class ChatsController : ControllerBase var result = await _sender.Send(new UpdateChatCommand(id, _userContext.UserId, request.Name, request.Description), ct); if (result.IsFailure) return NotFound(); - var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); + var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct); return Ok(chatResult.Value); } @@ -139,7 +139,7 @@ public sealed class ChatsController : ControllerBase var result = await _sender.Send(new AddMembersCommand(id, _userContext.UserId, request.UserIds.ToList()), ct); if (result.IsFailure) return NotFound(); - var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); + var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct); return Ok(chatResult.Value); } @@ -149,7 +149,7 @@ public sealed class ChatsController : ControllerBase var result = await _sender.Send(new RemoveMemberCommand(id, _userContext.UserId, userId), ct); if (result.IsFailure) return NotFound(); - var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); + var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct); return Ok(chatResult.Value); } @@ -163,7 +163,7 @@ public sealed class ChatsController : ControllerBase if (result.IsFailure) return NotFound(); - var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); + var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct); return Ok(chatResult.Value); } @@ -177,7 +177,7 @@ public sealed class ChatsController : ControllerBase if (result.IsFailure) return NotFound(); - var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); + var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct); return Ok(chatResult.Value); } @@ -187,7 +187,7 @@ public sealed class ChatsController : ControllerBase var result = await _sender.Send(new RemoveGroupAvatarCommand(id, _userContext.UserId), ct); if (result.IsFailure) return NotFound(); - var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); + var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct); return Ok(chatResult.Value); } } diff --git a/apps/server-net/src/Host/Controllers/FilesController.cs b/apps/server-net/src/Host/Controllers/FilesController.cs index 46960ea..992e338 100644 --- a/apps/server-net/src/Host/Controllers/FilesController.cs +++ b/apps/server-net/src/Host/Controllers/FilesController.cs @@ -4,6 +4,7 @@ using Knot.Shared.Kernel.Storage; namespace Host.Controllers; +[Authorize] [ApiController] [Route("api/files")] public sealed class FilesController : ControllerBase diff --git a/apps/server-net/src/Modules/Chats/Application/Chats/GetChatById/GetChatById.cs b/apps/server-net/src/Modules/Chats/Application/Chats/GetChatById/GetChatById.cs index 56a0b74..613c527 100644 --- a/apps/server-net/src/Modules/Chats/Application/Chats/GetChatById/GetChatById.cs +++ b/apps/server-net/src/Modules/Chats/Application/Chats/GetChatById/GetChatById.cs @@ -11,7 +11,7 @@ using Knot.Modules.Chats.Application.Abstractions; namespace Knot.Modules.Chats.Application.Chats.GetChatById; -public record GetChatByIdQuery(Guid ChatId) : IQuery; +public record GetChatByIdQuery(Guid UserId, Guid ChatId) : IQuery; internal sealed class GetChatByIdQueryHandler : IQueryHandler { @@ -31,6 +31,11 @@ internal sealed class GetChatByIdQueryHandler : IQueryHandler(null); + if (!chat.Members.Any(m => m.UserId == request.UserId)) + { + return Result.Failure(new Error("Chats.Forbidden", "Вы не являетесь участником этого чата.")); + } + var userIdsToFetch = new HashSet(); foreach (var m in chat.Members) userIdsToFetch.Add(m.UserId); diff --git a/apps/server-net/src/Modules/Chats/Application/Messages/GetMessages/GetMessagesQuery.cs b/apps/server-net/src/Modules/Chats/Application/Messages/GetMessages/GetMessagesQuery.cs index ff26c99..5fd8101 100644 --- a/apps/server-net/src/Modules/Chats/Application/Messages/GetMessages/GetMessagesQuery.cs +++ b/apps/server-net/src/Modules/Chats/Application/Messages/GetMessages/GetMessagesQuery.cs @@ -8,6 +8,8 @@ using Knot.Shared.Kernel; using Knot.Modules.Chats.Application.DTOs; using Knot.Modules.Chats.Domain; +using Knot.Modules.Chats.Application.Abstractions; + namespace Knot.Modules.Chats.Application.Messages.GetMessages; public record GetMessagesQuery(Guid UserId, Guid ChatId, string? Cursor) : IQuery>; @@ -16,15 +18,23 @@ internal sealed class GetMessagesQueryHandler : IQueryHandler>> Handle(GetMessagesQuery request, CancellationToken cancellationToken) { + var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken); + if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId)) + { + return Result.Failure>(new Error("Chats.Forbidden", "Вы не являетесь участником этого чата.")); + } + DateTime? cursorDate = null; if (!string.IsNullOrEmpty(request.Cursor) && DateTime.TryParse(request.Cursor, null, System.Globalization.DateTimeStyles.RoundtripKind, out var parsed)) { diff --git a/apps/server-net/src/Modules/Chats/Application/Messages/GetSharedMedia/GetSharedMediaQuery.cs b/apps/server-net/src/Modules/Chats/Application/Messages/GetSharedMedia/GetSharedMediaQuery.cs index 73bb1a8..04fc240 100644 --- a/apps/server-net/src/Modules/Chats/Application/Messages/GetSharedMedia/GetSharedMediaQuery.cs +++ b/apps/server-net/src/Modules/Chats/Application/Messages/GetSharedMedia/GetSharedMediaQuery.cs @@ -9,6 +9,8 @@ using Knot.Shared.Kernel; using Knot.Modules.Chats.Application.DTOs; using Knot.Modules.Chats.Domain; +using Knot.Modules.Chats.Application.Abstractions; + namespace Knot.Modules.Chats.Application.Messages.GetSharedMedia; public record GetSharedMediaQuery(Guid UserId, Guid ChatId, string? Type) : IQuery>; @@ -17,15 +19,23 @@ internal sealed class GetSharedMediaQueryHandler : IQueryHandler>> Handle(GetSharedMediaQuery request, CancellationToken cancellationToken) { + var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken); + if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId)) + { + return Result.Failure>(new Error("Chats.Forbidden", "Вы не являетесь участником этого чата.")); + } + var messages = await _messageRepository.GetChatMessagesAsync(request.ChatId, 300, 0, cancellationToken); messages = messages.Where(m => !m.IsDeleted && !m.DeletedByUsers.Contains(request.UserId)).ToList(); diff --git a/apps/server-net/src/Modules/Chats/Application/Messages/SearchMessages/SearchMessagesQuery.cs b/apps/server-net/src/Modules/Chats/Application/Messages/SearchMessages/SearchMessagesQuery.cs index 13f22e0..f19cc7c 100644 --- a/apps/server-net/src/Modules/Chats/Application/Messages/SearchMessages/SearchMessagesQuery.cs +++ b/apps/server-net/src/Modules/Chats/Application/Messages/SearchMessages/SearchMessagesQuery.cs @@ -25,7 +25,7 @@ internal sealed class SearchMessagesQueryHandler : IQueryHandler>> Handle(SearchMessagesQuery request, CancellationToken cancellationToken) { - var messages = await _messageRepository.SearchMessagesAsync(request.Query, request.ChatId, cancellationToken); + var messages = await _messageRepository.SearchMessagesAsync(request.Query, request.ChatId, request.UserId, cancellationToken); messages = messages.Where(m => !m.DeletedByUsers.Contains(request.UserId)).ToList(); var userIds = messages.Select(m => m.SenderId).ToList(); diff --git a/apps/server-net/src/Modules/Chats/Application/Messages/Send/SendMessageCommandHandler.cs b/apps/server-net/src/Modules/Chats/Application/Messages/Send/SendMessageCommandHandler.cs index 6ff6c9e..0eb57df 100644 --- a/apps/server-net/src/Modules/Chats/Application/Messages/Send/SendMessageCommandHandler.cs +++ b/apps/server-net/src/Modules/Chats/Application/Messages/Send/SendMessageCommandHandler.cs @@ -47,8 +47,11 @@ public sealed class SendMessageCommandHandler : ICommandHandler(new Error("Chats.NotFound", "Чат не найден.")); } - // 2. Проверяем, является ли отправитель участником (в будущем) - // if (!chat.Members.Any(m => m.UserId == request.SenderId)) ... + // 2. Проверяем, является ли отправитель участником + if (!chat.Members.Any(m => m.UserId == request.SenderId)) + { + return Result.Failure(new Error("Chats.Forbidden", "Вы не являетесь участником этого чата.")); + } // 3. Создаем сообщение var message = Message.Create( diff --git a/apps/server-net/src/Modules/Chats/Domain/IMessageRepository.cs b/apps/server-net/src/Modules/Chats/Domain/IMessageRepository.cs index d515432..2f82d65 100644 --- a/apps/server-net/src/Modules/Chats/Domain/IMessageRepository.cs +++ b/apps/server-net/src/Modules/Chats/Domain/IMessageRepository.cs @@ -7,7 +7,7 @@ public interface IMessageRepository void Add(Message message); Task GetByIdAsync(Guid id, CancellationToken cancellationToken); Task> GetChatMessagesAsync(Guid chatId, int limit, int offset, CancellationToken cancellationToken); - Task> SearchMessagesAsync(string query, Guid? chatId, CancellationToken cancellationToken); + Task> SearchMessagesAsync(string query, Guid? chatId, Guid requestingUserId, CancellationToken cancellationToken); Task AddReadReceiptsAsync(Guid userId, List messageIds, CancellationToken cancellationToken); Task AddReactionAsync(Guid messageId, Guid userId, string emoji, CancellationToken cancellationToken); Task RemoveReactionAsync(Guid messageId, Guid userId, string emoji, CancellationToken cancellationToken); diff --git a/apps/server-net/src/Modules/Chats/Infrastructure/Persistence/ChatsDbContext.cs b/apps/server-net/src/Modules/Chats/Infrastructure/Persistence/ChatsDbContext.cs index 1cd279d..4af77d8 100644 --- a/apps/server-net/src/Modules/Chats/Infrastructure/Persistence/ChatsDbContext.cs +++ b/apps/server-net/src/Modules/Chats/Infrastructure/Persistence/ChatsDbContext.cs @@ -58,6 +58,8 @@ public sealed class ChatsDbContext : DbContext, IChatsUnitOfWork { builder.ToTable("Messages"); builder.HasKey(m => m.Id); + builder.HasIndex(m => m.ChatId); + builder.HasIndex(m => new { m.ChatId, m.CreatedAt }); builder.Property(m => m.Content) .HasConversion( diff --git a/apps/server-net/src/Modules/Chats/Infrastructure/Persistence/MessageRepository.cs b/apps/server-net/src/Modules/Chats/Infrastructure/Persistence/MessageRepository.cs index e1d4787..812c5c0 100644 --- a/apps/server-net/src/Modules/Chats/Infrastructure/Persistence/MessageRepository.cs +++ b/apps/server-net/src/Modules/Chats/Infrastructure/Persistence/MessageRepository.cs @@ -58,9 +58,14 @@ public sealed class MessageRepository : IMessageRepository .ToListAsync(cancellationToken); } - public async Task> SearchMessagesAsync(string query, Guid? chatId, CancellationToken cancellationToken) + public async Task> SearchMessagesAsync(string query, Guid? chatId, Guid requestingUserId, CancellationToken cancellationToken) { - var q = _dbContext.Messages.AsQueryable(); + var validChatIdsQuery = _dbContext.Chats + .Where(c => c.Members.Any(m => m.UserId == requestingUserId)) + .Select(c => c.Id); + + var q = _dbContext.Messages.Where(m => validChatIdsQuery.Contains(m.ChatId)); + if (chatId.HasValue) { q = q.Where(m => m.ChatId == chatId.Value);