Skip to content

Conversation

AnujChhikara
Copy link
Member

@AnujChhikara AnujChhikara commented Oct 9, 2025

Date: 10 Oct 2025

Developer Name: @AnujChhikara


Issue Ticket Number

Description

  • added validation for poc role update , only admins should be able to update the team PoC

Documentation Updated?

  • Yes
  • No

Under Feature Flag

  • Yes
  • No

Database Changes

  • Yes
  • No

Breaking Changes

  • Yes
  • No

Development Tested?

  • Yes
  • No

Screenshots

Screenshot 1

Test Coverage

Screenshot 1

Additional Notes

Description by Korbit AI

What change is being made?

Enforce PoC update permissions by adding _validate_poc_update_permissions and invoking it during team updates when PoC changes, and extend tests to cover authorized/unauthorized scenarios.

Why are these changes being made?

To ensure only the team creator or an admin can update the PoC, preventing unauthorized updates and surfacing a consistent unauthorized error. This strengthens access control around PoC changes.

Is this description stale? Ask me to generate a new description by commenting /korbit-generate-pr-description

…ly authorized users can update the point of contact for a team
@AnujChhikara AnujChhikara self-assigned this Oct 9, 2025
Copy link

coderabbitai bot commented Oct 9, 2025

Summary by CodeRabbit

  • New Features

    • Enforced permissions for updating a team’s point of contact (POC): only team creators and team admins can make this change.
    • Clearer unauthorized error response when users without sufficient permissions attempt to update the POC.
  • Tests

    • Added unit tests to validate admin and non-admin behaviors for POC updates.

Walkthrough

Adds a permission check for updating a team’s POC in TeamService.update_team. Introduces a new classmethod to validate updater permissions, imports ApiErrors, and updates unit tests to cover admin and non-admin scenarios for the new authorization gate.

Changes

Cohort / File(s) Summary
Service authorization logic
todo/services/team_service.py
Imports ApiErrors. Adds _validate_poc_update_permissions(team_id, updated_by_user_id, team) to enforce POC update rules. Integrates pre-check in update_team when dto.poc_id is provided; raises PermissionError if unauthorized.
Unit tests for permissions
todo/tests/unit/services/test_team_service.py
Adds tests verifying admin passes permission check and member fails with PermissionError. Mocks UserRoleService.has_role calls with RoleName.ADMIN and RoleScope.TEAM. Imports ApiErrors, RoleName, RoleScope.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Client
  participant TS as TeamService
  participant URS as UserRoleService

  C->>TS: update_team(team_id, dto, updated_by_user_id)
  alt dto includes poc_id
    TS->>TS: _validate_poc_update_permissions(team_id, updated_by_user_id, team)
    alt Updater is team creator
      Note right of TS: Allow
    else Check ADMIN role in TEAM scope
      TS->>URS: has_role(user_id, ADMIN, TEAM, team_id)
      URS-->>TS: boolean
      alt is_admin == false
        TS-->>C: PermissionError(ApiErrors.UNAUTHORIZED_TITLE)
      else Continue
      end
    end
    TS-->>C: proceed with update flow
  else No POC change
    TS-->>C: proceed with update flow
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • RishiChaubey31
  • iamitprakash

Poem

A carrot of checks in the meadow of teams,
I hop through roles and permission streams.
If you’re the creator, come right through—
Admins as well, we welcome you.
But guard this POC, my whiskers insist;
Unauthorized paws? Denied with a twist. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly and concisely summarizes the primary change by stating the addition of validation for updating the team PoC role, directly reflecting the code modifications made in this pull request.
Description Check ✅ Passed The description outlines the purpose of the change by explaining the new PoC role validation and specifies that only admins can perform updates, which aligns with the code changes in the pull request.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch anuj/fix-poc-update-validation-issue

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@korbit-ai korbit-ai bot left a comment

Choose a reason for hiding this comment

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

Review by Korbit AI

Korbit automatically attempts to detect when you fix issues in new commits.
Category Issue Status
Error Handling Missing context in PermissionError for PoC update validation ▹ view
Performance Unnecessary PoC permission validation on unchanged values ▹ view
Readability Unclear Permission Logic Flow ▹ view
Files scanned
File Path Reviewed
todo/services/team_service.py

Explore our documentation to understand the languages and file types we support and the files we ignore.

Check out our docs on how you can make Korbit work best for you and your team.

Loving Korbit!? Share us on LinkedIn Reddit and X

if UserRoleService.has_role(updated_by_user_id, RoleName.ADMIN.value, RoleScope.TEAM.value, team_id):
return

raise PermissionError(ApiErrors.UNAUTHORIZED_TITLE)
Copy link

Choose a reason for hiding this comment

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

Missing context in PermissionError for PoC update validation category Error Handling

