Skip to content

Conversation

@yassinedorbozgithub
Copy link
Collaborator

@yassinedorbozgithub yassinedorbozgithub commented Jul 8, 2025

Motivation

This PR introduces permission checks for Web Socket global rooms(subscriber, message)., ensuring that only authorized users can access and interact within them.

Fixes #1031

Demo

Authorization.Module.WebSocket.rooms.integration.webm

Type of change:

  • Bug fix (non-breaking change which fixes an issue)

Checklist:

  • I have performed a self-review of my own code
  • I have added unit tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

  • New Features

    • Added role-based permission checks for subscribing to message and subscriber notifications, ensuring only authorized users can join notification rooms.
    • Introduced a method to verify user access rights based on HTTP method, user roles, and target resource.
    • Integrated a new global authorization module to centralize permission and user role management.
    • Enhanced websocket gateway to enforce authorization before joining notification rooms.
  • Bug Fixes

    • Improved error handling for unauthorized access attempts when subscribing to notifications.
  • Tests

    • Expanded and improved test coverage for permission checks and subscription logic.
    • Added new model and permission fixtures to support enhanced testing scenarios.
    • Updated tests to use richer request/response mocks and included user fixture data for realistic scenarios.

@yassinedorbozgithub yassinedorbozgithub self-assigned this Jul 8, 2025
@yassinedorbozgithub yassinedorbozgithub added the security Vulnerabilities, exploits, sensitive data label Jul 8, 2025
@yassinedorbozgithub yassinedorbozgithub linked an issue Jul 8, 2025 that may be closed by this pull request
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 8, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Role-based access control was added to the subscribe methods in both MessageService and SubscriberService, requiring user role and permission checks before joining notification rooms. Corresponding unit tests and fixtures were updated to support and verify this logic. Permission checking logic was introduced in PermissionService.

Changes

Files/Paths Change Summary
api/src/chat/services/message.service.ts, api/src/chat/services/subscriber.service.ts Added user role and permission checks to subscribe methods; updated calls to joinNotificationSockets to pass full request and model string.
api/src/chat/services/message.service.spec.ts, api/src/chat/services/subscriber.service.spec.ts Enhanced unit tests: added mocks for new dependencies, helper for request/response, split tests for GET/POST methods.
api/src/user/services/permission.service.ts Added canAccess method for permission checking based on HTTP method, user roles, and model.
api/src/utils/test/fixtures/model.ts Added fixtures for "Message" and "Subscriber" models.
api/src/utils/test/fixtures/permission.ts Added permission fixtures for "Message" and "Subscriber" models.
api/src/utils/test/fixtures/subscriber.ts Updated to install permission fixtures instead of user fixtures.
api/src/app.module.ts Imported and added AuthorizationModule to main app module imports.
api/src/authorization/authorization.module.ts Added new global AuthorizationModule with permission and user services and repositories.
api/src/authorization/authorization.service.ts Added AuthorizationService with canAccess method to centralize permission checks by user and model.
api/src/user/guards/ability.guard.ts Refactored guard to delegate permission checking to AuthorizationService instead of inline logic.
api/src/websocket/websocket.gateway.ts Refactored joinNotificationSockets to accept full request, perform authorization check before joining room.
api/src/websocket/websocket.gateway.spec.ts Updated tests to use richer request mocks and user fixtures; adapted tests for new joinNotificationSockets signature and authorization logic.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Gateway
    participant MessageService
    participant UserService
    participant PermissionService

    Client->>Gateway: Socket subscribe request
    Gateway->>MessageService: subscribe(req, res)
    MessageService->>UserService: findById(userId)
    UserService-->>MessageService: user (with roles)
    MessageService->>PermissionService: canAccess(method, userRoles, "message")
    PermissionService-->>MessageService: true/false
    alt Permission granted
        MessageService->>Gateway: joinNotificationSockets(sessionId, Room.MESSAGE)
        MessageService-->>Client: success response
    else Permission denied
        MessageService-->>Client: ForbiddenException
    end
