1 | const pick = require('lodash/pick')
|
2 | const merge = require('lodash/merge')
|
3 |
|
4 |
|
5 | async 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 |
|
14 | module.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 |
|
107 |
|
108 |
|
109 | return pick(this.toJSON(), fields)
|
110 | }
|
111 | }
|
112 | }
|
113 | }
|