-
-
Save grimen/ffc7ac8e21d6054f2ebd8deab69e2c33 to your computer and use it in GitHub Desktop.
CASL + Objection
This file contains 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
const { defineAbility } = require('@casl/ability'); | |
const { rulesToQuery } = require('@casl/ability/extra'); | |
const Knex = require('knex'); | |
const { Model } = require('objection'); | |
const { interpret } = require('@ucast/objection') | |
const { CompoundCondition } = require('@ucast/core') | |
const knex = Knex({ | |
client: 'sqlite3', | |
connection: ':memory:' | |
}); | |
Model.knex(knex); | |
class User extends Model { | |
static tableName = 'users' | |
static get relationMappings() { | |
return { | |
projects: { | |
relation: Model.HasManyRelation, | |
modelClass: Project, | |
join: { from: 'users.id', to: 'projects.user_id' } | |
} | |
} | |
} | |
} | |
class Project extends Model { | |
static tableName = 'projects' | |
static get relationMappings() { | |
return { | |
user: { | |
relation: Model.BelongsToOneRelation, | |
modelClass: User, | |
join: { from: 'users.id', to: 'projects.user_id' } | |
} | |
} | |
} | |
} | |
async function createSchema() { | |
await knex.schema.createTable('users', (table) => { | |
table.increments('id').primary(); | |
table.string('name'); | |
table.string('email'); | |
}); | |
await knex.schema.createTable('projects', (table) => { | |
table.increments('id').primary(); | |
table.string('name'); | |
table.integer('user_id').references('users.id'); | |
}) | |
} | |
const createAbility = user => defineAbility((can) => { | |
can('manage', User, { id: user.id }); | |
can('manage', Project, { user_id: user.id }); | |
}); | |
function toObjectionQuery(ability, action, query) { | |
const { $and = [], $or = [] } = rulesToQuery(ability, action, query.modelClass(), (rule) => { | |
if (!rule.ast) { | |
throw new Error('Unable to create Objection.Query without AST') | |
} | |
return rule.ast; | |
}); | |
const condition = new CompoundCondition('and', [ | |
...$and, | |
new CompoundCondition('or', $or) | |
]); | |
return interpret(condition, query); | |
} | |
async function setup() { | |
await createSchema(); | |
const user = await User.query().insert({ | |
name: 'John', | |
email: '[email protected]' | |
}); | |
const projects = await Promise.all([ | |
{ name: 'Project #1', user_id: user.id }, | |
{ name: 'Project #2', user_id: user.id + 1 }, | |
{ name: 'Project #3', user_id: user.id }, | |
{ name: 'Project #4', user_id: user.id + 2 }, | |
].map(record => Project.query().insert(record))); | |
return { user, projects }; | |
} | |
async function main() { | |
const { user } = await setup(); | |
const ability = createAbility(user); | |
const items = await toObjectionQuery(ability, 'read', Project.query()); | |
console.log(items) | |
} | |
main() | |
.then(() => process.exit(0)) | |
.catch(console.error) |
This file contains 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
{ | |
"name": "casl-objection-example", | |
"version": "0.0.1", | |
"scripts": { | |
"start": "node index.js" | |
}, | |
"author": "", | |
"license": "MIT", | |
"dependencies": { | |
"@casl/ability": "^5.0.0", | |
"@ucast/core": "^1.2.0", | |
"@ucast/objection": "^2.0.0", | |
"knex": "^0.21.2", | |
"objection": "^2.2.1", | |
"sqlite3": "^5.0.0" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
...and for Prisma: stalniy/casl#161