Loading
sequenceDiagram
    participant Client
    participant Gateway
    participant SubscriberService
    participant UserService
    participant PermissionService

    Client->>Gateway: Socket subscribe request
    Gateway->>SubscriberService: subscribe(req, res)
    SubscriberService->>UserService: findById(userId)
    UserService-->>SubscriberService: user (with roles)
    SubscriberService->>PermissionService: canAccess(method, userRoles, "subscriber")
    PermissionService-->>SubscriberService: true/false
    alt Permission granted
        SubscriberService->>Gateway: joinNotificationSockets(sessionId, Room.SUBSCRIBER)
        SubscriberService-->>Client: success response
    else Permission denied
        SubscriberService-->>Client: ForbiddenException
    end
Loading

Assessment against linked issues

Objective Addressed Explanation
Add WS gateway unit tests to cover socket join operations: Message, Subscriber (#1031)

Suggested labels

bug, needs-review

Suggested reviewers

  • medchedli
  • marrouchi
  • MohamedAliBouhaouala

Poem

In the warren, sockets hop and play,
Now guarded by roles, they’re safe each day.
With tests that check who gets to join,
Only the worthy can now enjoin.
So here’s a cheer from this CodeRabbit’s den—
For secure messages, again and again! 🐇✨


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
api/src/chat/services/subscriber.service.spec.ts (1)

123-145: Tests cover happy path but missing permission verification.

The tests properly verify the gateway calls and response structure, but they don't test the actual permission checking logic. Consider adding tests for scenarios where permission checks fail.

Consider adding tests like:

it('should return 403 when user lacks permission', async () => {
  // Mock permission service to return false
  jest.spyOn(permissionService, 'canAccess').mockResolvedValue(false);
  
  const [req, res] = buildReqRes('GET', allUsers[0].id);
  await mockSubscriberService.subscribe(req, res);
  
  expect(res.status).toHaveBeenCalledWith(403);
  expect(mockGateway.joinNotificationSockets).not.toHaveBeenCalled();
});
🧹 Nitpick comments (2)
api/src/user/services/permission.service.ts (1)

86-121: Well-implemented permission checking method with room for improvement.

The canAccess method correctly implements role-based permission checking. However, consider these improvements:

  1. Method parameter typing: The method parameter should be more specific
  2. Error handling: The catch block is too generic
  3. Code readability: The permission filtering logic could be clearer

Apply this diff to improve the method:

  async canAccess(
-   method: string,
+   method: keyof typeof MethodToAction,
    userRoles: string[],
    targetModel: TModel,
  ): Promise<boolean> {
    try {
      const permissions = await this.getPermissions();

      if (permissions && userRoles.length) {
-       const permissionsFromRoles = Object.entries(permissions)
-         .filter(([key, _]) => userRoles.includes(key))
-         .map(([_, value]) => value);
+       const permissionsFromRoles = userRoles
+         .map(roleId => permissions[roleId])
+         .filter(Boolean);

        if (
          permissionsFromRoles.some((permission) =>
            permission[targetModel]?.includes(MethodToAction[method]),
          )
        ) {
          return true;
        }
      }
-   } catch (e) {
-     this.logger.error('Request has no ability to get access', e);
+   } catch (error) {
+     this.logger.error(
+       `Permission check failed for method ${method} on model ${targetModel}`,
+       error,
+     );
      return false;
    }

    return false;
  }
api/src/chat/services/subscriber.service.spec.ts (1)

106-116: Helper function parameter name mismatch.

The parameter is named userId but the JSDoc comment says subscriberId.

Apply this diff to fix the parameter name:

-   buildReqRes = (method: 'GET' | 'POST', userId: string) => [
+   buildReqRes = (method: 'GET' | 'POST', subscriberId: string) => [
      {
        sessionID: SESSION_ID,
        method,
-       session: { passport: { user: { id: userId } } },
+       session: { passport: { user: { id: subscriberId } } },
      } as SocketRequest,
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b10d07 and 6281a33.

📒 Files selected for processing (8)
  • api/src/chat/services/message.service.spec.ts (4 hunks)
  • api/src/chat/services/message.service.ts (3 hunks)
  • api/src/chat/services/subscriber.service.spec.ts (5 hunks)
  • api/src/chat/services/subscriber.service.ts (4 hunks)
  • api/src/user/services/permission.service.ts (3 hunks)
  • api/src/utils/test/fixtures/model.ts (2 hunks)
  • api/src/utils/test/fixtures/permission.ts (2 hunks)
  • api/src/utils/test/fixtures/subscriber.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (3)
api/src/utils/test/fixtures/permission.ts (1)
Learnt from: medchedli
PR: Hexastack/Hexabot#1096
File: api/src/chat/repositories/block.repository.ts:0-0
Timestamp: 2025-07-03T17:41:54.853Z
Learning: In the Hexabot block schema, a block is designed so that it can never simultaneously have both an `attachedBlock` and non-empty `nextBlocks`; the two are mutually exclusive.
api/src/user/services/permission.service.ts (1)
Learnt from: medchedli
PR: Hexastack/Hexabot#1096
File: api/src/chat/repositories/block.repository.ts:0-0
Timestamp: 2025-07-03T17:41:54.853Z
Learning: In the Hexabot block schema, a block is designed so that it can never simultaneously have both an `attachedBlock` and non-empty `nextBlocks`; the two are mutually exclusive.
api/src/utils/test/fixtures/model.ts (1)
Learnt from: medchedli
PR: Hexastack/Hexabot#1096
File: api/src/chat/repositories/block.repository.ts:0-0
Timestamp: 2025-07-03T17:41:54.853Z
Learning: In the Hexabot block schema, a block is designed so that it can never simultaneously have both an `attachedBlock` and non-empty `nextBlocks`; the two are mutually exclusive.
🧬 Code Graph Analysis (6)
api/src/utils/test/fixtures/subscriber.ts (1)
api/src/utils/test/fixtures/permission.ts (1)
  • installPermissionFixtures (69-87)
api/src/chat/services/subscriber.service.spec.ts (3)
api/src/websocket/utils/socket-request.ts (1)
  • SocketRequest (20-131)
api/src/websocket/utils/socket-response.ts (1)
  • SocketResponse (11-81)
api/src/utils/test/utils.ts (1)
  • buildTestingMocks (252-305)
api/src/user/services/permission.service.ts (1)
api/src/user/controllers/user.controller.ts (1)
  • permissions (135-161)
api/src/chat/services/subscriber.service.ts (1)
api/src/user/services/permission.service.ts (1)
  • canAccess (94-121)
api/src/chat/services/message.service.spec.ts (4)
api/src/websocket/utils/socket-request.ts (1)
  • SocketRequest (20-131)
api/src/websocket/utils/socket-response.ts (1)
  • SocketResponse (11-81)
api/src/utils/test/utils.ts (1)
  • buildTestingMocks (252-305)
api/src/utils/test/fixtures/message.ts (1)
  • installMessageFixtures (58-71)
api/src/chat/services/message.service.ts (1)
api/src/user/services/permission.service.ts (1)
  • canAccess (94-121)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Frontend Tests
  • GitHub Check: API-Tests
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (31)
api/src/user/services/permission.service.ts (2)

2-2: Copyright year updated correctly.

The copyright year has been updated from 2024 to 2025 as part of the regular maintenance.


25-26: New imports added for permission checking functionality.

The imports for MethodToAction and TModel are correctly added to support the new canAccess method.

api/src/utils/test/fixtures/permission.ts (2)

2-2: Copyright year updated correctly.

The copyright year has been updated from 2024 to 2025 as part of the regular maintenance.


43-66: Permission fixtures added for Message and Subscriber models.

The new permission fixtures correctly provide CREATE and READ permissions for the Message and Subscriber models. These fixtures align with the new permission checking functionality and follow the established pattern.

api/src/utils/test/fixtures/subscriber.ts (2)

18-18: Import correctly updated to use permission fixtures.

The import has been appropriately changed from user fixtures to permission fixtures to align with the new permission-based access control system.


109-109: Function call updated to install permission fixtures.

The change from installUserFixtures() to installPermissionFixtures() is correct. Since installPermissionFixtures() internally calls installUserFixtures() (as shown in the relevant code snippets), the users variable is still available while providing the necessary permission data for the new access control system.

api/src/utils/test/fixtures/model.ts (2)

2-2: Copyright year updated correctly.

The copyright year has been updated from 2024 to 2025 as part of the regular maintenance.


27-38: Model fixtures added for Message and Subscriber.

The new model fixtures for Message and Subscriber are correctly structured and follow the established pattern. The identities 'message' and 'subscriber' align with the permission checking logic implemented in the PermissionService.

api/src/chat/services/subscriber.service.spec.ts (2)

21-22: New service imports added for permission checking.

The imports for PermissionService, UserService, SocketRequest, and SocketResponse are correctly added to support the updated testing requirements.

Also applies to: 33-34


62-67: Test variables and helper function properly declared.

The new test variables for permission and user services are correctly declared, and the buildReqRes helper function type is well-defined.

api/src/chat/services/message.service.spec.ts (9)

11-12: Good addition of required service imports.

The imports for PermissionService and UserService are correctly added to support the new permission checking functionality in the tests.


26-27: Appropriate imports for WebSocket utilities.

The imports for SocketRequest and SocketResponse are necessary for the new buildReqRes helper function and improved test structure.


57-62: Well-structured test variable declarations.

The addition of userService, permissionService, and buildReqRes variables provides good organization for the enhanced test suite.


71-85: Comprehensive service mock setup.

The expansion of the getMocks array to include UserService and PermissionService is correctly implemented to support the new permission checking functionality.


102-107: Constructor injection properly updated.

The mockMessageService instantiation correctly includes the new userService and permissionService dependencies, maintaining consistency with the actual service constructor.


108-118: Well-designed helper function for test setup.

The buildReqRes helper function effectively generates mock SocketRequest and SocketResponse objects with appropriate session and method details, promoting code reuse and consistency across tests.


125-135: Improved test specificity for GET method.

The test is now explicitly focused on testing the GET method behavior, which provides better clarity and separation of concerns.


137-147: Good test coverage for POST method.

The addition of a separate test case for the POST method ensures comprehensive coverage of both HTTP methods supported by the subscribe endpoint.


66-66: All model dependencies are correctly registered

RoleModel and ModelModel are both defined in api/src/user/schemas/{role,model}.schema.ts and are imported into api/src/user/user.module.ts’s MongooseModule.forFeature. The chat‐service tests include these models so that the permission service’s populate calls (on role and model fields) will work. No further changes are needed.

api/src/chat/services/subscriber.service.ts (6)

9-13: Appropriate exception import for access control.

The import of ForbiddenException is correctly added to support the new permission checking functionality in the subscribe method.


26-27: Required service imports for permission system.

The imports for PermissionService and UserService are necessary for implementing role-based access control in the WebSocket subscription process.


62-63: Constructor properly updated with new dependencies.

The constructor injection of userService and permissionService is correctly implemented to support the new permission checking functionality.


82-94: Robust user validation and role checking.

The implementation properly retrieves the user from the session and validates that they have roles before proceeding with permission checks. The error message "User is required!" clearly indicates the access requirement.


96-106: Well-implemented permission checking flow.

The use of permissionService.canAccess with the request method, user roles, and 'subscriber' target model follows a clear and consistent pattern. The success response includes the appropriate room and status information.


108-108: Appropriate access denial handling.

The ForbiddenException with the message "Not able to access" provides clear feedback for unauthorized access attempts while maintaining security by not revealing specific permission details.

api/src/chat/services/message.service.ts (6)

9-13: Appropriate exception import for access control.

The import of ForbiddenException is correctly added to support the new permission checking functionality in the subscribe method.


15-16: Required service imports for permission system.

The imports for PermissionService and UserService are necessary for implementing role-based access control in the WebSocket subscription process.


44-45: Constructor properly updated with new dependencies.

The constructor injection of userService and permissionService is correctly implemented to support the new permission checking functionality.


63-75: Consistent user validation and permission checking.

The implementation follows the same robust pattern as the SubscriberService, properly retrieving the user from the session, validating roles, and checking permissions using the appropriate target model ('message'). The consistency across services ensures a unified security approach.


77-84: Well-implemented success response flow.

The success path properly joins the notification sockets for the message room and returns an appropriate response with status 200 and the expected JSON payload structure.


86-86: Appropriate access denial handling.

The ForbiddenException with the message "Not able to access" provides clear feedback for unauthorized access attempts while maintaining security consistency with the SubscriberService implementation.

@yassinedorbozgithub yassinedorbozgithub force-pushed the 1031-implement-websocket-gateway-unit-tests branch from 6281a33 to 40b3fb6 Compare July 8, 2025 10:23
@yassinedorbozgithub yassinedorbozgithub changed the base branch from main to 1209-issue---add-authorization-module July 8, 2025 10:23
@yassinedorbozgithub yassinedorbozgithub changed the title fix(api): add subscriber WebSocket room permission checks fix(api): add subscriber WebSocket room permission checks [1209<-1031] Jul 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

security Vulnerabilities, exploits, sensitive data

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants