sumba 2.29.0 → 2.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/extend/bajo/hook.js +40 -42
- package/extend/bajo/intl/en-US.json +5 -5
- package/extend/bajo/intl/id.json +5 -5
- package/extend/dobo/feature/status.js +3 -1
- package/extend/dobo/fixture/site.json +1 -1
- package/extend/dobo/fixture/team-user.json +1 -1
- package/extend/dobo/fixture/team.json +2 -2
- package/extend/dobo/fixture/user.json +1 -1
- package/extend/dobo/model.js +82 -122
- package/extend/dobo/model.json +2 -9
- package/extend/sumba/route/anonymous.json +5 -0
- package/extend/sumba/route/secure.json +8 -0
- package/extend/waibuDb/schema/anonymous-guard.js +15 -0
- package/extend/waibuDb/schema/{route-guard.js → secure-guard.js} +2 -2
- package/extend/waibuDb/schema/team.json +1 -1
- package/extend/waibuMpa/extend/waibuAdmin/route/{x/model-guard → anonymous-guard}/@action.js +2 -3
- package/extend/waibuMpa/extend/waibuAdmin/route/{route-guard → secure-guard}/@action.js +2 -2
- package/extend/waibuMpa/route/access-token.js +3 -1
- package/extend/waibuRestApi/route/manage/anonymous-guard/model-builder.json +4 -0
- package/extend/waibuRestApi/route/manage/{route-guard → secure-guard}/model-builder.json +1 -1
- package/index.js +153 -98
- package/lib/get-user.js +1 -1
- package/lib/util.js +28 -67
- package/package.json +1 -1
- package/wiki/CHANGES.md +8 -0
- package/extend/dobo/fixture/x-route-guard.js +0 -24
- package/extend/waibuDb/schema/x-attrib-guard.js +0 -15
- package/extend/waibuDb/schema/x-model-guard.js +0 -15
- package/extend/waibuDb/schema/x-route-guard.js +0 -15
- package/extend/waibuMpa/extend/waibuAdmin/route/index.json +0 -3
- package/extend/waibuMpa/extend/waibuAdmin/route/x/attrib-guard/@action.js +0 -12
- package/extend/waibuMpa/extend/waibuAdmin/route/x/index.json +0 -3
- package/extend/waibuMpa/extend/waibuAdmin/route/x/route-guard/@action.js +0 -12
package/extend/bajo/hook.js
CHANGED
|
@@ -22,45 +22,30 @@ async function clearCacheUser (id, result) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
async function applyModelGuard ({ model, q, teamIds, options }) {
|
|
25
|
-
const { get, set, orderBy,
|
|
25
|
+
const { get, set, orderBy, without } = this.app.lib._
|
|
26
26
|
const { includes } = this.app.lib.aneka
|
|
27
27
|
const { req } = options
|
|
28
28
|
const results = []
|
|
29
29
|
|
|
30
30
|
const guards = await this.getModelGuards()
|
|
31
|
-
const
|
|
31
|
+
const fields = model.getNonVirtualProperties().map(prop => prop.name)
|
|
32
32
|
const filterFn = item => {
|
|
33
|
-
const bySiteId = item.
|
|
33
|
+
const bySiteId = item.siteId === req.site.id + ''
|
|
34
34
|
const byModel = item.models.includes(model.name)
|
|
35
|
-
const byTeamId =
|
|
36
|
-
const
|
|
37
|
-
return bySiteId && byModel && byTeamId &&
|
|
35
|
+
const byTeamId = includes(item.teamIds, teamIds)
|
|
36
|
+
const byFields = fields.includes(item.field)
|
|
37
|
+
return bySiteId && byModel && byTeamId && byFields
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
for (const col of columns) {
|
|
40
|
+
const rules = orderBy(guards.filter(filterFn), ['field'])
|
|
41
|
+
for (const field of fields) {
|
|
43
42
|
let values = []
|
|
44
|
-
|
|
43
|
+
const items = rules.filter(item => item.field === field)
|
|
45
44
|
for (const item of items) {
|
|
46
|
-
values
|
|
45
|
+
if (item.behavior === 'NIN') values = without(values, ...item.value)
|
|
46
|
+
else if (item.behavior === 'IN') values.push(...item.value)
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
for (const item of items) {
|
|
50
|
-
values = without(values, ...item.value)
|
|
51
|
-
}
|
|
52
|
-
items = guards.local.filter(item => item.column === col && !item.negation)
|
|
53
|
-
const newValues = []
|
|
54
|
-
for (const item of items) {
|
|
55
|
-
newValues.push(...item.value)
|
|
56
|
-
}
|
|
57
|
-
if (values.length === 0) values = newValues
|
|
58
|
-
else if (newValues.length > 0) values = intersection(values, newValues)
|
|
59
|
-
items = guards.local.filter(item => item.column === col && item.negation)
|
|
60
|
-
for (const item of items) {
|
|
61
|
-
values = without(values, ...item.value)
|
|
62
|
-
}
|
|
63
|
-
if (values.length) results.push(set({}, col, { $in: values }))
|
|
48
|
+
if (values.length) results.push(set({}, field, { $in: values }))
|
|
64
49
|
}
|
|
65
50
|
|
|
66
51
|
const allowEmpty = get(this, `config.dobo.model.${model.name}.allowEmptyQuery`, true)
|
|
@@ -76,16 +61,14 @@ export async function applyAttribGuard ({ model, teamIds, options }) {
|
|
|
76
61
|
|
|
77
62
|
const guards = await this.getAttribGuards()
|
|
78
63
|
const filterFn = item => {
|
|
79
|
-
const bySiteId = item.
|
|
64
|
+
const bySiteId = item.siteId === req.site.id + ''
|
|
80
65
|
const byModel = item.models.includes(model.name)
|
|
81
|
-
const byTeamId =
|
|
66
|
+
const byTeamId = includes(item.teamIds, teamIds)
|
|
82
67
|
return bySiteId && byModel && byTeamId
|
|
83
68
|
}
|
|
84
69
|
|
|
85
|
-
|
|
86
|
-
if (item) results.push(...item.
|
|
87
|
-
item = guards.local.filter(filterFn)[0]
|
|
88
|
-
if (item) results.push(...item.hiddenCols)
|
|
70
|
+
const item = guards.filter(filterFn)[0]
|
|
71
|
+
if (item) results.push(...item.hiddenFields)
|
|
89
72
|
options.hidden = options.hidden ?? []
|
|
90
73
|
if (results.length > 0) options.hidden.push(...results)
|
|
91
74
|
options.hidden = uniq(options.hidden)
|
|
@@ -168,7 +151,7 @@ async function hook () {
|
|
|
168
151
|
}
|
|
169
152
|
}
|
|
170
153
|
}, {
|
|
171
|
-
name: ['dobo.sumbaAttribGuard:afterTransaction'
|
|
154
|
+
name: ['dobo.sumbaAttribGuard:afterTransaction'],
|
|
172
155
|
handler: async function (action, ...args) {
|
|
173
156
|
if (!['createRecord', 'updateRecord', 'removeRecord'].includes(action)) return
|
|
174
157
|
await this.getAttribGuards(true)
|
|
@@ -205,16 +188,22 @@ async function hook () {
|
|
|
205
188
|
options.checksumId = true
|
|
206
189
|
}
|
|
207
190
|
}, {
|
|
208
|
-
name: ['dobo.sumbaModelGuard:afterTransaction'
|
|
191
|
+
name: ['dobo.sumbaModelGuard:afterTransaction'],
|
|
209
192
|
handler: async function (action, ...args) {
|
|
210
193
|
if (!['createRecord', 'updateRecord', 'removeRecord'].includes(action)) return
|
|
211
194
|
await this.getModelGuards(true)
|
|
212
195
|
}
|
|
213
196
|
}, {
|
|
214
|
-
name: ['dobo.
|
|
197
|
+
name: ['dobo.sumbaSecureGuard:afterTransaction'],
|
|
215
198
|
handler: async function (action, ...args) {
|
|
216
199
|
if (!['createRecord', 'updateRecord', 'removeRecord'].includes(action)) return
|
|
217
|
-
await this.
|
|
200
|
+
await this.getSecureGuards(true)
|
|
201
|
+
}
|
|
202
|
+
}, {
|
|
203
|
+
name: ['dobo.sumbaAnonymousGuard:afterTransaction'],
|
|
204
|
+
handler: async function (action, ...args) {
|
|
205
|
+
if (!['createRecord', 'updateRecord', 'removeRecord'].includes(action)) return
|
|
206
|
+
await this.getAnonymousGuards(true)
|
|
218
207
|
}
|
|
219
208
|
}, {
|
|
220
209
|
name: 'dobo.sumbaSite:afterCreateRecord',
|
|
@@ -237,7 +226,7 @@ async function hook () {
|
|
|
237
226
|
name: ['dobo.sumbaTeam:afterTransaction', 'dobo.sumbaSite:afterTransaction'],
|
|
238
227
|
handler: async function (action, ...args) {
|
|
239
228
|
if (!['createRecord', 'updateRecord', 'removeRecord'].includes(action)) return
|
|
240
|
-
await this.
|
|
229
|
+
await this.getSecureGuards(true)
|
|
241
230
|
await this.getModelGuards(true)
|
|
242
231
|
await this.getAttribGuards(true)
|
|
243
232
|
}
|
|
@@ -353,7 +342,7 @@ async function hook () {
|
|
|
353
342
|
await checkIconset.call(this, req, reply)
|
|
354
343
|
const secure = await checkUserId.call(this, req, reply, 'waibuMpa')
|
|
355
344
|
if (!secure) return
|
|
356
|
-
await checkTeam.call(this, req, reply
|
|
345
|
+
await checkTeam.call(this, req, reply)
|
|
357
346
|
await checkXSite.call(this, req, reply)
|
|
358
347
|
}
|
|
359
348
|
}, {
|
|
@@ -362,7 +351,7 @@ async function hook () {
|
|
|
362
351
|
handler: async function (req, reply) {
|
|
363
352
|
const secure = await checkUserId.call(this, req, reply, 'waibuRestApi')
|
|
364
353
|
if (!secure) return
|
|
365
|
-
await checkTeam.call(this, req, reply
|
|
354
|
+
await checkTeam.call(this, req, reply)
|
|
366
355
|
await checkXSite.call(this, req, reply)
|
|
367
356
|
}
|
|
368
357
|
}, {
|
|
@@ -371,13 +360,14 @@ async function hook () {
|
|
|
371
360
|
handler: async function (req, reply) {
|
|
372
361
|
const secure = await checkUserId.call(this, req, reply, 'waibuStatic')
|
|
373
362
|
if (!secure) return
|
|
374
|
-
await checkTeam.call(this, req, reply
|
|
363
|
+
await checkTeam.call(this, req, reply)
|
|
375
364
|
await checkXSite.call(this, req, reply)
|
|
376
365
|
}
|
|
377
366
|
}, {
|
|
378
367
|
name: 'waibu:afterAppBoot',
|
|
379
368
|
handler: async function () {
|
|
380
|
-
await this.
|
|
369
|
+
await this.getAnonymousGuards(true)
|
|
370
|
+
await this.getSecureGuards(true)
|
|
381
371
|
await this.getModelGuards(true)
|
|
382
372
|
await this.getAttribGuards(true)
|
|
383
373
|
}
|
|
@@ -392,6 +382,14 @@ async function hook () {
|
|
|
392
382
|
handler: async function (req, reply) {
|
|
393
383
|
const { getHostname } = this.app.waibu
|
|
394
384
|
req.site = await this.getSite(getHostname(req))
|
|
385
|
+
req.user = {}
|
|
386
|
+
}
|
|
387
|
+
}, {
|
|
388
|
+
name: 'waibu:beforeStart',
|
|
389
|
+
handler: async function () {
|
|
390
|
+
if (this.app.sumba.config.multiSite.enabled) return
|
|
391
|
+
const routes = ['waibuAdmin:/site/x/site/*']
|
|
392
|
+
this.app.waibu.config.route.disabled.push(...routes)
|
|
395
393
|
}
|
|
396
394
|
}]
|
|
397
395
|
}
|
|
@@ -132,15 +132,14 @@
|
|
|
132
132
|
"statusClosed": "Closed",
|
|
133
133
|
"allSites": "All Sites",
|
|
134
134
|
"permission": "Permission",
|
|
135
|
-
"
|
|
135
|
+
"anonymousGuard": "Anonymous Guard",
|
|
136
|
+
"secureGuard": "Secure Guard",
|
|
136
137
|
"modelGuard": "Model Guard",
|
|
137
138
|
"attribGuard": "Attribute Guard",
|
|
138
|
-
"xRouteGuard": "Route Guard",
|
|
139
|
-
"xModelGuard": "Model Guard",
|
|
140
|
-
"xAttribGuard": "Attribute Guard",
|
|
141
139
|
"xSite": "Cross-Site",
|
|
142
140
|
"xSiteAdminArea": "Cross-Site Admin Area",
|
|
143
141
|
"x": "Cross-Site",
|
|
142
|
+
"misc": "Miscellaneous",
|
|
144
143
|
"field": {
|
|
145
144
|
"currentPassword": "Current Password",
|
|
146
145
|
"newPassword": "New Password",
|
|
@@ -173,7 +172,8 @@
|
|
|
173
172
|
"column": "Column",
|
|
174
173
|
"hiddenCols": "Hidden Columns",
|
|
175
174
|
"models": "Models",
|
|
176
|
-
"siteIds": "Sites"
|
|
175
|
+
"siteIds": "Sites",
|
|
176
|
+
"allTeams": "All Teams?"
|
|
177
177
|
},
|
|
178
178
|
"validation": {
|
|
179
179
|
"password": {
|
package/extend/bajo/intl/id.json
CHANGED
|
@@ -138,15 +138,14 @@
|
|
|
138
138
|
"statusClosed": "Tertutup",
|
|
139
139
|
"allSites": "Semua Situs",
|
|
140
140
|
"permission": "Permisi",
|
|
141
|
-
"
|
|
141
|
+
"anonymousGuard": "Pelindung Rute Anonim",
|
|
142
|
+
"secureGuard": "Pelindung Rute Aman",
|
|
142
143
|
"modelGuard": "Pelindung Model",
|
|
143
144
|
"attribGuard": "Pelindung Atribut",
|
|
144
|
-
"xRouteGuard": "Pelindung Rute",
|
|
145
|
-
"xModelGuard": "Pelindung Model",
|
|
146
|
-
"xAttribGuard": "Pelindung Atribut",
|
|
147
145
|
"xSite": "Antar Situs",
|
|
148
146
|
"xSiteAdminArea": "Area Admin Antar Situs",
|
|
149
147
|
"x": "Antar Situs",
|
|
148
|
+
"misc": "Lain-lain",
|
|
150
149
|
"field": {
|
|
151
150
|
"currentPassword": "Kata Sandi Saat Ini",
|
|
152
151
|
"newPassword": "Kata Sandi Baru",
|
|
@@ -179,7 +178,8 @@
|
|
|
179
178
|
"column": "Kolom",
|
|
180
179
|
"hiddenCols": "Kolom Tersembunyi",
|
|
181
180
|
"models": "Model",
|
|
182
|
-
"siteIds": "Situs"
|
|
181
|
+
"siteIds": "Situs",
|
|
182
|
+
"allTeams": "Semua Tim?"
|
|
183
183
|
},
|
|
184
184
|
"validation": {
|
|
185
185
|
"password": {
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
async function status (opts = {}) {
|
|
2
2
|
opts.field = opts.field ?? 'status'
|
|
3
3
|
opts.required = opts.required ?? true
|
|
4
|
-
opts.
|
|
4
|
+
opts.default = opts.default ?? 'ACTIVE'
|
|
5
|
+
opts.values = opts.values ?? ['ACTIVE', 'INACTIVE']
|
|
5
6
|
return {
|
|
6
7
|
properties: [{
|
|
7
8
|
name: opts.field ?? 'status',
|
|
8
9
|
type: 'string',
|
|
9
10
|
maxLength: 50,
|
|
10
11
|
index: true,
|
|
12
|
+
default: opts.default,
|
|
11
13
|
required: opts.required,
|
|
12
14
|
values: opts.values
|
|
13
15
|
}],
|
package/extend/dobo/model.js
CHANGED
|
@@ -3,142 +3,102 @@ const buildEnd = async function (model) {
|
|
|
3
3
|
if (prop) prop.values = this.getModelNames(true)
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
const mgProperties = [
|
|
7
|
-
{
|
|
8
|
-
name: 'models',
|
|
9
|
-
type: 'array',
|
|
10
|
-
required: true
|
|
11
|
-
},
|
|
12
|
-
'column,,50,true,true',
|
|
13
|
-
{
|
|
14
|
-
name: 'negation',
|
|
15
|
-
type: 'boolean',
|
|
16
|
-
required: true,
|
|
17
|
-
default: false
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
name: 'status',
|
|
21
|
-
type: 'sumba:status',
|
|
22
|
-
values: ['ACTIVE', 'INACTIVE'],
|
|
23
|
-
default: 'ACTIVE'
|
|
24
|
-
},
|
|
25
|
-
'value,array,,,true',
|
|
26
|
-
'siteId,sumba:siteId',
|
|
27
|
-
'teamIds,sumba:teamIds'
|
|
28
|
-
]
|
|
29
|
-
|
|
30
6
|
const options = {
|
|
31
7
|
attachment: false,
|
|
32
8
|
cache: { ttlDur: 0 }
|
|
33
9
|
}
|
|
34
10
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
name: 'models',
|
|
42
|
-
type: 'array',
|
|
43
|
-
required: true
|
|
44
|
-
},
|
|
45
|
-
'hiddenCols,array',
|
|
46
|
-
'siteId,sumba:siteId',
|
|
47
|
-
'teamIds,sumba:teamIds'
|
|
48
|
-
]
|
|
49
|
-
|
|
50
|
-
const agFeatures = [
|
|
51
|
-
{
|
|
52
|
-
name: 'sumba:status',
|
|
53
|
-
values: ['ACTIVE', 'INACTIVE'],
|
|
54
|
-
default: 'ACTIVE'
|
|
55
|
-
},
|
|
56
|
-
'dobo:updatedAt'
|
|
57
|
-
]
|
|
58
|
-
|
|
59
|
-
export const rgProperties = [
|
|
60
|
-
'path,,255,true,true',
|
|
61
|
-
{
|
|
62
|
-
name: 'methods',
|
|
63
|
-
type: 'array',
|
|
64
|
-
required: true,
|
|
65
|
-
default: ['GET', 'POST', 'UPDATE', 'DELETE'],
|
|
66
|
-
values: ['GET', 'POST', 'UPDATE', 'DELETE']
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
name: 'weight',
|
|
70
|
-
type: 'smallint',
|
|
71
|
-
default: 0
|
|
72
|
-
}, {
|
|
73
|
-
name: 'negation',
|
|
74
|
-
type: 'boolean',
|
|
75
|
-
required: true,
|
|
76
|
-
default: false
|
|
77
|
-
}, {
|
|
78
|
-
name: 'anonymous',
|
|
79
|
-
type: 'boolean',
|
|
80
|
-
required: true,
|
|
81
|
-
default: false
|
|
82
|
-
},
|
|
83
|
-
'siteId,sumba:siteId',
|
|
84
|
-
'teamIds,sumba:teamIds'
|
|
85
|
-
]
|
|
11
|
+
const allTeams = {
|
|
12
|
+
name: 'allTeams',
|
|
13
|
+
type: 'boolean',
|
|
14
|
+
default: true,
|
|
15
|
+
required: true
|
|
16
|
+
}
|
|
86
17
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
18
|
+
const routeGuard = {
|
|
19
|
+
properties: [
|
|
20
|
+
{
|
|
21
|
+
name: 'path',
|
|
22
|
+
maxLength: 255,
|
|
23
|
+
required: true
|
|
24
|
+
},
|
|
25
|
+
allTeams,
|
|
26
|
+
'teamIds,sumba:teamIds',
|
|
27
|
+
{
|
|
28
|
+
name: 'weight',
|
|
29
|
+
type: 'smallint',
|
|
30
|
+
index: true,
|
|
31
|
+
default: 0
|
|
32
|
+
},
|
|
33
|
+
'siteId,sumba:siteId'
|
|
34
|
+
],
|
|
35
|
+
features: [
|
|
36
|
+
'sumba:status',
|
|
37
|
+
'dobo:updatedAt',
|
|
38
|
+
'dobo:immutable'
|
|
39
|
+
],
|
|
40
|
+
indexes: [{
|
|
41
|
+
type: 'unique',
|
|
42
|
+
fields: ['siteId', 'path']
|
|
43
|
+
}],
|
|
44
|
+
options: { ...options }
|
|
45
|
+
}
|
|
95
46
|
|
|
96
47
|
async function model () {
|
|
97
|
-
const {
|
|
48
|
+
const { merge, cloneDeep } = this.app.lib._
|
|
98
49
|
return [{
|
|
99
|
-
baseName: 'route-guard',
|
|
100
|
-
properties: rgProperties,
|
|
101
|
-
features: rgFeatures,
|
|
102
|
-
options
|
|
103
|
-
}, {
|
|
104
|
-
baseName: 'x-route-guard',
|
|
105
|
-
properties: rgProperties.filter(prop => {
|
|
106
|
-
if (!isString(prop)) return true
|
|
107
|
-
return !(prop.startsWith('teamIds') || prop.startsWith('siteId'))
|
|
108
|
-
}).concat('siteIds,sumba:siteIds'),
|
|
109
|
-
features: rgFeatures,
|
|
110
|
-
options
|
|
111
|
-
}, {
|
|
112
50
|
baseName: 'attrib-guard',
|
|
113
|
-
properties:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
51
|
+
properties: [
|
|
52
|
+
{
|
|
53
|
+
name: 'models',
|
|
54
|
+
type: 'array',
|
|
55
|
+
required: true
|
|
56
|
+
},
|
|
57
|
+
'hiddenFields,array',
|
|
58
|
+
allTeams,
|
|
59
|
+
'teamIds,sumba:teamIds',
|
|
60
|
+
'siteId,sumba:siteId'
|
|
61
|
+
],
|
|
62
|
+
features: [
|
|
63
|
+
'sumba:status',
|
|
64
|
+
'dobo:updatedAt',
|
|
65
|
+
'dobo:immutable'
|
|
66
|
+
],
|
|
67
|
+
options: { ...options },
|
|
125
68
|
buildEnd
|
|
126
69
|
}, {
|
|
127
70
|
baseName: 'model-guard',
|
|
128
|
-
properties:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
71
|
+
properties: [
|
|
72
|
+
{
|
|
73
|
+
name: 'models',
|
|
74
|
+
type: 'array',
|
|
75
|
+
required: true
|
|
76
|
+
},
|
|
77
|
+
'field,,50,true,true',
|
|
78
|
+
{
|
|
79
|
+
name: 'behavior',
|
|
80
|
+
type: 'string',
|
|
81
|
+
maxLength: 20,
|
|
82
|
+
required: true,
|
|
83
|
+
values: ['IN', 'NIN'],
|
|
84
|
+
default: 'IN'
|
|
85
|
+
},
|
|
86
|
+
'value,array,,,true',
|
|
87
|
+
allTeams,
|
|
88
|
+
'teamIds,sumba:teamIds',
|
|
89
|
+
'siteId,sumba:siteId'
|
|
90
|
+
],
|
|
91
|
+
features: [
|
|
92
|
+
'sumba:status',
|
|
93
|
+
'dobo:updatedAt',
|
|
94
|
+
'dobo:immutable'
|
|
95
|
+
],
|
|
96
|
+
options: { ...options },
|
|
140
97
|
buildEnd
|
|
141
|
-
},
|
|
98
|
+
},
|
|
99
|
+
merge(cloneDeep(routeGuard), { baseName: 'anonymous-guard' }),
|
|
100
|
+
merge(cloneDeep(routeGuard), { baseName: 'secure-guard' }),
|
|
101
|
+
{
|
|
142
102
|
buildLevel: 2,
|
|
143
103
|
baseName: 'user',
|
|
144
104
|
properties: [{
|
package/extend/dobo/model.json
CHANGED
|
@@ -88,10 +88,7 @@
|
|
|
88
88
|
"sumba:personInCharge",
|
|
89
89
|
"sumba:address",
|
|
90
90
|
"sumba:social",
|
|
91
|
-
|
|
92
|
-
"name": "sumba:status",
|
|
93
|
-
"default": "ACTIVE"
|
|
94
|
-
},
|
|
91
|
+
"sumba:status",
|
|
95
92
|
"dobo:createdAt",
|
|
96
93
|
"dobo:updatedAt",
|
|
97
94
|
"dobo:immutable"
|
|
@@ -146,11 +143,7 @@
|
|
|
146
143
|
"dobo:updatedAt",
|
|
147
144
|
"dobo:immutable",
|
|
148
145
|
"sumba:siteId",
|
|
149
|
-
|
|
150
|
-
"name": "sumba:status",
|
|
151
|
-
"default": "ENABLED",
|
|
152
|
-
"values": ["ENABLED", "DISABLED"]
|
|
153
|
-
},
|
|
146
|
+
"sumba:status",
|
|
154
147
|
"dobo:immutable"
|
|
155
148
|
]
|
|
156
149
|
}, {
|
package/extend/waibuMpa/extend/waibuAdmin/route/{x/model-guard → anonymous-guard}/@action.js
RENAMED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
const action = {
|
|
2
2
|
method: ['GET', 'POST'],
|
|
3
|
-
title: '
|
|
4
|
-
xSite: true,
|
|
3
|
+
title: 'anonymousGuard',
|
|
5
4
|
handler: async function (req, reply) {
|
|
6
5
|
const { importModule } = this.app.bajo
|
|
7
6
|
const crudSkel = await importModule('waibuAdmin:/lib/crud-skel.js')
|
|
8
|
-
return await crudSkel.call(this, '
|
|
7
|
+
return await crudSkel.call(this, 'SumbaAnonymousGuard', req, reply)
|
|
9
8
|
}
|
|
10
9
|
}
|
|
11
10
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const action = {
|
|
2
2
|
method: ['GET', 'POST'],
|
|
3
|
-
title: '
|
|
3
|
+
title: 'secureGuard',
|
|
4
4
|
handler: async function (req, reply) {
|
|
5
5
|
const { importModule } = this.app.bajo
|
|
6
6
|
const crudSkel = await importModule('waibuAdmin:/lib/crud-skel.js')
|
|
7
|
-
return await crudSkel.call(this, '
|
|
7
|
+
return await crudSkel.call(this, 'SumbaSecureGuard', req, reply)
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const apiToken = {
|
|
2
2
|
method: 'POST',
|
|
3
3
|
handler: async function (req, reply) {
|
|
4
|
-
|
|
4
|
+
const { get } = this.app.lib._
|
|
5
|
+
const uid = get(req, 'user.id')
|
|
6
|
+
if (!uid) return ''
|
|
5
7
|
const rec = await this.app.dobo.getModel('SumbaUser').getRecord(req.user.id, { forceNoHidden: true, noCache: true })
|
|
6
8
|
return (await this.createJwtFromUserRecord(rec)).token
|
|
7
9
|
}
|
package/index.js
CHANGED
|
@@ -20,7 +20,7 @@ const defMultiSite = {
|
|
|
20
20
|
async function factory (pkgName) {
|
|
21
21
|
const me = this
|
|
22
22
|
const { getModel } = this.app.dobo
|
|
23
|
-
const { cloneDeep,
|
|
23
|
+
const { cloneDeep, isEmpty } = this.app.lib._
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Sumba class
|
|
@@ -52,6 +52,14 @@ async function factory (pkgName) {
|
|
|
52
52
|
'/help': 'sumba:/help/contact-form',
|
|
53
53
|
'/help/trouble-tickets': 'sumba:/help/trouble-tickets/list'
|
|
54
54
|
},
|
|
55
|
+
redirectSubRoute: {
|
|
56
|
+
waibuAdmin: {
|
|
57
|
+
'/': 'waibuAdmin:/site/site',
|
|
58
|
+
'/x': 'waibuAdmin:/site/x/site/list',
|
|
59
|
+
'/x/*': 'waibuAdmin:/site/x/{2}/list',
|
|
60
|
+
'/*': 'waibuAdmin:/site/{1}/list'
|
|
61
|
+
}
|
|
62
|
+
},
|
|
55
63
|
menuHandler: [{
|
|
56
64
|
title: 'account',
|
|
57
65
|
icon: 'person',
|
|
@@ -155,9 +163,11 @@ async function factory (pkgName) {
|
|
|
155
163
|
getUserByTokenDur: '1m'
|
|
156
164
|
}
|
|
157
165
|
}
|
|
158
|
-
this.
|
|
159
|
-
this.modelGuards =
|
|
160
|
-
this.attribGuards =
|
|
166
|
+
this.secureGuards = []
|
|
167
|
+
this.modelGuards = []
|
|
168
|
+
this.attribGuards = []
|
|
169
|
+
this.secureGuards = []
|
|
170
|
+
this.anonymousGuards = []
|
|
161
171
|
|
|
162
172
|
this.unsafeUserFields = ['password']
|
|
163
173
|
this.selfBind(['createNewSite', 'removeSite', 'getSite', 'getUserById', 'getUserByToken', 'getUserByUsernamePassword'])
|
|
@@ -174,7 +184,11 @@ async function factory (pkgName) {
|
|
|
174
184
|
}
|
|
175
185
|
|
|
176
186
|
start = async () => {
|
|
177
|
-
|
|
187
|
+
await this.populateRouteGuards()
|
|
188
|
+
if (!this.config.multiSite.enabled) {
|
|
189
|
+
this.config.xSiteAdmins = []
|
|
190
|
+
return
|
|
191
|
+
}
|
|
178
192
|
if (this.config.xSiteAdmins.length === 0) {
|
|
179
193
|
const site = await getModel('SumbaSite').findOneRecord({ query: { alias: 'default' } }, { noMagic: true })
|
|
180
194
|
const user = await getModel('SumbaUser').findOneRecord({ query: { username: 'admin', siteId: site.id } }, { noMagic: true })
|
|
@@ -182,6 +196,84 @@ async function factory (pkgName) {
|
|
|
182
196
|
}
|
|
183
197
|
}
|
|
184
198
|
|
|
199
|
+
populateRouteGuards = async () => {
|
|
200
|
+
const { isString, get, difference } = this.app.lib._
|
|
201
|
+
const { pascalCase } = this.app.lib.aneka
|
|
202
|
+
const { eachPlugins, readConfig, breakNsPath } = this.app.bajo
|
|
203
|
+
const { getModel } = this.app.dobo
|
|
204
|
+
|
|
205
|
+
const allNs = this.app.getAllNs()
|
|
206
|
+
const sites = await getModel('SumbaSite').findAllRecord({ query: { status: 'ACTIVE' } }, { noMagic: true, dataOnly: true, fields: ['id', 'hostname'] })
|
|
207
|
+
|
|
208
|
+
const sanitize = (item, ns) => {
|
|
209
|
+
if (isString(item)) item = { path: item }
|
|
210
|
+
let [prefix, ...args] = item.path.split(':')
|
|
211
|
+
const neg = prefix[0] === '!'
|
|
212
|
+
if (neg) prefix = prefix.slice(1)
|
|
213
|
+
if (isEmpty(args)) {
|
|
214
|
+
args = prefix
|
|
215
|
+
prefix = null
|
|
216
|
+
} else args = args.join(':')
|
|
217
|
+
if (isEmpty(prefix)) prefix = ns
|
|
218
|
+
else {
|
|
219
|
+
const [_ns, subNs] = prefix.split('.')
|
|
220
|
+
if (subNs) prefix = `${ns}.${subNs}`
|
|
221
|
+
else if (!allNs.includes(_ns)) prefix = `${ns}.${_ns}`
|
|
222
|
+
else prefix = _ns
|
|
223
|
+
}
|
|
224
|
+
item.path = `${neg ? '!' : ''}${prefix}:${args}`
|
|
225
|
+
item._immutable = item._immutable ?? ['path']
|
|
226
|
+
if (neg) {
|
|
227
|
+
item.allTeams = true
|
|
228
|
+
item.teamIds = []
|
|
229
|
+
item._immutable = ['path', 'teamIds', 'allTeams']
|
|
230
|
+
}
|
|
231
|
+
return item
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const filterFn = item => {
|
|
235
|
+
let [ns] = (item.path.split(':')[0] ?? '').split('.')
|
|
236
|
+
if (ns[0] === '!') ns = ns.slice(1)
|
|
237
|
+
return allNs.includes(ns)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
for (const type of ['secure', 'anonymous']) {
|
|
241
|
+
const routes = []
|
|
242
|
+
// get it from <pluginDir>/extend/sumba/route/*
|
|
243
|
+
await eachPlugins(async function ({ file }) {
|
|
244
|
+
const { ns } = this
|
|
245
|
+
const items = (await readConfig(file)).map(item => sanitize(item, ns)).filter(filterFn)
|
|
246
|
+
routes.push(...items)
|
|
247
|
+
}, { glob: `route/${type}.*`, prefix: this.ns })
|
|
248
|
+
// get it from config
|
|
249
|
+
const items = get(this, `config.route.${type}`, []).map(item => {
|
|
250
|
+
if (isString(item)) item = { path: item }
|
|
251
|
+
const neg = item.path[0] === '!'
|
|
252
|
+
if (neg) item.path.slice(1)
|
|
253
|
+
const { fullNs } = breakNsPath(item.path)
|
|
254
|
+
if (neg) item.path = '!' + item.path
|
|
255
|
+
return sanitize(item, fullNs)
|
|
256
|
+
}).filter(filterFn)
|
|
257
|
+
routes.push(...items)
|
|
258
|
+
const paths = routes.map(item => item.path)
|
|
259
|
+
const model = getModel(pascalCase(`Sumba ${type} Guard`))
|
|
260
|
+
for (const site of sites) {
|
|
261
|
+
const query = { path: { $in: paths }, siteId: site.id }
|
|
262
|
+
const recs = await model.findAllRecord({ query }, { noMagic: true, dataOnly: true, fields: ['path', 'status'] })
|
|
263
|
+
const spaths = difference(paths, recs.map(rec => rec.path))
|
|
264
|
+
for (const path of spaths) {
|
|
265
|
+
const body = cloneDeep(routes.find(r => r.path === path))
|
|
266
|
+
body.status = 'ACTIVE'
|
|
267
|
+
body.siteId = site.id
|
|
268
|
+
await model.sanitizeFixture({ body, lookupValue: body })
|
|
269
|
+
try {
|
|
270
|
+
await model.createRecord(body, { noMagic: true, noReturn: true })
|
|
271
|
+
} catch (err) {}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
185
277
|
_getSetting = async (type, source) => {
|
|
186
278
|
const { defaultsDeep } = this.app.lib.aneka
|
|
187
279
|
const { get } = this.app.lib._
|
|
@@ -208,18 +300,10 @@ async function factory (pkgName) {
|
|
|
208
300
|
adminMenu = async (locals, req) => {
|
|
209
301
|
if (!this.app.waibuAdmin) return
|
|
210
302
|
const { getPluginPrefix } = this.app.waibu
|
|
303
|
+
const { findIndex } = this.app.lib._
|
|
211
304
|
const prefix = getPluginPrefix(this.ns)
|
|
212
305
|
const params = { action: 'list' }
|
|
213
306
|
const items = [{
|
|
214
|
-
title: 'xSite',
|
|
215
|
-
children: [
|
|
216
|
-
{ title: 'allSites', href: `waibuAdmin:/${prefix}/x/site/:action`, params },
|
|
217
|
-
{ title: 'xRouteGuard', href: `waibuAdmin:/${prefix}/x/route-guard/:action`, params },
|
|
218
|
-
{ title: 'xModelGuard', href: `waibuAdmin:/${prefix}/x/model-guard/:action`, params },
|
|
219
|
-
{ title: 'xAttribGuard', href: `waibuAdmin:/${prefix}/x/attrib-guard/:action`, params },
|
|
220
|
-
{ title: 'userSession', href: `waibuAdmin:/${prefix}/x/session/:action`, params }
|
|
221
|
-
]
|
|
222
|
-
}, {
|
|
223
307
|
title: 'manageSite',
|
|
224
308
|
children: [
|
|
225
309
|
{ title: 'siteProfile', href: `waibuAdmin:/${prefix}/site` },
|
|
@@ -242,7 +326,8 @@ async function factory (pkgName) {
|
|
|
242
326
|
}, {
|
|
243
327
|
title: 'permission',
|
|
244
328
|
children: [
|
|
245
|
-
{ title: '
|
|
329
|
+
{ title: 'secureGuard', href: `waibuAdmin:/${prefix}/secure-guard/:action`, params },
|
|
330
|
+
{ title: 'anonymousGuard', href: `waibuAdmin:/${prefix}/anonymous-guard/:action`, params },
|
|
246
331
|
{ title: 'modelGuard', href: `waibuAdmin:/${prefix}/model-guard/:action`, params },
|
|
247
332
|
{ title: 'attribGuard', href: `waibuAdmin:/${prefix}/attrib-guard/:action`, params }
|
|
248
333
|
]
|
|
@@ -260,9 +345,23 @@ async function factory (pkgName) {
|
|
|
260
345
|
{ title: 'manageDownload', href: `waibuAdmin:/${prefix}/download/:action`, params }
|
|
261
346
|
]
|
|
262
347
|
}]
|
|
348
|
+
const sessionMenu = { title: 'userSession', href: `waibuAdmin:/${prefix}/x/session/:action`, params }
|
|
349
|
+
const cacheMenu = { title: 'cacheStorage', href: `waibuAdmin:/${prefix}/x/cache/:action`, params }
|
|
350
|
+
if (this.config.multiSite.enabled) {
|
|
351
|
+
items.unshift({
|
|
352
|
+
title: 'xSite',
|
|
353
|
+
children: [
|
|
354
|
+
{ title: 'allSites', href: `waibuAdmin:/${prefix}/x/site/:action`, params },
|
|
355
|
+
sessionMenu
|
|
356
|
+
]
|
|
357
|
+
})
|
|
358
|
+
} else {
|
|
359
|
+
const idx = findIndex(items, i => i.title === 'misc')
|
|
360
|
+
if (idx > -1) items[idx].children.push(sessionMenu)
|
|
361
|
+
}
|
|
263
362
|
if (this.app.bajoCache) {
|
|
264
|
-
const
|
|
265
|
-
|
|
363
|
+
const idx = findIndex(items, i => i.title === this.config.multiSite.enabled ? 'xSite' : 'misc')
|
|
364
|
+
if (idx > -1)items[idx].children.push(cacheMenu)
|
|
266
365
|
}
|
|
267
366
|
return items
|
|
268
367
|
}
|
|
@@ -377,31 +476,11 @@ async function factory (pkgName) {
|
|
|
377
476
|
return true
|
|
378
477
|
}
|
|
379
478
|
|
|
380
|
-
|
|
381
|
-
const { includes } = this.app.lib.aneka
|
|
479
|
+
checkRouteGuard = (guards, paths) => {
|
|
382
480
|
const { outmatch } = this.app.lib
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
for (const path of paths) {
|
|
387
|
-
if (matchPath(path)) {
|
|
388
|
-
if (item.methods.includes(req.method)) {
|
|
389
|
-
if (includes(teamIds, item.teamIds)) return item
|
|
390
|
-
if (teamIds.length === 0) return item
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
checkPathsByGuard = ({ guards, paths }) => {
|
|
398
|
-
const { outmatch } = this.app.lib
|
|
399
|
-
const matcher = outmatch(guards)
|
|
400
|
-
let guarded
|
|
401
|
-
for (const path of paths) {
|
|
402
|
-
if (!guarded) guarded = matcher(path)
|
|
403
|
-
}
|
|
404
|
-
return guarded
|
|
481
|
+
const all = guards.map(item => item.path)
|
|
482
|
+
const isMatch = outmatch(all)
|
|
483
|
+
return paths.find(isMatch)
|
|
405
484
|
}
|
|
406
485
|
|
|
407
486
|
signout = async ({ req, reply, reason }) => {
|
|
@@ -545,81 +624,56 @@ async function factory (pkgName) {
|
|
|
545
624
|
return await hash(item, this.config.auth.common.apiKey.algo)
|
|
546
625
|
}
|
|
547
626
|
|
|
548
|
-
_fetchGuards = async (type
|
|
627
|
+
_fetchGuards = async (type) => {
|
|
549
628
|
const { getModel } = this.app.dobo
|
|
550
629
|
const options = { noMagic: true, noCache: true, noDriverHook: true, dataOnly: true }
|
|
551
630
|
const filter = { query: { status: 'ACTIVE' } }
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
}
|
|
631
|
+
const results = await getModel(`Sumba${type}Guard`).findAllRecord(filter, options)
|
|
632
|
+
return results.map(result => {
|
|
633
|
+
result.teamIds = result.teamIds.map(item => item + '')
|
|
634
|
+
return result
|
|
635
|
+
})
|
|
557
636
|
}
|
|
558
637
|
|
|
559
|
-
|
|
638
|
+
_getGuards = (inputs = []) => {
|
|
560
639
|
const { routePath } = this.app.waibu
|
|
561
640
|
const { orderBy } = this.app.lib._
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
result
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
return this.routeGuards
|
|
641
|
+
const normal = orderBy(inputs.filter(input => input.path[0] !== '!').map(result => {
|
|
642
|
+
result.path = routePath(result.path)
|
|
643
|
+
return result
|
|
644
|
+
}), ['weight', 'path'], ['desc', 'asc'])
|
|
645
|
+
const inverse = orderBy(inputs.filter(input => input.path[0] === '!').map(result => {
|
|
646
|
+
result.path = routePath(result.path)
|
|
647
|
+
return result
|
|
648
|
+
}), ['weight', 'path'], ['desc', 'asc'])
|
|
649
|
+
return [...normal, ...inverse]
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
getAnonymousGuards = async (reread) => {
|
|
653
|
+
if (!reread) return this.anonymousGuards
|
|
654
|
+
const guards = await this._fetchGuards('Anonymous')
|
|
655
|
+
this.anonymousGuards = this._getGuards(guards)
|
|
656
|
+
return this.anonymousGuards
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
getSecureGuards = async (reread) => {
|
|
660
|
+
if (!reread) return this.secureGuards
|
|
661
|
+
const guards = await this._fetchGuards('Secure')
|
|
662
|
+
this.secureGuards = this._getGuards(guards)
|
|
663
|
+
return this.secureGuards
|
|
586
664
|
}
|
|
587
665
|
|
|
588
666
|
getModelGuards = async (reread) => {
|
|
589
|
-
const { isSet } = this.app.lib.aneka
|
|
590
667
|
if (!reread) return this.modelGuards
|
|
591
668
|
|
|
592
|
-
|
|
593
|
-
for (const type of ['global', 'local']) {
|
|
594
|
-
this.modelGuards[type] = result[type].filter(item => (!isEmpty(item.value)) && (!isEmpty(item.models))).map(item => {
|
|
595
|
-
item.siteIds = item.siteIds ?? []
|
|
596
|
-
if (item.siteIds.length === 0) item.siteIds = [...result.siteIds]
|
|
597
|
-
if (item.siteId) item.siteIds = uniq([...item.siteIds, item.siteId].filter(i => isSet(i)).map(i => i + ''))
|
|
598
|
-
item.teamIds = (item.teamIds ?? []).filter(i => isSet(i)).map(i => i + '')
|
|
599
|
-
delete item.siteId
|
|
600
|
-
return item
|
|
601
|
-
})
|
|
602
|
-
}
|
|
669
|
+
this.modelGuards = await this._fetchGuards('Model')
|
|
603
670
|
return this.modelGuards
|
|
604
671
|
}
|
|
605
672
|
|
|
606
673
|
getAttribGuards = async (reread) => {
|
|
607
|
-
const { isSet } = this.app.lib.aneka
|
|
608
674
|
if (!reread) return this.attribGuards
|
|
609
675
|
|
|
610
|
-
|
|
611
|
-
for (const type of ['global', 'local']) {
|
|
612
|
-
this.attribGuards[type] = result[type].map(item => {
|
|
613
|
-
item.siteIds = item.siteIds ?? []
|
|
614
|
-
if (item.siteIds.length === 0) item.siteIds = [...result.siteIds]
|
|
615
|
-
if (item.siteId) item.siteIds = uniq([...item.siteIds, item.siteId].filter(i => isSet(i)).map(i => i + ''))
|
|
616
|
-
item.teamIds = (item.teamIds ?? []).filter(i => isSet(i)).map(i => i + '')
|
|
617
|
-
item.models = item.models ?? []
|
|
618
|
-
item.hiddenCols = item.hiddenCols ?? []
|
|
619
|
-
delete item.siteId
|
|
620
|
-
return item
|
|
621
|
-
})
|
|
622
|
-
}
|
|
676
|
+
this.attribGuards = await this._fetchGuards('Model')
|
|
623
677
|
return this.attribGuards
|
|
624
678
|
}
|
|
625
679
|
|
|
@@ -632,6 +686,7 @@ async function factory (pkgName) {
|
|
|
632
686
|
removeSite = removeSite
|
|
633
687
|
getSite = getSite
|
|
634
688
|
getUserById = getUserById
|
|
689
|
+
|
|
635
690
|
getUserByToken = getUserByToken
|
|
636
691
|
getUserByUsernamePassword = getUserByUsernamePassword
|
|
637
692
|
}
|
package/lib/get-user.js
CHANGED
|
@@ -10,7 +10,7 @@ export async function mergeTeam (user, req) {
|
|
|
10
10
|
if (userTeam.length === 0) return
|
|
11
11
|
delete query.userId
|
|
12
12
|
query.id = { $in: map(userTeam, 'teamId') }
|
|
13
|
-
query.status = '
|
|
13
|
+
query.status = 'ACTIVE'
|
|
14
14
|
mdl = getModel('SumbaTeam')
|
|
15
15
|
const teams = await mdl.findAllRecord({ query })
|
|
16
16
|
if (teams.length > 0) {
|
package/lib/util.js
CHANGED
|
@@ -26,7 +26,7 @@ export function parseNsSettings (ns, setting, items) {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export function pathsToCheck (req
|
|
29
|
+
export function pathsToCheck (req) {
|
|
30
30
|
const { uniq, without } = this.app.lib._
|
|
31
31
|
const items = [req.routeOptions.url, req.url].map(url => url.split('?')[0].split('#')[0])
|
|
32
32
|
return uniq(without(items, undefined, null))
|
|
@@ -56,36 +56,25 @@ export async function checkTheme (req, reply) {
|
|
|
56
56
|
req.theme = req.theme ?? 'default'
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export async function checkTeam (req, reply
|
|
60
|
-
const {
|
|
59
|
+
export async function checkTeam (req, reply) {
|
|
60
|
+
const { includes } = this.app.lib.aneka
|
|
61
61
|
const { outmatch } = this.app.lib
|
|
62
|
-
route.teamIds = route.teamIds ?? []
|
|
63
|
-
if (route.teamIds.length === 0) return
|
|
64
62
|
|
|
65
|
-
const teamIds = map(req.user.teams, 'id')
|
|
66
|
-
const teamAliases = map(req.user.teams, 'alias')
|
|
67
63
|
if (req.user.isAdmin) return
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
if (!match) {
|
|
82
|
-
match = this.checkPathsByRoute({ paths, teamIds, guards: globalGuards.filter(item => !item.negation) })
|
|
83
|
-
if (match) {
|
|
84
|
-
const neg = this.checkPathsByRoute({ paths, teamIds, guards: globalGuards.filter(item => item.negation) })
|
|
85
|
-
if (neg) match = undefined
|
|
86
|
-
}
|
|
64
|
+
if (req.routeOptions.config.xSite && req.user.isXSiteAdmin) return
|
|
65
|
+
|
|
66
|
+
const teamIds = req.user.teams.map(item => item.id + '')
|
|
67
|
+
if (req.user.teams.map(item => item.alias).length === 0) throw this.error('accessDenied', { statusCode: 403 })
|
|
68
|
+
|
|
69
|
+
const paths = pathsToCheck.call(this, req)
|
|
70
|
+
const results = (await this.getSecureGuards()).filter(item => {
|
|
71
|
+
if (item.siteId !== req.site.id + '' || item.path[0] === '!') return false
|
|
72
|
+
return paths.some(outmatch([item.path]))
|
|
73
|
+
})
|
|
74
|
+
for (const result of results) {
|
|
75
|
+
if (result.allTeams) continue
|
|
76
|
+
if (!includes(teamIds, result.teamIds)) throw this.error('accessDenied', { statusCode: 403 })
|
|
87
77
|
}
|
|
88
|
-
if (!match) throw this.error('accessDenied', { statusCode: 403 })
|
|
89
78
|
// passed
|
|
90
79
|
}
|
|
91
80
|
|
|
@@ -116,51 +105,22 @@ export async function checkUserId (req, reply, source) {
|
|
|
116
105
|
}
|
|
117
106
|
|
|
118
107
|
const paths = pathsToCheck.call(this, req)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const globalGuards = guards.global.filter(item => item.siteIds.includes(req.site.id + ''))
|
|
123
|
-
const localGuards = guards.local.filter(item => item.siteIds.includes(req.site.id + ''))
|
|
124
|
-
// find anonymousPath
|
|
125
|
-
anonymousPath = await this.checkPathsByRoute({ req, paths, guards: localGuards.filter(item => item.anonymous && !item.negation) })
|
|
126
|
-
if (anonymousPath) {
|
|
127
|
-
const neg = await this.checkPathsByRoute({ req, paths, guards: localGuards.filter(item => item.anonymous && item.negation) })
|
|
128
|
-
if (neg) anonymousPath = undefined
|
|
129
|
-
}
|
|
130
|
-
if (!anonymousPath) {
|
|
131
|
-
anonymousPath = await this.checkPathsByRoute({ req, paths, guards: globalGuards.filter(item => item.anonymous && !item.negation) })
|
|
132
|
-
if (anonymousPath) {
|
|
133
|
-
const neg = await this.checkPathsByRoute({ req, paths, guards: globalGuards.filter(item => item.anonymous && item.negation) })
|
|
134
|
-
if (neg) anonymousPath = undefined
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
// find securePath
|
|
138
|
-
securePath = await this.checkPathsByRoute({ req, paths, guards: localGuards.filter(item => !item.anonymous && !item.negation) })
|
|
139
|
-
if (securePath) {
|
|
140
|
-
const neg = await this.checkPathsByRoute({ req, paths, guards: localGuards.filter(item => !item.anonymous && item.negation) })
|
|
141
|
-
if (neg) securePath = undefined
|
|
142
|
-
}
|
|
143
|
-
if (!securePath) {
|
|
144
|
-
securePath = await this.checkPathsByRoute({ req, paths, guards: globalGuards.filter(item => !item.anonymous && !item.negation) })
|
|
145
|
-
if (securePath) {
|
|
146
|
-
const neg = await this.checkPathsByRoute({ req, paths, guards: globalGuards.filter(item => !item.anonymous && item.negation) })
|
|
147
|
-
if (neg) securePath = undefined
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
// checking...
|
|
151
|
-
if (!securePath && !anonymousPath) {
|
|
152
|
-
if (userId) await setUser()
|
|
153
|
-
return false // regular, unguarded path. Not secure & not anonymous path
|
|
154
|
-
}
|
|
155
|
-
if (anonymousPath) {
|
|
108
|
+
let guards = (await this.getAnonymousGuards()).filter(item => item.siteId === req.site.id + '')
|
|
109
|
+
const anonymous = this.checkRouteGuard(guards, paths)
|
|
110
|
+
if (anonymous) {
|
|
156
111
|
if (!userId) return false
|
|
157
112
|
req.session.ref = req.url
|
|
158
113
|
return reply.redirectTo(routePath(this.config.redirect.signout))
|
|
159
114
|
}
|
|
160
|
-
|
|
115
|
+
guards = (await this.getSecureGuards()).filter(item => item.siteId === req.site.id + '')
|
|
116
|
+
const secure = this.checkRouteGuard(guards, paths)
|
|
117
|
+
if (!secure) {
|
|
118
|
+
if (userId) await setUser()
|
|
119
|
+
return false // regular, unguarded path. Not secure & not anonymous path
|
|
120
|
+
}
|
|
161
121
|
if (userId) {
|
|
162
122
|
await setUser()
|
|
163
|
-
return
|
|
123
|
+
return secure
|
|
164
124
|
}
|
|
165
125
|
const silentOnError = this.config.auth[webApp].silentOnError ?? this.config.auth.common.silentOnError
|
|
166
126
|
const payload = silentOnError ? { noContent: true } : undefined
|
|
@@ -177,7 +137,7 @@ export async function checkUserId (req, reply, source) {
|
|
|
177
137
|
}
|
|
178
138
|
}
|
|
179
139
|
if (!success) throw this.error('accessDeniedNoAuth', merge({ statusCode: 403 }, payload))
|
|
180
|
-
return
|
|
140
|
+
return secure
|
|
181
141
|
}
|
|
182
142
|
|
|
183
143
|
export async function latLngHook (body, options) {
|
|
@@ -196,6 +156,7 @@ export async function latLngHook (body, options) {
|
|
|
196
156
|
*/
|
|
197
157
|
export async function checkXSite (req, reply) {
|
|
198
158
|
const { get } = this.app.lib._
|
|
159
|
+
if (!this.config.multiSite.enabled) return
|
|
199
160
|
if (!get(req, 'routeOptions.config.xSite')) return
|
|
200
161
|
if (!get(req, 'user.isXSiteAdmin')) throw this.error('accessDenied', { statusCode: 403 })
|
|
201
162
|
}
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-06-10
|
|
4
|
+
|
|
5
|
+
- [2.30.0] Refactoring all guards
|
|
6
|
+
- [2.30.0] Feature ```sumba:status``` now by default using ```ACTIVE``` and ```INACTIVE``` states
|
|
7
|
+
- [2.30.0] Bug in ```multiSite``` handling
|
|
8
|
+
- [2.30.0] Add ```populateRouteGuards()```
|
|
9
|
+
- [2.30.0] Bug fix in ```hook.js```
|
|
10
|
+
|
|
3
11
|
## 2026-06-03
|
|
4
12
|
|
|
5
13
|
- [2.29.0] Some models with property ```values``` now use the newly introduced function values
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
const routes = [
|
|
2
|
-
'sumba:/your-stuff/**/*',
|
|
3
|
-
'sumba:/signout',
|
|
4
|
-
'sumba:/help/trouble-tickets/**/*',
|
|
5
|
-
'sumba.restapi:/user/api-key',
|
|
6
|
-
'sumba.restapi:/your-stuff/**/*',
|
|
7
|
-
'sumba.restapi:/manage/**/*',
|
|
8
|
-
'~sumba:/user/**/*',
|
|
9
|
-
'~sumba:/signin',
|
|
10
|
-
'~sumba.restapi:/user/access-token/**/*'
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
async function routeGuard () {
|
|
14
|
-
return routes.map(r => {
|
|
15
|
-
const anonymous = r[0] === '~'
|
|
16
|
-
return {
|
|
17
|
-
path: anonymous ? r.slice(1) : r,
|
|
18
|
-
anonymous,
|
|
19
|
-
status: 'ACTIVE'
|
|
20
|
-
}
|
|
21
|
-
})
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export default routeGuard
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
const action = {
|
|
2
|
-
method: ['GET', 'POST'],
|
|
3
|
-
title: 'xAttribGuard',
|
|
4
|
-
xSite: true,
|
|
5
|
-
handler: async function (req, reply) {
|
|
6
|
-
const { importModule } = this.app.bajo
|
|
7
|
-
const crudSkel = await importModule('waibuAdmin:/lib/crud-skel.js')
|
|
8
|
-
return await crudSkel.call(this, 'SumbaXAttribGuard', req, reply)
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default action
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
const action = {
|
|
2
|
-
method: ['GET', 'POST'],
|
|
3
|
-
title: 'xRouteGuard',
|
|
4
|
-
xSite: true,
|
|
5
|
-
handler: async function (req, reply) {
|
|
6
|
-
const { importModule } = this.app.bajo
|
|
7
|
-
const crudSkel = await importModule('waibuAdmin:/lib/crud-skel.js')
|
|
8
|
-
return await crudSkel.call(this, 'SumbaXRouteGuard', req, reply)
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export default action
|