Skip to content

fix: reevaluate pbac on task changes - #854

Open
mbritense wants to merge 1 commit into
next-minorfrom
bugfix/691-reevaluate-pbac-on-task
Open

fix: reevaluate pbac on task changes#854
mbritense wants to merge 1 commit into
next-minorfrom
bugfix/691-reevaluate-pbac-on-task

Conversation

@mbritense

Copy link
Copy Markdown
Contributor

@mbritense
mbritense requested review from a team as code owners July 27, 2026 14:06
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Task permissions are now re-evaluated after assignment or unassignment changes.
    • Task detail dialogs close automatically when access is revoked or the task is no longer available.
    • Prevented stale permission results from incorrectly keeping assignment controls enabled.
    • Permission checks are also refreshed after external task updates.
  • Tests

    • Added coverage for permission cache invalidation, task assignment changes, access loss, and unavailable tasks.
  • Documentation

    • Updated the 13.39.0 release notes with these permission and task dialog improvements.

Walkthrough

PermissionService now invalidates cached and in-flight permission requests by resource and identifier. Task detail components invalidate permissions before refreshing after assignment changes and SSE updates, re-check view access, and close when task retrieval returns 403 or 404. New tests cover cache invalidation and modal behavior, and the release notes document the changes.

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bugfix/691-reevaluate-pbac-on-task

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/projects/valtimo/access-control/src/lib/services/permission.service.ts (1)

140-177: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Correlate stale in-flight batches with the current request generation.

_pendingPermissions and _permissionRequestsByKey are keyed by the deterministic request hash, so after invalidateResource deletes the subject for that key, any later requestPermission(K) reuses the same key. If the older batch for K resolves after the new request is enqueued and its stale result is returned from the cache, or if the newer request is discarded because only one request is queued for a key, PBAC re-evaluation can return the wrong answer for that resource/identifier. Tag each queued request with a per-key generation/version bumped in invalidateResource, and ignore stale resolutions or deconflict duplicate pending requests. Add a test for invalidateResource + fresh requestPermission while the earlier batch for the same key is still unresolved.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7ff26f83-89e6-4a9f-b48a-fb4ee0ce7c87

📥 Commits

Reviewing files that changed from the base of the PR and between 9c18743 and 4bed811.

📒 Files selected for processing (6)
  • documentation/release-notes/13.x.x/13.39.0/README.md
  • frontend/projects/valtimo/access-control/src/lib/services/permission.service.spec.ts
  • frontend/projects/valtimo/access-control/src/lib/services/permission.service.ts
  • frontend/projects/valtimo/case/src/lib/components/case-detail-task-detail/case-detail-task-detail.component.ts
  • frontend/projects/valtimo/task/src/lib/components/task-detail-modal/task-detail-modal.component.spec.ts
  • frontend/projects/valtimo/task/src/lib/components/task-detail-modal/task-detail-modal.component.ts

Comment on lines +201 to +220
private refreshTaskAfterAssignmentChange(taskId: string): Observable<any> {
// The new assignment can change the outcome of permission checks for this task,
// so drop cached permissions before the task is re-fetched and re-checked
this.permissionService.invalidateResource(TASK_DETAIL_PERMISSION_RESOURCE.task, taskId);

return this.taskService.getTask(taskId).pipe(
// a 403 or 404 means the user can no longer view the task
catchError(error => (error?.status === 403 || error?.status === 404 ? of(null) : EMPTY))
);
}

