UNPKG

3.14 kBJavaScriptView Raw
1const pick = require('lodash/pick')
2const merge = require('lodash/merge')
3
4// TODO: @sssss465 I mean like this kind of shit
5async function fillResourceContext(args) {
6 if (!args.context._fetchResourceContextFromDB) return
7
8 const OGValue = args.context._authorize
9 args.context._authorize = false
10 args.context._resource = await args.asFindQuery()
11 args.context._authorize = OGValue
12}
13
14module.exports = (acl, library = 'role-acl', opts) => {
15 if (!acl || typeof library === 'object') {
16 throw new Error(
17 "usage: require('objection-authorize')(acl, library: String[, opts: Object])(Model)"
18 )
19 }
20
21 const defaultOpts = {
22 defaultRole: 'anonymous',
23 unauthenticatedErrorCode: 401,
24 unauthorizedErrorCode: 403,
25 casl: {
26 useInputItemAsResourceForRelation: false
27 }
28 }
29 opts = merge(defaultOpts, opts)
30
31 const Adapter = require(`./adapters/${library}`)
32
33 return Model => {
34 class AuthZQueryBuilder extends Model.QueryBuilder {
35 action(_action) {
36 return this.context({ _action })
37 }
38
39 authorize(user, resource, optOverride) {
40 return this.context({
41 _user: Object.assign({ role: opts.defaultRole }, user),
42 _opts: Object.assign({}, opts, optOverride),
43 _resource: resource,
44 _class: this.modelClass(),
45 _authorize: true
46 })
47 }
48
49 fetchResourceContextFromDB() {
50 return this.context({
51 _fetchResourceContextFromDB: true
52 })
53 }
54
55 diffInputFromResource() {
56 return this.context({
57 _diffInputFromResource: true
58 })
59 }
60 }
61
62 return class extends Model {
63 static get QueryBuilder() {
64 return AuthZQueryBuilder
65 }
66
67 static async beforeInsert(args) {
68 await super.beforeInsert(args)
69 new Adapter(acl, args, 'create').checkAccess()
70 }
71
72 static async beforeFind(args) {
73 await super.beforeFind(args)
74 new Adapter(acl, args, 'read').checkAccess()
75 }
76
77 static async beforeUpdate(args) {
78 await super.beforeUpdate(args)
79 await fillResourceContext(args)
80 new Adapter(acl, args, 'update').checkAccess()
81 }
82
83 static async beforeDelete(args) {
84 await super.beforeDelete(args)
85 await fillResourceContext(args)
86 new Adapter(acl, args, 'delete').checkAccess()
87 }
88
89 authorizeRead(user, action = 'read', optOverride) {
90 const args = {
91 items: [],
92 inputItems: [],
93 relation: '',
94 context: {
95 _user: Object.assign({ role: opts.defaultRole }, user),
96 _opts: Object.assign({}, opts, optOverride),
97 _action: action,
98 _resource: this,
99 _class: this.constructor,
100 _authorize: true
101 }
102 }
103
104 const fields = new Adapter(acl, args, action).allowedFields
105
106 // using lodash's implementation of pick instead of "internal" Objection.js one
107 // because the ORM's implementation doesn't support nested JSON:
108 // https://git.io/JLMsm
109 return pick(this.toJSON(), fields)
110 }
111 }
112 }
113}