Tell me more
What is the issue?

Generic PermissionError is raised without contextual information about what specific permission check failed.

Why this matters

When this error occurs, developers and users won't know whether the failure was due to not being the team creator, not being an admin, or which specific PoC update permission was violated, making debugging and user experience poor.

Suggested change ∙ Feature Preview

Include specific context about the failed permission check:

raise PermissionError(f"Unauthorized to update team PoC. Only team creator or admin can update PoC for team {team_id}. User {updated_by_user_id} lacks required permissions.")
Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

Comment on lines +348 to +349
if dto.poc_id is not None:
cls._validate_poc_update_permissions(team_id, updated_by_user_id, existing_team)
Copy link

Choose a reason for hiding this comment

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

Unnecessary PoC permission validation on unchanged values category Performance

Tell me more
What is the issue?

The PoC permission validation is called for every team update when poc_id is not None, even when the PoC is not actually being changed.

Why this matters

This results in unnecessary permission checks and database queries when the PoC field is included in the update but remains the same value, wasting computational resources.

Suggested change ∙ Feature Preview

Only validate PoC update permissions when the PoC is actually being changed by comparing the new value with the existing value:

if dto.poc_id is not None and str(existing_team.poc_id) != dto.poc_id:
    cls._validate_poc_update_permissions(team_id, updated_by_user_id, existing_team)
Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

Comment on lines +515 to +521
if str(team.created_by) == updated_by_user_id:
return

if UserRoleService.has_role(updated_by_user_id, RoleName.ADMIN.value, RoleScope.TEAM.value, team_id):
return

raise PermissionError(ApiErrors.UNAUTHORIZED_TITLE)
Copy link

Choose a reason for hiding this comment

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

Unclear Permission Logic Flow category Readability

Tell me more
What is the issue?

The _validate_poc_update_permissions method uses early returns which makes the authorization logic harder to follow. The conditions for authorization are split across multiple returns.

Why this matters

Early returns in permission validation can make it difficult to understand all the conditions that grant access. A more explicit approach would make the authorization rules immediately clear.

Suggested change ∙ Feature Preview
@classmethod
def _validate_poc_update_permissions(cls, team_id: str, updated_by_user_id: str, team):
    is_team_creator = str(team.created_by) == updated_by_user_id
    is_team_admin = UserRoleService.has_role(updated_by_user_id, RoleName.ADMIN.value, RoleScope.TEAM.value, team_id)
    
    if not (is_team_creator or is_team_admin):
        raise PermissionError(ApiErrors.UNAUTHORIZED_TITLE)
Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

Copy link

@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: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a995e9 and cfbfb38.

