sumba 2.22.0 → 2.23.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.
@@ -0,0 +1,6 @@
1
+ async function afterAction (action, ...args) {
2
+ if (!['createRecord', 'updateRecord', 'removeRecord'].includes(action)) return
3
+ await this.getTeamGuards(true)
4
+ }
5
+
6
+ export default afterAction
@@ -0,0 +1,6 @@
1
+ async function afterAction (action, ...args) {
2
+ if (!['createRecord', 'updateRecord', 'removeRecord'].includes(action)) return
3
+ await this.getUserGuards(true)
4
+ }
5
+
6
+ export default afterAction
@@ -1,10 +1,8 @@
1
1
  export async function clearCache (id, result) {
2
2
  if (!this.app.bajoCache) return
3
- const { hash } = this.app.bajoExtra
4
3
  const { clear } = this.app.bajoCache ?? {}
5
- const { get, isEmpty } = this.app.lib._
6
- let token = get(result, 'data.token', get(result, 'oldData.token', ''))
7
- if (!isEmpty(token)) token = await hash(token)
4
+ const { get } = this.app.lib._
5
+ const token = get(result, 'data.token', get(result, 'oldData.token', ''))
8
6
  await clear({ key: `dobo|SumbaUser|getUserById|${id}` })
9
7
  await clear({ key: `dobo|SumbaUser|getUserByToken|${token}` })
10
8
  }
@@ -1,13 +1,13 @@
1
- import { checkUserId, checkTeam, checkTheme, checkIconset, checkinterSite } from '../../../lib/util.js'
1
+ import { checkUserId, checkTeam, checkTheme, checkIconset, checkInterSite } from '../../../lib/util.js'
2
2
 
3
3
  const preParsing = {
4
4
  level: 10,
5
5
  handler: async function (req, reply) {
6
6
  await checkTheme.call(this, req, reply)
7
7
  await checkIconset.call(this, req, reply)
8
- await checkUserId.call(this, req, reply, 'waibuMpa')
9
- await checkTeam.call(this, req, reply, 'waibuMpa')
10
- await checkinterSite.call(this, req, reply)
8
+ if (!await checkUserId.call(this, req, reply, 'waibuMpa')) return
9
+ if (!await checkTeam.call(this, req, reply, 'waibuMpa')) return
10
+ await checkInterSite.call(this, req, reply)
11
11
  }
12
12
  }
13
13
 
@@ -1,11 +1,11 @@
1
- import { checkUserId, checkTeam, checkinterSite } from '../../../lib/util.js'
1
+ import { checkUserId, checkTeam, checkInterSite } from '../../../lib/util.js'
2
2
 
3
3
  const preParsing = {
4
4
  level: 10,
5
5
  handler: async function (req, reply) {
6
- await checkUserId.call(this, req, reply, 'waibuRestApi')
7
- await checkTeam.call(this, req, reply, 'waibuRestApi')
8
- await checkinterSite.call(this, req, reply)
6
+ if (!await checkUserId.call(this, req, reply, 'waibuRestApi')) return
7
+ if (!await checkTeam.call(this, req, reply, 'waibuRestApi')) return
8
+ await checkInterSite.call(this, req, reply)
9
9
  }
10
10
  }
11
11
 
@@ -1,11 +1,11 @@
1
- import { checkUserId, checkTeam, checkinterSite } from '../../../lib/util.js'
1
+ import { checkUserId, checkTeam, checkInterSite } from '../../../lib/util.js'
2
2
 
3
3
  const preParsing = {
4
4
  level: 10,
5
5
  handler: async function (req, reply) {
6
- await checkUserId.call(this, req, reply, 'waibuStatic')
7
- await checkTeam.call(this, req, reply, 'waibuStatic')
8
- await checkinterSite.call(this, req, reply)
6
+ if (!await checkUserId.call(this, req, reply, 'waibuStatic')) return
7
+ if (!await checkTeam.call(this, req, reply, 'waibuStatic')) return
8
+ await checkInterSite.call(this, req, reply)
9
9
  }
10
10
  }
11
11
 
@@ -1,23 +1,5 @@
1
- import { collectRoutes, collectTeam } from '../../../lib/collect.js'
2
-
3
- async function afterAppBoot () {
4
- const { runHook } = this.app.bajo
5
- await runHook(`${this.ns}:beforeBoot`)
6
- this.log.trace('collectingRouteGuards')
7
- await collectRoutes.call(this, 'secure')
8
- await runHook(`${this.ns}:afterCollectSecureRoutes`, this.secureRoutes, this.secureNegRoutes)
9
- this.log.trace('secureRoutes%d', this.secureRoutes.length)
10
- this.log.trace('secureNegRoutes%d', this.secureNegRoutes.length)
11
- await collectRoutes.call(this, 'anonymous')
12
- await runHook(`${this.ns}:afterCollectAnonymousRoutes`, this.anonymousRoutes, this.anonymousNegRoutes)
13
- this.log.trace('anonRoutes%d', this.anonymousRoutes.length)
14
- this.log.trace('anonNegRoutes%d', this.anonymousNegRoutes.length)
15
- this.log.trace('collectingTeamGuards')
16
- await collectTeam.call(this)
17
- await runHook(`${this.ns}:afterCollectTeamRoutes`, this.teamRoutes, this.teamNegRoutes)
18
- this.log.trace('teamRoutes%d', this.teamRoutes.length)
19
- this.log.trace('teamNegRoutes%d', this.teamNegRoutes.length)
20
- await runHook(`${this.ns}:afterBoot`)
21
- }
22
-
23
- export default afterAppBoot
1
+ async function afterAppBoot () {
2
+ await this.getRouteGuards(true)
3
+ }
4
+
5
+ export default afterAppBoot
@@ -1,13 +1,8 @@
1
- import { checkNoRouteSetting, checkNoRouteSettingKey } from '../../../lib/util.js'
2
-
3
1
  const preParsing = {
4
2
  level: 10,
5
3
  handler: async function (req, reply) {
6
- const { get } = this.app.lib._
7
4
  const { getHostname } = this.app.waibu
8
5
  req.site = await this.getSite(getHostname(req))
9
- const routes = get(req.site, checkNoRouteSettingKey)
10
- checkNoRouteSetting.call(this, req, routes)
11
6
  }
12
7
  }
13
8
 
@@ -61,11 +61,6 @@
61
61
  "troubleTickets": "Trouble Tickets",
62
62
  "guest": "Guest",
63
63
  "warningMemberOnly%s": "Please authenticate yourself first, because the <a href=\"%s\">page<a> your're trying to access is a member only page",
64
- "collectingRouteGuards": "Collecting route guards:",
65
- "secureRoutes%d": "- Secure routes: %d",
66
- "secureNegRoutes%d": "- Secure, negated routes: %d",
67
- "anonRoutes%d": "- Anonymous routes: %d",
68
- "anonNegRoutes%d": "- Anonymous, negated routes: %d",
69
64
  "replies": "Replies",
70
65
  "compose": "Compose",
71
66
  "yourReply": "Your Reply",
@@ -136,6 +131,8 @@
136
131
  "statusOpen": "Open",
137
132
  "statusClosed": "Closed",
138
133
  "allSites": "All Sites",
134
+ "permission": "Permission",
135
+ "routeGuard": "Route Guard",
139
136
  "field": {
140
137
  "currentPassword": "Current Password",
141
138
  "newPassword": "New Password",
@@ -159,7 +156,12 @@
159
156
  "team": "Team",
160
157
  "ns": "Module's Namespace",
161
158
  "value": "Value",
162
- "notes": "Notes"
159
+ "notes": "Notes",
160
+ "path": "Path",
161
+ "inverse": "Inverse?",
162
+ "anonymous": "Anonymous?",
163
+ "methods": "Methods",
164
+ "teams": "Teams"
163
165
  },
164
166
  "validation": {
165
167
  "password": {
@@ -0,0 +1,26 @@
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 userGuard () {
14
+ return routes.map(r => {
15
+ const anonymous = r[0] === '~'
16
+ return {
17
+ path: anonymous ? r.slice(1) : r,
18
+ anonymous,
19
+ _immutable: true,
20
+ siteId: '?:SumbaSite::alias:default',
21
+ status: 'ACTIVE'
22
+ }
23
+ })
24
+ }
25
+
26
+ export default userGuard
@@ -0,0 +1,60 @@
1
+ async function routeGuard () {
2
+ return {
3
+ connection: 'memory',
4
+ properties: [
5
+ 'path,,255,true,true',
6
+ {
7
+ name: 'inverse',
8
+ type: 'boolean',
9
+ required: true,
10
+ default: false
11
+ }, {
12
+ name: 'anonymous',
13
+ type: 'boolean',
14
+ required: true,
15
+ default: false
16
+ }, {
17
+ name: 'methods',
18
+ type: 'array',
19
+ required: true,
20
+ default: ['GET', 'POST', 'UPDATE', 'DELETE'],
21
+ values: ['GET', 'POST', 'UPDATE', 'DELETE']
22
+ }, {
23
+ name: 'teams',
24
+ type: 'array',
25
+ default: [],
26
+ ref: {
27
+ teams: {
28
+ model: 'SumbaTeam',
29
+ field: 'alias',
30
+ searchField: 'name',
31
+ fields: ['alias', 'name']
32
+ }
33
+ }
34
+ }, {
35
+ name: 'weight',
36
+ type: 'smallint',
37
+ default: 0
38
+ }
39
+ ],
40
+ features: [
41
+ {
42
+ name: 'sumba:status',
43
+ values: ['ACTIVE', 'INACTIVE'],
44
+ default: 'ACTIVE'
45
+ },
46
+ {
47
+ name: 'dobo:unique',
48
+ fields: ['path', 'anonymous', 'methods', 'teams', 'inverse', 'weight', 'status', 'siteId']
49
+ },
50
+ 'dobo:immutable',
51
+ 'dobo:updatedAt',
52
+ 'sumba:siteId'
53
+ ],
54
+ options: {
55
+ persistence: false
56
+ }
57
+ }
58
+ }
59
+
60
+ export default routeGuard
@@ -28,6 +28,7 @@ async function user () {
28
28
  maxLength: 100,
29
29
  virtual: true,
30
30
  getValue: async function (val, rec) {
31
+ if (!rec.salt) return
31
32
  return await this.plugin.hash(rec.salt)
32
33
  }
33
34
  }, {
@@ -63,7 +64,7 @@ async function user () {
63
64
  fields: ['email', 'siteId'],
64
65
  type: 'unique'
65
66
  }],
66
- hidden: ['password', 'token'],
67
+ hidden: ['password'],
67
68
  features: [
68
69
  'sumba:address',
69
70
  'sumba:social',
@@ -0,0 +1,15 @@
1
+ async function routeGuard () {
2
+ return {
3
+ common: {
4
+ widget: {
5
+ teams: {
6
+ attr: {
7
+ refUrl: 'waibuAdmin:/{prefix}/team/details?id={id}'
8
+ }
9
+ }
10
+ }
11
+ }
12
+ }
13
+ }
14
+
15
+ export default routeGuard
@@ -0,0 +1,11 @@
1
+ const action = {
2
+ method: ['GET', 'POST'],
3
+ title: 'routeGuard',
4
+ handler: async function (req, reply) {
5
+ const { importModule } = this.app.bajo
6
+ const crudSkel = await importModule('waibuAdmin:/lib/crud-skel.js')
7
+ return await crudSkel.call(this, 'SumbaRouteGuard', req, reply)
8
+ }
9
+ }
10
+
11
+ export default action
@@ -0,0 +1,4 @@
1
+ {
2
+ "model": "SumbaRouteGuard",
3
+ "disabled": ["create", "update", "remove"]
4
+ }
@@ -1,5 +1,5 @@
1
1
  const model = 'SumbaUser'
2
- const hidden = ['password', 'token', 'siteId', 'salt']
2
+ const hidden = ['password', 'siteId']
3
3
 
4
4
  async function get () {
5
5
  const { getRecord } = this.app.waibuDb
package/index.js CHANGED
@@ -152,6 +152,8 @@ async function factory (pkgName) {
152
152
  getUserByTokenDur: '1m'
153
153
  }
154
154
  }
155
+ this.userGuards = []
156
+ this.teamGuards = []
155
157
  this.unsafeUserFields = ['password']
156
158
  this.selfBind(['createNewSite', 'removeSite', 'getSite', 'getUserById', 'getUserByToken', 'getUserByUsernamePassword'])
157
159
  }
@@ -160,10 +162,6 @@ async function factory (pkgName) {
160
162
  const { getPluginDataDir } = this.app.bajo
161
163
  this.downloadDir = `${getPluginDataDir(this.ns)}/download`
162
164
  this.app.lib.fs.ensureDirSync(this.downloadDir)
163
- for (const type of ['secure', 'anonymous', 'team']) {
164
- this[`${type}Routes`] = this[`${type}Routes`] ?? []
165
- this[`${type}NegRoutes`] = this[`${type}NegRoutes`] ?? []
166
- }
167
165
  if (this.config.multiSite === true) {
168
166
  this.config.multiSite = cloneDeep(defMultiSite)
169
167
  this.config.multiSite.enabled = true
@@ -173,8 +171,8 @@ async function factory (pkgName) {
173
171
  start = async () => {
174
172
  const { getModel } = this.app.dobo
175
173
  if (this.config.interSiteAdmins.length === 0) {
176
- const site = await getModel('SumbaSite').findOneRecord({ query: { alias: 'default' } }, { noHook: true })
177
- const user = await getModel('SumbaUser').findOneRecord({ query: { username: 'admin', siteId: site.id } }, { noHook: true })
174
+ const site = await getModel('SumbaSite').findOneRecord({ query: { alias: 'default' } }, { noMagic: true })
175
+ const user = await getModel('SumbaUser').findOneRecord({ query: { username: 'admin', siteId: site.id } }, { noMagic: true })
178
176
  this.config.interSiteAdmins.push(user.id)
179
177
  }
180
178
  }
@@ -227,6 +225,11 @@ async function factory (pkgName) {
227
225
  { title: 'teamUser', href: `waibuAdmin:/${prefix}/team-user/list` },
228
226
  { title: 'teamSetting', href: `waibuAdmin:/${prefix}/team-setting/list` }
229
227
  ]
228
+ }, {
229
+ title: 'permission',
230
+ children: [
231
+ { title: 'routeGuard', href: `waibuAdmin:/${prefix}/route-guard/list` }
232
+ ]
230
233
  }, {
231
234
  title: 'supportSystem',
232
235
  children: [
@@ -358,7 +361,7 @@ async function factory (pkgName) {
358
361
  return true
359
362
  }
360
363
 
361
- checkPathsByTeam = ({ paths = [], method = 'GET', teams = [], guards = [] }) => {
364
+ checkPathsByRoute = ({ paths = [], method = 'GET', teams = [], guards = [] }) => {
362
365
  const { includes } = this.app.lib.aneka
363
366
  const { outmatch } = this.app.lib
364
367
 
@@ -366,9 +369,8 @@ async function factory (pkgName) {
366
369
  const matchPath = outmatch(item.path)
367
370
  for (const path of paths) {
368
371
  if (matchPath(path)) {
369
- const matchMethods = outmatch(item.methods, { separator: false })
370
- if (matchMethods(method)) {
371
- if (item.teams.length === 0) return item
372
+ if (item.methods.includes(method)) {
373
+ if (teams.length === 0) return item
372
374
  if (includes(teams, item.teams)) return item
373
375
  }
374
376
  }
@@ -376,19 +378,6 @@ async function factory (pkgName) {
376
378
  }
377
379
  }
378
380
 
379
- checkPathsByRoute = ({ paths = [], method = 'GET', guards = [] }) => {
380
- const { outmatch } = this.app.lib
381
- for (const item of guards) {
382
- const matchPath = outmatch(item.path)
383
- for (const path of paths) {
384
- if (matchPath(path)) {
385
- const matchMethods = outmatch(item.methods, { separator: false })
386
- if (matchMethods(method)) return item
387
- }
388
- }
389
- }
390
- }
391
-
392
381
  checkPathsByGuard = ({ guards, paths }) => {
393
382
  const { outmatch } = this.app.lib
394
383
  const matcher = outmatch(guards)
@@ -535,31 +524,25 @@ async function factory (pkgName) {
535
524
  return password
536
525
  }
537
526
 
538
- parseRouteGuard = item => {
539
- const { routePath, routePathHandlers } = this.app.waibu
540
- const { isString, isEmpty } = this.app.lib._
541
- if (isString(item)) {
542
- let [path, methods] = item.split('|').map(i => i.trim())
543
- methods = isEmpty(methods) ? ['*'] : methods.split(',').map(i => i.trim())
544
- item = { path, methods }
545
- }
546
- item.methods = item.methods ?? ['*']
547
- if (item.methods.includes('*')) item.methods = ['*']
548
- const [, routeHandler] = item.path.split(':')[0].split('.')
549
- if (!isEmpty(routeHandler) && !routePathHandlers[routeHandler]) return
550
- const rns = isEmpty(routeHandler) ? 'waibuMpa' : routePathHandlers[routeHandler].ns
551
- if (!this.app[rns]) return
552
- item.inverse = item.path[0] === '!'
553
- if (item.inverse) item.path = item.path.slice(1)
554
- item.path = routePath(item.path, { defFormat: false })
555
- return item
556
- }
557
-
558
527
  hash = async (item) => {
559
528
  const { hash } = this.app.bajoExtra
560
529
  return await hash(item, this.config.auth.common.apiKey.algo)
561
530
  }
562
531
 
532
+ getRouteGuards = async (reread) => {
533
+ const { getModel } = this.app.dobo
534
+ const { routePath } = this.app.waibu
535
+ const { map, orderBy } = this.app.lib._
536
+ if (!reread) return this.routeGuards
537
+ const model = getModel('SumbaRouteGuard')
538
+ const results = await model.findAllRecord({ query: { status: 'ACTIVE' } }, { noMagic: true, dataOnly: true })
539
+ this.routeGuards = orderBy(map(results, item => {
540
+ item.path = routePath(item.path)
541
+ return item
542
+ }), ['weight', 'path'])
543
+ return this.routeGuards
544
+ }
545
+
563
546
  createNewSite = createNewSite
564
547
  removeSite = removeSite
565
548
  getSite = getSite
package/lib/util.js CHANGED
@@ -1,9 +1,6 @@
1
- export const checkNoRouteSettingKey = 'setting.waibu.noRoutes.$in'
2
-
3
1
  export function parseNsSettings (ns, setting, items) {
4
- const { trim, set, isPlainObject, isArray, isEmpty, find, get, isString } = this.app.lib._
2
+ const { trim, set, isPlainObject, isArray, isEmpty, find } = this.app.lib._
5
3
  const { parseObject, dayjs } = this.app.lib
6
- const { routePath } = this.app.waibu
7
4
 
8
5
  for (const item of items) {
9
6
  if (item.ns === '_var' || ns === '_var') continue
@@ -27,30 +24,6 @@ export function parseNsSettings (ns, setting, items) {
27
24
  if ((isPlainObject(value) || isArray(value)) && isEmpty(value)) continue
28
25
  set(setting, `${ns}.${item.key}`, value)
29
26
  }
30
- const key = checkNoRouteSettingKey.slice(checkNoRouteSettingKey.indexOf('.') + 1)
31
- let noRoutes = get(setting, key, [])
32
- if (noRoutes.length > 0) {
33
- noRoutes = noRoutes.map(item => {
34
- if (!isString(item)) return item
35
- let [url, methods] = item.split('|')
36
- if (methods === '*' || !methods) methods = 'GET,POST,PUT,DELETE'
37
- methods = methods.split(',').map(m => m.trim().toUpperCase())
38
- return { url: routePath(url), methods }
39
- })
40
- set(setting, key, noRoutes)
41
- }
42
- }
43
-
44
- export function checkNoRouteSetting (req, routes) {
45
- const { isEmpty } = this.app.lib._
46
- const { outmatch } = this.app.lib
47
- if (isEmpty(routes)) return
48
- for (const route of routes) {
49
- const isMatchUrl = outmatch(route.url)
50
- if (isMatchUrl(req.url) || isMatchUrl(req.routeOptions.url)) {
51
- if (route.methods.includes(req.method)) throw this.error('accessDenied', { statusCode: 403 })
52
- }
53
- }
54
27
  }
55
28
 
56
29
  export function pathsToCheck (req, withHome) {
@@ -84,25 +57,20 @@ export async function checkTheme (req, reply) {
84
57
  }
85
58
 
86
59
  export async function checkTeam (req, reply, source) {
87
- const { get, isEmpty } = this.app.lib._
88
60
  if (!req.user) return
89
61
  const { map } = this.app.lib._
90
62
  const paths = pathsToCheck.call(this, req, true)
91
63
  const teams = map(req.user.teams, 'alias')
92
- let match = this.checkPathsByTeam({ paths, method: req.method, teams, guards: this.teamRoutes })
93
- if (!match) match = this.checkPathsByTeam({ paths, method: req.method, teams, guards: this.teamNegRoutes })
94
- if (!match) {
95
- const guards = map(this.teamRoutes, 'path')
96
- match = !this.checkPathsByGuard({ paths, guards })
64
+ const allGuards = (await this.getRouteGuards()).filter(item => item.siteId === req.site.id + '' && item.teams.length > 0)
65
+ if (allGuards.length === 0) return // no route guarded
66
+ let match = this.checkPathsByRoute({ paths, method: req.method, teams, guards: allGuards.filter(item => !item.inverse) })
67
+ if (!match) return // route is NOT protected by team guard
68
+ if (match) {
69
+ const neg = this.checkPathsByRoute({ paths, method: req.method, teams, guards: allGuards.filter(item => item.inverse) })
70
+ if (neg) match = undefined
97
71
  }
98
72
  if (!match) throw this.error('accessDenied', { statusCode: 403 })
99
- const routes = []
100
- for (const team of (req.user.teams ?? [])) {
101
- const items = get(team, checkNoRouteSettingKey, [])
102
- if (isEmpty(items)) continue
103
- routes.push(...items)
104
- }
105
- checkNoRouteSetting.call(this, req, routes)
73
+ // passed
106
74
  }
107
75
 
108
76
  export async function checkUserId (req, reply, source) {
@@ -111,10 +79,9 @@ export async function checkUserId (req, reply, source) {
111
79
  const userId = get(req, 'session.userId')
112
80
 
113
81
  const setUser = async () => {
114
- const id = get(req, 'session.userId')
115
- if (!id) return
82
+ if (!userId) return
116
83
  try {
117
- const user = await this.getUserById(id, req)
84
+ const user = await this.getUserById(userId, req)
118
85
  if (user) req.user = user
119
86
  else req.session.userId = null
120
87
  } catch (err) {
@@ -133,46 +100,47 @@ export async function checkUserId (req, reply, source) {
133
100
  }
134
101
 
135
102
  const paths = pathsToCheck.call(this, req)
136
- let securePath = await this.checkPathsByRoute({ paths, method: req.method, guards: this.secureRoutes })
103
+ const allGuards = (await this.getRouteGuards()).filter(item => item.siteId === req.site.id + '')
104
+ if (allGuards.length === 0) return false // no routes protected
105
+ let securePath = await this.checkPathsByRoute({ paths, method: req.method, guards: allGuards.filter(item => !item.anonymous && !item.inverse) })
137
106
  if (securePath) {
138
- const neg = await this.checkPathsByRoute({ paths, method: req.method, guards: this.secureNegRoutes })
107
+ const neg = await this.checkPathsByRoute({ paths, method: req.method, guards: allGuards.filter(item => !item.anonymous && item.inverse) })
139
108
  if (neg) securePath = undefined
140
109
  }
141
- let anonymousPath = await this.checkPathsByRoute({ paths, method: req.method, guards: this.anonymousRoutes })
110
+ let anonymousPath = await this.checkPathsByRoute({ paths, method: req.method, guards: allGuards.filter(item => item.anonymous && !item.inverse) })
142
111
  if (anonymousPath) {
143
- const neg = await this.checkPathsByRoute({ paths, method: req.method, guards: this.anonymousNegRoutes })
112
+ const neg = await this.checkPathsByRoute({ paths, method: req.method, guards: allGuards.filter(item => item.anonymous && item.inverse) })
144
113
  if (neg) anonymousPath = undefined
145
114
  }
146
115
  if (!securePath && !anonymousPath) {
147
116
  if (userId) await setUser()
148
- return
117
+ return false // regular, unguarded path. Not secure & not anonymous path
149
118
  }
150
119
  if (anonymousPath) {
151
- if (!userId) return
120
+ if (!userId) return false
152
121
  req.session.ref = req.url
153
122
  return reply.redirectTo(routePath(this.config.redirect.signout))
154
123
  }
155
- if (securePath) {
156
- if (userId) {
157
- await setUser()
158
- return
159
- }
160
- const silentOnError = this.config.auth[webApp].silentOnError ?? this.config.auth.common.silentOnError
161
- const payload = silentOnError ? { noContent: true } : undefined
162
- const authMethods = this.config.auth[webApp].methods ?? []
163
- if (isEmpty(authMethods)) throw this.error('noAuthMethod', merge({ statusCode: 500 }, payload))
164
- let success
165
- for (const m of authMethods) {
166
- const handler = this[camelCase(`verify ${m}`)]
167
- if (!handler) throw this.error('invalidAuthMethod%s', m, merge({ statusCode: 500 }, payload))
168
- const check = await handler(req, reply, source, payload)
169
- if (check) {
170
- success = check
171
- break
172
- }
124
+ if (userId) {
125
+ await setUser()
126
+ return true
127
+ }
128
+ const silentOnError = this.config.auth[webApp].silentOnError ?? this.config.auth.common.silentOnError
129
+ const payload = silentOnError ? { noContent: true } : undefined
130
+ const authMethods = this.config.auth[webApp].methods ?? []
131
+ if (isEmpty(authMethods)) throw this.error('noAuthMethod', merge({ statusCode: 500 }, payload))
132
+ let success
133
+ for (const m of authMethods) {
134
+ const handler = this[camelCase(`verify ${m}`)]
135
+ if (!handler) throw this.error('invalidAuthMethod%s', m, merge({ statusCode: 500 }, payload))
136
+ const check = await handler(req, reply, source, payload)
137
+ if (check) {
138
+ success = check
139
+ break
173
140
  }
174
- if (!success) throw this.error('accessDeniedNoAuth', merge({ statusCode: 403 }, payload))
175
141
  }
142
+ if (!success) throw this.error('accessDeniedNoAuth', merge({ statusCode: 403 }, payload))
143
+ return true
176
144
  }
177
145
 
178
146
  export async function latLngHook (body, options) {
@@ -182,7 +150,14 @@ export async function latLngHook (body, options) {
182
150
  body[options.field] = round(body[options.field], options.scale)
183
151
  }
184
152
 
185
- export async function checkinterSite (req, reply) {
153
+ /**
154
+ * If current route is an inter site route user is an inter site admin, then let it passed
155
+ *
156
+ * @param {Object} req - Request object
157
+ * @param {Object} reply - Reply object
158
+ * @returns
159
+ */
160
+ export async function checkInterSite (req, reply) {
186
161
  const { get } = this.app.lib._
187
162
  const isinterSite = get(req, 'routeOptions.config.interSite')
188
163
  const isInterSiteAdmin = get(req, 'user.interSiteAdmin')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sumba",
3
- "version": "2.22.0",
3
+ "version": "2.23.0",
4
4
  "description": "Biz Suite for Bajo Framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/wiki/CHANGES.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-05-11
4
+
5
+ - [2.23.0] Unify route guards and put it in ```SumbaRouteGuard``` database
6
+ - [2.23.0] Phased out ```setting.noRoutes```
7
+
8
+ ## 2026-04-25
9
+
10
+ - [2.21.1] Bug fix on team routes collections
11
+
3
12
  ## 2026-04-25
4
13
 
5
14
  - [2.21.0] Change options to format value using the new key set by dobo
@@ -1,10 +0,0 @@
1
- [{
2
- "path": "sumba:/user/**/*",
3
- "methods": ["*"]
4
- }, {
5
- "path": "sumba:/signin",
6
- "methods": ["*"]
7
- }, {
8
- "path": "sumba.restapi:/user/access-token/**/*",
9
- "methods": ["*"]
10
- }]
@@ -1,8 +0,0 @@
1
- [
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
- ]
package/lib/collect.js DELETED
@@ -1,52 +0,0 @@
1
- export async function collect ({ type = '', handler, container, file, ns, dir }) {
2
- const { readConfig } = this.app.bajo
3
- const { parseRouteGuard } = this
4
- const { camelCase, find, isEmpty } = this.app.lib._
5
- let items = await readConfig(file, { ns, baseNs: this.ns, defValue: [] })
6
- if (isEmpty(items)) items = []
7
- for (let item of items) {
8
- item = parseRouteGuard(item)
9
- if (!item) continue
10
- if (handler) await handler.call(this, item)
11
- const guards = this[camelCase(`${type} ${item.reverse ? 'Neg' : ''} ${container}`)]
12
- delete item.reverse
13
- if (find(guards, { path: item.path })) continue
14
- guards.push(item)
15
- }
16
- }
17
-
18
- function collectHandler (item, keys = []) {
19
- const { isString } = this.app.lib._
20
- for (const k of keys) {
21
- item[k] = item[k] ?? []
22
- if (isString(item[k])) item[k] = item[k].split(',')
23
- }
24
- }
25
-
26
- export async function collectRoutes (type) {
27
- const { eachPlugins } = this.app.bajo
28
- const me = this
29
-
30
- function handler (item) {
31
- collectHandler.call(this, item, ['methods'])
32
- }
33
-
34
- await eachPlugins(async function ({ file, dir }) {
35
- const { ns } = this
36
- await collect.call(me, { type, container: 'Routes', handler, file, ns, dir })
37
- }, { glob: `route-guard/${type}.*`, prefix: this.ns })
38
- }
39
-
40
- export async function collectTeam () {
41
- const { eachPlugins } = this.app.bajo
42
- const me = this
43
-
44
- function handler (item) {
45
- collectHandler.call(this, item, ['methods', 'teams', 'features'])
46
- }
47
-
48
- await eachPlugins(async function ({ file, dir }) {
49
- const { ns } = this
50
- await collect.call(me, { type: 'team', container: 'Routes', handler, file, ns, dir })
51
- }, { glob: 'route/team.*', prefix: this.ns })
52
- }