Created
August 10, 2020 12:52
-
-
Save stalniy/b710d65ac8a6c15f37a435c910624ef7 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
I am not sure I understand the significance of this line:
Why not do this: