UNPKG

3.25 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 castDiffToModelClass: true,
26 casl: {
27 useInputItemAsResourceForRelation: false
28 }
29 }
30 opts = merge({}, defaultOpts, opts)
31
32 const Adapter = require(`./adapters/${library}`)
33
34 return Model => {
35 class AuthZQueryBuilder extends Model.QueryBuilder {
36 action(_action) {
37 return this.context({ _action })
38 }
39
40 inputItem(_resource) {
41 return this.context({ _resource })
42 }
43
44 authorize(user, resource, optOverride) {
45 const _opts = merge({}, opts, optOverride)
46
47 return this.context({
48 _user: merge({ role: _opts.defaultRole }, user),
49 _opts,
50 _resource: resource,
51 _class: this.modelClass(),
52 _authorize: true
53 })
54 }
55
56 fetchResourceContextFromDB() {
57 return this.context({
58 _fetchResourceContextFromDB: true
59 })
60 }
61
62 diffInputFromResource() {
63 return this.context({
64 _diffInputFromResource: true
65 })
66 }
67 }
68
69 return class extends Model {
70 static get QueryBuilder() {
71 return AuthZQueryBuilder
72 }
73
74 static async beforeInsert(args) {
75 await super.beforeInsert(args)
76 new Adapter(acl, args, 'create').checkAccess()
77 }
78
79 static async beforeFind(args) {
80 await super.beforeFind(args)
81 new Adapter(acl, args, 'read').checkAccess()
82 }
83
84 static async beforeUpdate(args) {
85 await super.beforeUpdate(args)
86 await fillResourceContext(args)
87 new Adapter(acl, args, 'update').checkAccess()
88 }
89
90 static async beforeDelete(args) {
91 await super.beforeDelete(args)
92 await fillResourceContext(args)
93 new Adapter(acl, args, 'delete').checkAccess()
94 }
95
96 authorizeRead(user, action = 'read', optOverride) {
97 const args = {
98 items: [],
99 inputItems: [],
100 relation: '',
101 context: {
102 _user: merge({ role: opts.defaultRole }, user),
103 _opts: merge({}, opts, optOverride),
104 _action: action,
105 _resource: this,
106 _class: this.constructor,
107 _authorize: true
108 }
109 }
110
111 const fields = new Adapter(acl, args, action).allowedFields
112
113 // using lodash's implementation of pick instead of "internal" Objection.js one
114 // because the ORM's implementation doesn't support nested JSON:
115 // https://git.io/JLMsm
116 return pick(this.toJSON(), fields)
117 }
118 }
119 }
120}