📒 Files selected for processing (2)
  • todo/services/team_service.py (3 hunks)
  • todo/tests/unit/services/test_team_service.py (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-22T15:34:24.054Z
Learnt from: Hariom01010
PR: Real-Dev-Squad/todo-backend#279
File: todo/management/commands/migrate_add_roles_to_teams.py:3-3
Timestamp: 2025-09-22T15:34:24.054Z
Learning: In the todo-backend repository, UserTeamDetailsRepository is defined in todo/repositories/team_repository.py and should be imported from there, not from a separate module.

Applied to files:

  • todo/services/team_service.py
🧬 Code graph analysis (2)
todo/services/team_service.py (3)
todo/constants/messages.py (1)
  • ApiErrors (25-58)
todo/services/user_role_service.py (1)
  • has_role (82-87)
todo/constants/role.py (2)
  • RoleName (9-13)
  • RoleScope (4-6)
todo/tests/unit/services/test_team_service.py (3)
todo/constants/messages.py (1)
  • ApiErrors (25-58)
todo/constants/role.py (2)
  • RoleName (9-13)
  • RoleScope (4-6)
todo/services/team_service.py (2)
  • TeamService (29-545)
  • _validate_poc_update_permissions (514-521)
🔇 Additional comments (3)
todo/tests/unit/services/test_team_service.py (1)

5-5: LGTM!

The new imports are correctly added and necessary for the test methods that validate POC update permissions.

Also applies to: 17-17

todo/services/team_service.py (2)

9-9: LGTM!

The ApiErrors import is correctly added and used in the new permission validation method.


513-521: LGTM!

The permission validation logic is correctly implemented:

  • Team creators can always update the POC
  • Team admins (with ADMIN role in TEAM scope) can update the POC
  • Other users are denied with an appropriate error message

The string comparison on line 515 correctly converts the PyObjectId to string for comparison.

Comment on lines +348 to +349
if dto.poc_id is not None:
cls._validate_poc_update_permissions(team_id, updated_by_user_id, existing_team)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add validation to prevent POC removal without reassignment.

The current implementation allows admins to set the POC to None (by passing an empty string), but ApiErrors.CANNOT_REMOVE_POC indicates that "POC cannot be removed from a team. Reassign the POC first." This suggests a business rule that's not being enforced.

Additionally, there's no validation ensuring the new POC is actually a member of the team (unlike create_team which automatically adds the POC as a member).

Consider these validations:

 if dto.poc_id is not None:
     cls._validate_poc_update_permissions(team_id, updated_by_user_id, existing_team)
+    
+    # Prevent removal of POC without reassignment
+    if existing_team.poc_id and (not dto.poc_id or not dto.poc_id.strip()):
+        raise ValueError(ApiErrors.CANNOT_REMOVE_POC)
+    
+    # Validate new POC is a team member
+    if dto.poc_id and dto.poc_id.strip():
+        team_members = UserService.get_users_by_team_id(team_id)
+        team_member_ids = [user.id for user in team_members]
+        if dto.poc_id not in team_member_ids:
+            raise ValueError(f"User {dto.poc_id} is not a member of the team")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if dto.poc_id is not None:
cls._validate_poc_update_permissions(team_id, updated_by_user_id, existing_team)
if dto.poc_id is not None:
cls._validate_poc_update_permissions(team_id, updated_by_user_id, existing_team)
# Prevent removal of POC without reassignment
if existing_team.poc_id and (not dto.poc_id or not dto.poc_id.strip()):
raise ValueError(ApiErrors.CANNOT_REMOVE_POC)
# Validate new POC is a team member
if dto.poc_id and dto.poc_id.strip():
team_members = UserService.get_users_by_team_id(team_id)
team_member_ids = [user.id for user in team_members]
if dto.poc_id not in team_member_ids:
raise ValueError(f"User {dto.poc_id} is not a member of the team")
🤖 Prompt for AI Agents
In todo/services/team_service.py around lines 348-349, the update path currently
allows clearing the POC by passing an empty string and does not validate that a
newly assigned POC is a team member; update the logic to (1) treat
empty-string/blank values as an attempt to remove the POC and reject that by
raising ApiErrors.CANNOT_REMOVE_POC (i.e., only allow POC changes when a
non-blank ID is provided), and (2) when a non-blank new poc_id is provided,
ensure that user is a team member—if they are not, either add them to the team’s
members list (consistent with create_team) or raise a validation error;
implement the chosen behavior and add tests for both the removal attempt and
assignment-of-non-member cases.

Comment on lines +306 to +322
@patch("todo.services.team_service.UserRoleService.has_role")
def test_poc_update_permissions_team_admin_success(self, mock_has_role):
mock_has_role.return_value = True

result = TeamService._validate_poc_update_permissions(self.team_id, self.admin_id, self.team_model)
self.assertIsNone(result)
mock_has_role.assert_called_once_with(self.admin_id, RoleName.ADMIN.value, RoleScope.TEAM.value, self.team_id)

@patch("todo.services.team_service.UserRoleService.has_role")
def test_poc_update_permissions_regular_member_fails(self, mock_has_role):
mock_has_role.return_value = False

with self.assertRaises(PermissionError) as context:
TeamService._validate_poc_update_permissions(self.team_id, self.member_id, self.team_model)

self.assertIn(ApiErrors.UNAUTHORIZED_TITLE, str(context.exception))
mock_has_role.assert_called_once_with(self.member_id, RoleName.ADMIN.value, RoleScope.TEAM.value, self.team_id)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add test coverage for the team creator updating POC.

The tests validate the admin and non-admin scenarios, but don't cover the case where the team creator (not necessarily an admin) updates the POC. According to the implementation in team_service.py (line 515-516), the creator can update the POC without needing the admin role.

Add a test case to verify the creator bypass path:

@patch("todo.services.team_service.UserRoleService.has_role")
def test_poc_update_permissions_team_creator_success(self, mock_has_role):
    """Test that team creator can update POC without admin role"""
    # Creator updates POC - should bypass role check
    result = TeamService._validate_poc_update_permissions(self.team_id, self.user_id, self.team_model)
    
    self.assertIsNone(result)
    # Verify has_role was NOT called since creator bypass should occur first
    mock_has_role.assert_not_called()
🤖 Prompt for AI Agents
In todo/tests/unit/services/test_team_service.py around lines 306 to 322, tests
cover admin and non-admin flows for _validate_poc_update_permissions but miss
the team-creator bypass case; add a new unit test where the caller is the team
creator (self.user_id) so the method returns None and UserRoleService.has_role
is not invoked. Implement the test using
@patch("todo.services.team_service.UserRoleService.has_role") with
mock_has_role, call TeamService._validate_poc_update_permissions(self.team_id,
self.user_id, self.team_model), assert the result is None, and assert
mock_has_role.assert_not_called() to confirm the creator path short-circuits the
role check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant