Skip to content

Commit ce245e6

Browse files
build(deps): migrate @casl/ability v6 → v7
v7 renames PureAbility to Ability and drops the default conditions matcher; the historical MongoDB-matching Ability class no longer exists. Build abilities via createMongoAbility so condition rules ({ _id }, { organizationId }) keep matching — without this, authorization silently denies and endpoints return 403/422. Supersedes dependabot #3693. See MIGRATIONS.md for downstream notes.
1 parent 1b9f7a6 commit ce245e6

5 files changed

Lines changed: 60 additions & 27 deletions

File tree

MIGRATIONS.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,37 @@ Breaking changes and upgrade notes for downstream projects.
44

55
---
66

7+
## @casl/ability v6 → v7 (2026-05-22)
8+
9+
`@casl/ability` upgraded from `^6.8.1` to `^7.0.0`.
10+
11+
### What changed (this repo)
12+
13+
- **`lib/middlewares/policy.js`** — v7 renames `PureAbility` to `Ability` and **drops the default conditions matcher** from it; the historical MongoDB-matching `Ability` class no longer exists. `defineAbilityFor()` now builds via `createMongoAbility`:
14+
```js
15+
// before (v6)
16+
const { AbilityBuilder, Ability } = await import('@casl/ability');
17+
const { can, cannot, build } = new AbilityBuilder(Ability);
18+
// after (v7)
19+
const { AbilityBuilder, createMongoAbility } = await import('@casl/ability');
20+
const { can, cannot, build } = new AbilityBuilder(createMongoAbility);
21+
```
22+
Without this, conditions like `can('manage', 'Organization', { _id })` stop matching → authorization silently denies → endpoints return 403/422.
23+
- **JSDoc type refs** `import('@casl/ability').Ability``MongoAbility` (`lib/middlewares/policy.js`, `lib/helpers/abilities.js`).
24+
- **`package.json`**`@casl/ability` `^6.8.1``^7.0.0`.
25+
26+
### Downstream action required
27+
28+
The `policy.js` fix is a devkit-owned file → it arrives via `/update-stack` (`--theirs`). The **dependency bump does not auto-propagate** (`package.json` is `--ours`):
29+
30+
1. Bump `@casl/ability` to `^7.0.0` in `package.json` and reinstall.
31+
2. After `/update-stack`, verify `lib/middlewares/policy.js` (~line 95) reads `new AbilityBuilder(createMongoAbility)`.
32+
3. **Module policy files need no change** — they use `can`/`cannot` closures, never the `Ability` class.
33+
4. The serialized rules format is **unchanged** (`createMongoAbility` keeps the MongoQuery rule shape), so Node→client rule packing stays compatible.
34+
5. Run unit + integration + e2e to confirm authorization paths still pass.
35+
36+
---
37+
738
## Sentry removed — PostHog Error Tracking is now sole source (2026-05-10)
839

940
The `@sentry/node` integration shipped in 2026-03-26 (still documented below as **PostHog Analytics (2026-03-26)** + the now-removed Sentry monitoring section) is dropped. Error capture moves entirely to PostHog Error Tracking via `posthog.capture('$exception', ...)`.

lib/helpers/abilities.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* Serialize CASL abilities to a JSON-safe array compatible with createMongoAbility().
3-
* @param {import('@casl/ability').Ability} ability - The CASL ability instance
3+
* @param {import('@casl/ability').MongoAbility} ability - The CASL ability instance
44
* @returns {Array<{action: string, subject: string, conditions?: Object, inverted?: boolean}>} Array of serialized rules
55
*/
66
const serializeAbilities = (ability) =>

lib/middlewares/policy.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,13 @@ const registerAbilities = (entry) => {
8888
* Iterates over all registered ability builder functions from module policy files.
8989
* @param {Object|null} user - The authenticated user, or null/undefined for guests
9090
* @param {Object|null} [membership] - Optional organization membership (reserved for future use)
91-
* @returns {Promise<import('@casl/ability').Ability>} CASL ability instance
91+
* @returns {Promise<import('@casl/ability').MongoAbility>} CASL ability instance
9292
*/
9393
const defineAbilityFor = async (user, membership) => {
94-
const { AbilityBuilder, Ability } = await loadCasl();
95-
const { can, cannot, build } = new AbilityBuilder(Ability);
94+
// v7: the `Ability` export is now PureAbility (no default conditions matcher).
95+
// createMongoAbility restores MongoDB-style condition matching ({ _id }, { organizationId }).
96+
const { AbilityBuilder, createMongoAbility } = await loadCasl();
97+
const { can, cannot, build } = new AbilityBuilder(createMongoAbility);
9698

9799
// Normalize Mongoose membership to a plain object so all fields (role, status, etc.)
98100
// are accessible, and flatten populated organizationId to a plain string ID.

package-lock.json

Lines changed: 22 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"release:auto": "npx semantic-release"
5050
},
5151
"dependencies": {
52-
"@casl/ability": "^6.8.1",
52+
"@casl/ability": "^7.0.0",
5353
"@jest/globals": "^30.4.1",
5454
"axios": "^1.16.1",
5555
"bcrypt": "^6.0.0",

0 commit comments

Comments
 (0)