private handleAssignmentChangeResponse(response: any): void {
if (response) {
this.refreshTask(response);
} else {
this.onClose();
}
this.assignmentOfTaskChanged.emit();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -a 'case-detail-task-detail.component.spec.ts' frontend

Repository: valtimo-platform/valtimo

Length of output: 162


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== component/spec files matching case-detail-task-detail ====="
git ls-files | rg 'case-detail-task-detail\.component\.(ts|spec\.ts)$' || true

echo
echo "== spec files mentioning TaskDetailModalComponent / handleAssignmentChangeResponse / refreshTaskAfterAssignmentChange =="
rg -n "CaseDetailsTaskDetailComponent|TaskDetailModalComponent|handleAssignmentChangeResponse|refreshTaskAfterAssignmentChange|assignmentOfTaskChanged" frontend || true

echo
echo "== relevant component header/slice =="
FILE="frontend/projects/valtimo/case/src/lib/components/case-detail-task-detail/case-detail-task-detail.component.ts"
if [ -f "$FILE" ]; then
  wc -l "$FILE"
  sed -n '1,280p' "$FILE" | nl -ba
fi

echo
echo "== test files in nearby component area =="
dir="$(dirname "$FILE")"
fd -a '(\.spec\.ts|spec\.ts)$' "$dir" || true

Repository: valtimo-platform/valtimo

Length of output: 7882


Add coverage for the case detail assignment-change refresh flow.

frontend/projects/valtimo/case/src/lib/components/case-detail-task-detail/case-detail-task-detail.component.spec.ts is missing, while CaseDetailsTaskDetailComponent now runs refreshTaskAfterAssignmentChange() via the same assignment-change path.

Comment on lines +384 to +409
private refreshTaskAfterAssignmentChange(taskId: string): Observable<any> {
// The new assignment can change the outcome of permission checks for this task,
// so drop cached permissions before the task is re-fetched and re-checked
this.permissionService.invalidateResource(TASK_DETAIL_PERMISSION_RESOURCE.task, taskId);

return this.taskService.getTask(taskId).pipe(
catchError(error => {
// a 403 or 404 means the user can no longer view the task
if (error?.status === 403 || error?.status === 404) {
return of(null);
}
this.logger.error('Failed to fetch task after assignment change', error);
return EMPTY;
})
);
}

private handleAssignmentChangeResponse(response: any): void {
if (response) {
this.refreshTask(response);
} else {
this.closeModal();
}
this.assignmentOfTaskChanged.emit();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Unexpected re-fetch errors are silently swallowed in three near-duplicated places. The new "invalidate → refetch → treat 403/404 as unavailable" pattern was copy/pasted across two components without a shared implementation, and each copy maps any other error to EMPTY, so the pipe just completes with no downstream effect — no refresh, no close, no assignmentOfTaskChanged emit — leaving the user with a stale/stuck UI and no explanation. The duplication has already caused visible drift (one copy logs, one doesn't).

  • frontend/projects/valtimo/task/src/lib/components/task-detail-modal/task-detail-modal.component.ts#L384-L409: on non-403/404 errors, surface a user-facing failure (e.g. via the already-injected GlobalNotificationService) instead of just logging and silently no-oping.
  • frontend/projects/valtimo/task/src/lib/components/task-detail-modal/task-detail-modal.component.ts#L227-L245: same — the SSE handler logs the error but still leaves the modal open with no user feedback; give it the same failure handling.
  • frontend/projects/valtimo/case/src/lib/components/case-detail-task-detail/case-detail-task-detail.component.ts#L201-L210: inject a logger (none is currently available in this component) and apply the same user-facing failure handling; consider extracting the shared "invalidate + refetch + 403/404 handling" logic into a common helper so the two components can't diverge again.
♻️ Illustrative fix direction
     return this.taskService.getTask(taskId).pipe(
-      catchError(error => (error?.status === 403 || error?.status === 404 ? of(null) : EMPTY))
+      catchError(error => {
+        if (error?.status === 403 || error?.status === 404) {
+          return of(null);
+        }
+        // surface the failure instead of leaving the panel stuck with no feedback
+        return of(null);
+      })
     );
📍 Affects 2 files
  • frontend/projects/valtimo/task/src/lib/components/task-detail-modal/task-detail-modal.component.ts#L384-L409 (this comment)
  • frontend/projects/valtimo/task/src/lib/components/task-detail-modal/task-detail-modal.component.ts#L227-L245
  • frontend/projects/valtimo/case/src/lib/components/case-detail-task-detail/case-detail-task-detail.component.ts#L201-L210

@sofiaIvarsRitense

sofiaIvarsRitense commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Testing finding:
The button now is disabled after assigning a admin role but I still get the error

Video:

Screen.Recording.2026-07-28.at.14.52.18.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants