feat(sql): add CASE/WHEN operator for mixed inverted and direct rules#72
Draft
feat(sql): add CASE/WHEN operator for mixed inverted and direct rules#72
Conversation
Add a new `sqlCase` compound operator that generates CASE/WHEN SQL
statements to correctly handle the combination of inverted (cannot)
and direct (can) rules with priority ordering.
Each child of a CompoundCondition('case', [...]) is interpreted as:
- Direct conditions → WHEN condition THEN 1
- Not-wrapped conditions → WHEN condition THEN 0
The overall SQL is: (CASE WHEN ... ELSE 0 END = 1)
This ensures consistency with CASL's runtime priority-based rule
evaluation when translating to SQL queries.
Co-authored-by: stalniy <1159966+stalniy@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Handle inverted and direct rules with same condition in SQL
feat(sql): add CASE/WHEN operator for mixed inverted and direct rules
Mar 7, 2026
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #72 +/- ##
==========================================
+ Coverage 91.93% 92.06% +0.12%
==========================================
Files 12 12
Lines 248 252 +4
Branches 40 41 +1
==========================================
+ Hits 228 232 +4
Misses 12 12
Partials 8 8 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When CASL rules combine
canandcannotfor the same resource/attributes, translating to SQL via simple AND/OR produces logically incorrect queries (e.g.,NOT (id = 1) AND id = 1is always FALSE). The rule priority ordering from CASL's runtime is lost in translation.Changes
packages/sql/src/interpreters.ts— NewsqlCasecompound operator that interpretsCompoundCondition('case', [...])into aCASE/WHENSQL expression. Children wrapped innotbecomeWHEN ... THEN 0(inverted/cannot), direct children becomeWHEN ... THEN 1(can). Uses(CASE ... ELSE 0 END = 1)for cross-dialect compatibility.packages/sql/src/defaults.ts— Registered ascaseinallInterpreters.packages/sql/spec/interpreters.spec.ts— 7 test cases covering single/mixed/compound conditions and MySQL dialect.Usage
This is a 1:1 match with CASL's runtime evaluation semantics, as discussed in stalniy/casl#1010.
Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
https://api.github.com/repos/stalniy/casl/issues/comments/2576798050/home/REDACTED/work/_temp/ghcca-node/node/bin/node /home/REDACTED/work/_temp/ghcca-node/node/bin/node --enable-source-maps /home/REDACTED/work/_temp/copilot-developer-action-main/dist/index.js(http block)/usr/bin/curl curl -s REDACTED -H Accept: application/vnd.github.v3+json(http block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
This section details on the original issue you should resolve
<issue_title>Handling inverted and direct rules with same condition when interpreting SQL conditions </issue_title>
<issue_description>Hello!
First, thanks so much for this library! We've gotten a lot of use out of CASL so far. Up to this point, we've yet to convert resource collections of our REST API to be scoped based on the CASL ability instance for a given user (we're reliant on legacy code to do this). Making this leap is one of the last steps we need to take to deprecate our legacy permissions model implementation.
We use
knexand scope queries on REST resource collections before applying user-provided filters onto the query. We would instead like to use the interpreter in@ucast/sqlto process the Ability instance AST; producing a raw where constraint we can apply to our query builder.For additive permissions, everything seems to work as expected. However, we need to be able to support negative (inverted) permissions as well. The ability instance handles this great when querying permissions on individual resources. However, something is lost in translation in our processing of the underlying AST when producing SQL and after tinkering for a while and doing some research, we're left thinking this HAS to be a common problem others have solved.
I've created a simple example that demonstrates what we're trying to achieve:
Output when ordering is
cannotbeforecanOutput when ordering is
canbeforecannotA few important notes:
ability.cancheck behaves as expected. I will lose access if thecannotcomes last and I retain access ifcanis last.We've also come across https://gist.github.com/ygrishajev/9ef01444fdb5c386c43b6611400c0fc6 which uses
rulesToQueryto return AST nodes and constructs a compound condition manually. However, it seems to hit this same issue when there are inverted and non-inverted rules applied to a resource where you get a lossy translation to the database; differing from results you see callingability.can.</issue_description>
<agent_instructions>Implement handling of inverted rules with help of CASE/WHEN in SQL. add support for this in @ucast/sql</agent_instructions>
Comments on the Issue (you are @copilot in this section)
@stalniy I also always had hard time to translate such intersection to DB query and as a result this edge case has never been solved on casl side.The recommendation says to get rid of inverted rules as much as possible. If you do not use them then you don’t have to solve this edge case. </comment_new>
<comment_new>@stalniy
But it works better than it sounds from my message above. Because the issue appears only if we use the same property in
canandcannotrules. So the ...🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.