Skip to content

Instantly share code, notes, and snippets.

@ygrishajev
Created February 2, 2024 11:03
Show Gist options
  • Save ygrishajev/9ef01444fdb5c386c43b6611400c0fc6 to your computer and use it in GitHub Desktop.
Save ygrishajev/9ef01444fdb5c386c43b6611400c0fc6 to your computer and use it in GitHub Desktop.
import { AnyAbility, ForbiddenError, Generics, Normalize } from '@casl/ability';
import { Rule } from '@casl/ability/dist/types/Rule';
import { rulesToQuery } from '@casl/ability/extra';
import { CompoundCondition } from '@ucast/core';
import type { Condition } from '@ucast/core/dist/types/Condition';
import {
allInterpreters,
createSqlInterpreter,
pg,
SqlQueryOptions,
} from '@ucast/sql';
import { knex } from 'knex';
const interpret = createSqlInterpreter(allInterpreters);
const OPS_INVERTED = {
eq: 'ne',
ne: 'eq',
gt: 'lte',
gte: 'lt',
lt: 'gte',
lte: 'gt',
in: 'nin',
nin: 'in',
};
knex.QueryBuilder.extend(
'accessibleBy',
function (
ability: AnyAbility,
action: Normalize<Generics<AnyAbility>['abilities']>[0],
subject: string,
) {
const [sql, replacements] = toKnexRawQuery(ability, action, subject);
if (sql === '()') {
return this;
}
return this.whereRaw(sql, replacements);
},
);
export const toKnexRawQuery = (
ability: any,
action: string,
subject: string,
): [string, ...any[]] => {
ForbiddenError.from(ability).throwUnlessCan(action, subject);
const { $and = [], $or = [] } = rulesToQuery(
ability,
action,
subject as never,
(rule: Rule<any, any>) => {
if (!rule.ast) {
throw new Error('Unable to create knex query without AST');
}
if (rule.inverted) {
return {
...rule.ast,
operator: OPS_INVERTED[rule.ast.operator],
};
}
return rule.ast;
},
) as { $and: Condition[]; $or: Condition[] };
const condition = new CompoundCondition('and', [
...$and,
new CompoundCondition('or', $or),
]);
const [sql, replacements] = interpret(condition, {
...(pg as unknown as SqlQueryOptions),
paramPlaceholder: () => '?',
});
return [sql, replacements];
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment