Skip to content

refactor: casl factory - proposals#2779

Open
HayenNico wants to merge 1 commit into
refactor-casl-factory-cleanupfrom
refactor-casl-factory-proposals
Open

refactor: casl factory - proposals#2779
HayenNico wants to merge 1 commit into
refactor-casl-factory-cleanupfrom
refactor-casl-factory-proposals

Conversation

@HayenNico

@HayenNico HayenNico commented Jun 8, 2026

Copy link
Copy Markdown
Member

Description

Subsection of PR #2748 for proposals. PR depends on #2759 to be merged first.

This unifies the proposalsEndpointAccess and proposalsInstanceAccess functions in CaslAbilityFactory, removes instance-level Action elements and adjusts the affected controllers to accommodate the change. The proposal-specific code is extracted into a separate module.

Changes:

  • Replace CaslAbilityFactory.proposalsInstanceAccess and CaslAbilityFactory.proposalsEndpointAccess with one function CaslAbilityFactory.proposalAccess
  • Code for CaslAbilityFactory.proposalAccess is factored out into new module ProposalAbility
  • Remove all instance-level proposal Action elements, rename endpoint-level actions
  • Adjust endpoint and instance auth logic in proposal controller
  • Adjustments in datasets-access.service for proposal lookup (will need merge conflict resolution with datasets PR)

Tests included

  • Included for each change/fix?
  • Passing?

Documentation

  • swagger documentation updated (required for API changes)
  • official documentation updated

Summary by Sourcery

Unify and centralize proposal authorization logic in a new CASL ability, replacing legacy proposal-specific actions and access methods across controllers and services.

Enhancements:

  • Replace separate proposal endpoint and instance access methods with a single proposalAccess entry point backed by a dedicated ProposalAbility service.
  • Simplify proposal and dataset permission checks in controllers and datasets-access service to rely on generic AccessAny and consolidated proposal and attachment actions.
  • Introduce streamlined proposal-related CASL actions in the Action enum and remove legacy, fine-grained proposal action variants.
  • Wire ProposalAbility into the CASL module and ability factory for dependency injection and reuse.

Tests:

  • Update proposal controller unit tests to reflect the new proposalAccess behavior and revised filter constraints for unauthenticated and admin users.

@HayenNico HayenNico requested a review from a team as a code owner June 8, 2026 22:12

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In ProposalsController the /proposals/:pid/datasets endpoint now uses proposalAccess(user) but checks permissions on DatasetClass (e.g. ability.can(Action.AccessAny, DatasetClass) and Action.DatasetRead), which ProposalAbility does not define; this will effectively always deny or mis-evaluate dataset permissions and should be switched back to a dataset-specific ability (e.g. datasetInstanceAccess) or extended with dataset rules.
  • The changes in DatasetsAccessService.getRelationViewAccess and addRelationViewRestrictions mean unauthenticated users get canView* mapped from ProposalRead but the restriction logic now short-circuits when currentUser is falsy, resulting in no $match pipeline and potentially exposing non-public proposals via dataset lookups; consider restoring a public-only match (e.g. isPublished: true) for unauthenticated users.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `ProposalsController` the `/proposals/:pid/datasets` endpoint now uses `proposalAccess(user)` but checks permissions on `DatasetClass` (e.g. `ability.can(Action.AccessAny, DatasetClass)` and `Action.DatasetRead`), which `ProposalAbility` does not define; this will effectively always deny or mis-evaluate dataset permissions and should be switched back to a dataset-specific ability (e.g. `datasetInstanceAccess`) or extended with dataset rules.
- The changes in `DatasetsAccessService.getRelationViewAccess` and `addRelationViewRestrictions` mean unauthenticated users get `canView*` mapped from `ProposalRead` but the restriction logic now short-circuits when `currentUser` is falsy, resulting in no `$match` pipeline and potentially exposing non-public proposals via dataset lookups; consider restoring a public-only match (e.g. `isPublished: true`) for unauthenticated users.

## Individual Comments

### Comment 1
<location path="src/datasets/datasets-access.service.ts" line_range="147-150" />
<code_context>
       if (!canViewAny) {
         let pipeline: PipelineStage.Lookup["$lookup"]["pipeline"];
-        if (canViewAccess) {
+        if (currentUser && canViewAccess) {
           pipeline = [
             {
</code_context>
<issue_to_address>
**🚨 issue (security):** Guarding the lookup pipeline on `currentUser` can leave unauthenticated users with an unrestricted proposal lookup.

Because the `if` branches now require `currentUser`, when `currentUser` is falsy but `canViewAccess`/`canViewOwner` is true, `pipeline` stays `undefined` while `!canViewAny`. With `getRelationViewAccess` deriving these flags from `ProposalRead` (including the `isPublished` rule), unauthenticated users can end up with `canViewAccess === true` and `currentUser === undefined`, so the `$lookup` runs without any `isPublished` or group constraints.

That can either prevent unauthenticated users from seeing public proposals or, depending on the lookup shape, expose non‑public proposals. We should either:
- Keep and use a `canViewPublic` flag to always enforce `{ isPublished: true }` for unauthenticated users, or
- Add an explicit unauthenticated branch that builds a public‑only pipeline instead of gating on `currentUser`.

Given this directly affects data exposure, the unauthenticated case needs a more explicit, constrained pipeline.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/datasets/datasets-access.service.ts
@HayenNico

Copy link
Copy Markdown
Member Author

In ProposalsController the /proposals/:pid/datasets endpoint now uses proposalAccess(user) but checks permissions on DatasetClass (e.g. ability.can(Action.AccessAny, DatasetClass) and Action.DatasetRead), which ProposalAbility does not define; this will effectively always deny or mis-evaluate dataset permissions and should be switched back to a dataset-specific ability (e.g. datasetInstanceAccess) or extended with dataset rules.

This is true, but mirrors the current logic in ProposalsController. Due to the check for non-existing permissions, the endpoint always runs with the isPublished filter (so long as the user can call the endpoint with ProposalDatasetRead). Moving to a datasetAccess check would be a breaking change

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