@live-change/access-control-service 0.9.28 → 0.9.29
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/index.js +1 -0
- package/indexes.js +480 -0
- package/invite.js +1 -1
- package/model.js +23 -0
- package/package.json +3 -3
package/index.js
CHANGED
package/indexes.js
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import App from '@live-change/framework'
|
|
2
|
+
const app = App.app()
|
|
3
|
+
import definition from './definition.js'
|
|
4
|
+
const config = definition.config
|
|
5
|
+
|
|
6
|
+
import { parentsSources } from './accessControlParents.js'
|
|
7
|
+
|
|
8
|
+
const AccessParent = definition.model({
|
|
9
|
+
name: "AccessParent",
|
|
10
|
+
properties: {
|
|
11
|
+
childType: {
|
|
12
|
+
type: String
|
|
13
|
+
},
|
|
14
|
+
property: {
|
|
15
|
+
type: String
|
|
16
|
+
},
|
|
17
|
+
type: {
|
|
18
|
+
type: String
|
|
19
|
+
},
|
|
20
|
+
possibleTypes: {
|
|
21
|
+
type: Array,
|
|
22
|
+
of: {
|
|
23
|
+
type: String
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
definition.afterStart(async () => {
|
|
30
|
+
const destObjects =
|
|
31
|
+
Object.entries(parentsSources)
|
|
32
|
+
.flatMap(([childType, properties]) =>
|
|
33
|
+
properties.map(property => ({
|
|
34
|
+
id: `${childType}.${property.property}`,
|
|
35
|
+
childType,
|
|
36
|
+
...property
|
|
37
|
+
}))
|
|
38
|
+
)
|
|
39
|
+
let idsSet = new Set(destObjects.map(obj => obj.id))
|
|
40
|
+
const existingParents = await AccessParent.rangeGet({})
|
|
41
|
+
const promises = []
|
|
42
|
+
for(const obj of destObjects) {
|
|
43
|
+
promises.push(AccessParent.create(obj))
|
|
44
|
+
}
|
|
45
|
+
for(const parent of existingParents) {
|
|
46
|
+
if(!idsSet.has(parent.id)) {
|
|
47
|
+
promises.push(AccessParent.delete(parent.id))
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
await Promise.all(promises)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
if(config.indexed) {
|
|
54
|
+
|
|
55
|
+
definition.index({
|
|
56
|
+
name: 'childByParent',
|
|
57
|
+
async function(input, output,
|
|
58
|
+
{ accessParentTableName }) {
|
|
59
|
+
|
|
60
|
+
const reindexerBucket = 128
|
|
61
|
+
|
|
62
|
+
function indexEntry(parentType, childType, parent, child, property) {
|
|
63
|
+
const indexPart = [parentType, parent, childType, child]
|
|
64
|
+
.map(v => JSON.stringify(v)).join(':')
|
|
65
|
+
return {
|
|
66
|
+
id: indexPart + '_' + property,
|
|
67
|
+
parentType,
|
|
68
|
+
parent,
|
|
69
|
+
childType,
|
|
70
|
+
child,
|
|
71
|
+
property
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function indexEntryByProperty(childType, item, property) {
|
|
76
|
+
const value = item?.[property.property]
|
|
77
|
+
if(!value) return
|
|
78
|
+
const type = property.type || item[property.property + 'Type']
|
|
79
|
+
return indexEntry(type, childType, value, item.id, property.property)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
class TableIndexer {
|
|
83
|
+
constructor(childType, properties) {
|
|
84
|
+
this.childType = childType
|
|
85
|
+
this.properties = properties
|
|
86
|
+
this.removedProperties = []
|
|
87
|
+
this.table = input.table(childType)
|
|
88
|
+
this.promise = this.table.onChange(async (item, oldItem) => await this.index(item, oldItem))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async removeProperty(property) {
|
|
92
|
+
const index = this.properties.indexOf(property)
|
|
93
|
+
if(index >= 0) {
|
|
94
|
+
this.properties.splice(index, 1)
|
|
95
|
+
this.removedProperties.push(property)
|
|
96
|
+
await this.reindex()
|
|
97
|
+
this.removedProperties.splice(this.removedProperties.indexOf(property), 1)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async addProperty(property) {
|
|
102
|
+
this.properties.push(property)
|
|
103
|
+
await this.reindex()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async reindex() {
|
|
107
|
+
let position = ''
|
|
108
|
+
while(true) {
|
|
109
|
+
const bucket = await this.table.get({
|
|
110
|
+
gt: position,
|
|
111
|
+
limit: reindexerBucket
|
|
112
|
+
})
|
|
113
|
+
for(const item of bucket) await this.index(item)
|
|
114
|
+
if(bucket.length < reindexerBucket) break
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async index(item, oldItem) {
|
|
119
|
+
output.debug("!!!", item, oldItem, this.properties)
|
|
120
|
+
for(const property of this.removedProperties) {
|
|
121
|
+
await output.remove(indexEntryByProperty(this.childType, item, property))
|
|
122
|
+
}
|
|
123
|
+
for(const property of this.properties) {
|
|
124
|
+
const newEntry = indexEntryByProperty(this.childType, item, property)
|
|
125
|
+
const oldEntry = indexEntryByProperty(this.childType, oldItem, property)
|
|
126
|
+
output.debug('<-->', newEntry, oldEntry)
|
|
127
|
+
if(newEntry || oldEntry) await output.change(newEntry, oldEntry)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const tableIndexers = new Map()
|
|
133
|
+
|
|
134
|
+
async function addParentProperty(property) {
|
|
135
|
+
let indexer = tableIndexers.get(property.childType)
|
|
136
|
+
if(!indexer) {
|
|
137
|
+
indexer = new TableIndexer(property.childType, [property.property])
|
|
138
|
+
tableIndexers.set(property.childType, indexer)
|
|
139
|
+
await indexer.promise
|
|
140
|
+
}
|
|
141
|
+
await indexer.addProperty(property)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function removeParentProperty(property) {
|
|
145
|
+
let indexer = tableIndexers.get(property.childType)
|
|
146
|
+
if(!indexer) return
|
|
147
|
+
indexer.removeProperty(property)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const accessParentTable = input.table(accessParentTableName)
|
|
151
|
+
let parentsLoaded = false
|
|
152
|
+
await accessParentTable.onChange((parentProperty, oldParentProperty) => {
|
|
153
|
+
if(!parentsLoaded) return
|
|
154
|
+
if(oldParentProperty && !parentProperty) removeParentProperty(oldParentProperty)
|
|
155
|
+
if(parentProperty) addParentProperty(parentProperty)
|
|
156
|
+
})
|
|
157
|
+
const initialParentsState = await accessParentTable.get({})
|
|
158
|
+
const propertiesByChildType = new Map()
|
|
159
|
+
for(const parentProperty of initialParentsState) {
|
|
160
|
+
const properties = propertiesByChildType.get(parentProperty.childType) || []
|
|
161
|
+
properties.push(parentProperty)
|
|
162
|
+
propertiesByChildType.set(parentProperty.childType, properties)
|
|
163
|
+
}
|
|
164
|
+
const indexerPromises = []
|
|
165
|
+
for(const [childType, properties] of propertiesByChildType) {
|
|
166
|
+
const indexer = new TableIndexer(childType, properties)
|
|
167
|
+
tableIndexers.set(childType, indexer)
|
|
168
|
+
indexerPromises.push(indexer.promise)
|
|
169
|
+
}
|
|
170
|
+
parentsLoaded = true
|
|
171
|
+
await Promise.all(indexerPromises)
|
|
172
|
+
},
|
|
173
|
+
parameters: {
|
|
174
|
+
accessParentTableName: definition.name + '_AccessParent'
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
definition.index({
|
|
179
|
+
name: 'parentByChild',
|
|
180
|
+
dependencies: ['childByParent'],
|
|
181
|
+
async function(input, output, { childByParentIndexName }) {
|
|
182
|
+
function mapper(entry) {
|
|
183
|
+
if(!entry) return null
|
|
184
|
+
const { parentType, parent, childType, child, property } = entry
|
|
185
|
+
const indexPart = [childType, child, parentType, parent]
|
|
186
|
+
.map(v => JSON.stringify(v)).join(':')
|
|
187
|
+
return {
|
|
188
|
+
...entry,
|
|
189
|
+
id: indexPart + '_' + property,
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const childByParentIndex = await input.index(childByParentIndexName)
|
|
194
|
+
await childByParentIndex.onChange(async (entry, oldEntry) =>
|
|
195
|
+
await output.change(mapper(entry), mapper(oldEntry))
|
|
196
|
+
)
|
|
197
|
+
},
|
|
198
|
+
parameters: {
|
|
199
|
+
childByParentIndexName: definition.name + '_childByParent'
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
definition.index({
|
|
204
|
+
name: 'pathsByAncestorDescendantRelation',
|
|
205
|
+
dependencies: ['childByParent', 'parentByChild'],
|
|
206
|
+
async function(input, output, { childByParentIndexName, parentByChildIndexName }) {
|
|
207
|
+
const childByParentIndex = await input.index(childByParentIndexName)
|
|
208
|
+
const parentByChildIndex = await input.index(parentByChildIndexName)
|
|
209
|
+
|
|
210
|
+
const bucketSize = 128
|
|
211
|
+
|
|
212
|
+
async function iterate(index, prefix, cb) {
|
|
213
|
+
let position = prefix + ':'
|
|
214
|
+
while(true) {
|
|
215
|
+
const bucket = await index.get({
|
|
216
|
+
gt: position,
|
|
217
|
+
lte: prefix + '_\xFF\xFF\xFF\xFF',
|
|
218
|
+
limit: bucketSize
|
|
219
|
+
})
|
|
220
|
+
for(const entry of bucket) await cb(entry)
|
|
221
|
+
if(bucket.length < bucketSize) break
|
|
222
|
+
position = bucket[bucket.length - 1].id
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function propagateChange(create, ancestorType, ancestor, descendantType, descendant,
|
|
227
|
+
intermediate = []) {
|
|
228
|
+
if(ancestorType === descendantType && ancestor === descendant) {// break recursion cycle
|
|
229
|
+
output.debug("FOUND PARENT RECURSION", ancestorType, ancestor, intermediate)
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
//output.debug("PROPAGATE", create, ancestorType, ancestor, descendantType, descendant, intermediate)
|
|
233
|
+
const hash = sha1([...intermediate].join('>'), 'base64')
|
|
234
|
+
const pathId = [ancestorType, ancestor, descendantType, descendant].map(v => JSON.stringify(v))
|
|
235
|
+
.join(':') + '_' + hash
|
|
236
|
+
const exists = await output.objectGet(pathId)
|
|
237
|
+
if(create === exists) return // no change
|
|
238
|
+
const entry = {
|
|
239
|
+
id: pathId,
|
|
240
|
+
ancestorType, ancestor, descendantType, descendant, intermediate, hash
|
|
241
|
+
}
|
|
242
|
+
if(create) {
|
|
243
|
+
await output.put(entry)
|
|
244
|
+
} else {
|
|
245
|
+
await output.delete(entry)
|
|
246
|
+
}
|
|
247
|
+
const descendantPrefix = [descendantType, descendant].map(v => JSON.stringify(v)).join(':')
|
|
248
|
+
const ancestorPrefix = [ancestorType, ancestor].map(v => JSON.stringify(v)).join(':')
|
|
249
|
+
await iterate(childByParentIndex, descendantPrefix, async descendantChild => {
|
|
250
|
+
await propagateChange(create,
|
|
251
|
+
ancestorType, ancestor, descendantChild.childType, descendantChild.child,
|
|
252
|
+
[...intermediate, descendantPrefix, descendantChild.property])
|
|
253
|
+
})
|
|
254
|
+
await iterate(parentByChildIndex, ancestorPrefix, async ancestorParent => {
|
|
255
|
+
await propagateChange(create,
|
|
256
|
+
ancestorParent.parentType, ancestorParent.parent, descendantType, descendant,
|
|
257
|
+
[ancestorParent.property, ancestorPrefix, ...intermediate])
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/// reacting to index that are generated from childByParent index, because it will be updated later.
|
|
262
|
+
await parentByChildIndex.onChange(async (entry, oldEntry) => {
|
|
263
|
+
const anyEntry = entry || oldEntry
|
|
264
|
+
await propagateChange(!!entry, entry.parentType, entry.parent, entry.childType, entry.child, [anyEntry.property])
|
|
265
|
+
})
|
|
266
|
+
},
|
|
267
|
+
parameters: {
|
|
268
|
+
childByParentIndexName: definition.name + '_childByParent',
|
|
269
|
+
parentByChildIndexName: definition.name + '_parentByChild'
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
definition.index({
|
|
274
|
+
name: 'expandedRoles',
|
|
275
|
+
async function(input, output, { accessIndexName, publicAccessTableName, pathsIndexName }) {
|
|
276
|
+
const bucketSize = 128
|
|
277
|
+
|
|
278
|
+
async function iterate(source, prefix, cb) {
|
|
279
|
+
let position = prefix + ':'
|
|
280
|
+
while(true) {
|
|
281
|
+
const bucket = await source.get({
|
|
282
|
+
gt: position,
|
|
283
|
+
lte: prefix + '_\xFF\xFF\xFF\xFF',
|
|
284
|
+
limit: bucketSize
|
|
285
|
+
})
|
|
286
|
+
for(const entry of bucket) await cb(entry)
|
|
287
|
+
if(bucket.length < bucketSize) break
|
|
288
|
+
position = bucket[bucket.length - 1].id
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const accessIndex = await input.index(accessIndexName)
|
|
293
|
+
const publicAccessTable = input.table(publicAccessTableName)
|
|
294
|
+
const pathsIndex = await input.index(pathsIndexName)
|
|
295
|
+
|
|
296
|
+
function updateRoles(descendantType, descendant, sessionOrUserType, sessionOrUser, pathId,
|
|
297
|
+
rolesAdded, rolesRemoved,
|
|
298
|
+
pathIdHash = sha1(pathId, 'base64')) {
|
|
299
|
+
const prefix = [descendantType, descendant, sessionOrUserType, sessionOrUser]
|
|
300
|
+
.map(v => JSON.stringify(v)).join(':')
|
|
301
|
+
const promises = []
|
|
302
|
+
for(const role of rolesAdded) {
|
|
303
|
+
promises.push(output.put({
|
|
304
|
+
id: prefix + ':' + JSON.stringify(role) + '_' + pathIdHash,
|
|
305
|
+
objectType: descendantType,
|
|
306
|
+
object: descendant,
|
|
307
|
+
sessionOrUserType,
|
|
308
|
+
sessionOrUser,
|
|
309
|
+
role,
|
|
310
|
+
path: pathId
|
|
311
|
+
}))
|
|
312
|
+
}
|
|
313
|
+
for(const role of rolesRemoved) {
|
|
314
|
+
promises.push(output.delete({
|
|
315
|
+
id: prefix + ':' + JSON.stringify(role) + '_' + pathIdHash
|
|
316
|
+
}))
|
|
317
|
+
}
|
|
318
|
+
return Promise.all(promises)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function rolesDiff(roles, oldRoles) {
|
|
322
|
+
if(!roles) roles = []
|
|
323
|
+
if(!oldRoles) oldRoles = []
|
|
324
|
+
const rolesAdded = roles.filter(r => !oldRoles.includes(r))
|
|
325
|
+
const rolesRemoved = oldRoles.filter(r => !roles.includes(r))
|
|
326
|
+
return [ rolesAdded, rolesRemoved ]
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async function handleAccessChanged(objectType, object, sessionOrUserType, sessionOrUser,
|
|
330
|
+
rolesAdded, rolesRemoved) {
|
|
331
|
+
/// Find object descendants
|
|
332
|
+
const pathsPrefix = [objectType, object].map(v => JSON.stringify(v)).join(':')
|
|
333
|
+
await iterate(pathsIndex, pathsPrefix, async path => {
|
|
334
|
+
const promises = []
|
|
335
|
+
const { descendantType, descendant, id: pathId } = path
|
|
336
|
+
const pathIdHash = sha1(pathId, 'base64')
|
|
337
|
+
await updateRoles(descendantType, descendant, sessionOrUserType, sessionOrUser, pathId,
|
|
338
|
+
rolesAdded, rolesRemoved, pathIdHash)
|
|
339
|
+
})
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async function handlePathChanged(path, oldPath) {
|
|
343
|
+
const existingPath = path || oldPath
|
|
344
|
+
const { ancestorType, ancestor, descendantType, descendant, id: pathId } = existingPath
|
|
345
|
+
const pathIdHash = sha1(pathId, 'base64')
|
|
346
|
+
const prefix = [ancestorType, ancestor].map(v => JSON.stringify(v)).join(':')
|
|
347
|
+
const publicAccess = await publicAccessTable.objectGet(prefix)
|
|
348
|
+
await Promise.all([
|
|
349
|
+
updateRoles(descendantType, descendant, 'publicSession', '', pathId,
|
|
350
|
+
path && publicAccess?.sessionRoles || [],
|
|
351
|
+
!path && publicAccess?.sessionRoles || [],
|
|
352
|
+
pathIdHash),
|
|
353
|
+
updateRoles(descendantType, descendant, 'publicUser', '', pathId,
|
|
354
|
+
path && publicAccess?.userRoles || [],
|
|
355
|
+
!path && publicAccess?.userRoles || [],
|
|
356
|
+
pathIdHash),
|
|
357
|
+
])
|
|
358
|
+
await iterate(accessIndex, prefix, async access => {
|
|
359
|
+
const { sessionOrUserType, sessionOrUser, roles } = access
|
|
360
|
+
await updateRoles(descendantType, descendant, sessionOrUserType, sessionOrUser, pathId,
|
|
361
|
+
path ? roles : [], path ? [] : roles, pathIdHash)
|
|
362
|
+
})
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
await accessIndex.onChange(async (access, oldAccess) => {
|
|
366
|
+
const existingAccess = access || oldAccess
|
|
367
|
+
const { objectType, object, sessionOrUserType, sessionOrUser } = existingAccess
|
|
368
|
+
const [ rolesAdded, rolesRemoved ] = rolesDiff(access?.roles, oldAccess?.roles)
|
|
369
|
+
await updateRoles(objectType, object, sessionOrUserType, sessionOrUser, null,
|
|
370
|
+
rolesAdded, rolesRemoved, 'self')
|
|
371
|
+
await handleAccessChanged(objectType, object, sessionOrUserType, sessionOrUser, rolesAdded, rolesRemoved)
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
await publicAccessTable.onChange(async (publicAccess, oldPublicAccess) => {
|
|
375
|
+
const existingPublicAccess = publicAccess || oldPublicAccess
|
|
376
|
+
const { objectType, object } = existingPublicAccess
|
|
377
|
+
const [sessionRolesAdded, sessionRolesRemoved] = rolesDiff(
|
|
378
|
+
publicAccess?.sessionRoles, oldPublicAccess?.sessionRoles
|
|
379
|
+
)
|
|
380
|
+
const [userRolesAdded, userRolesRemoved] = rolesDiff(
|
|
381
|
+
publicAccess?.userRoles, oldPublicAccess?.userRoles
|
|
382
|
+
)
|
|
383
|
+
await Promise.all([
|
|
384
|
+
updateRoles(objectType, object, 'publicSession', '', null,
|
|
385
|
+
sessionRolesAdded, sessionRolesRemoved, 'self'),
|
|
386
|
+
updateRoles(objectType, object, 'publicUser', '', null,
|
|
387
|
+
userRolesAdded, userRolesRemoved, 'self')
|
|
388
|
+
])
|
|
389
|
+
await handleAccessChanged(objectType, object, 'publicSession', '',
|
|
390
|
+
sessionRolesAdded, sessionRolesRemoved)
|
|
391
|
+
await handleAccessChanged(objectType, object, 'publicUser', '',
|
|
392
|
+
userRolesAdded, userRolesRemoved)
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
//await pathsIndex.onChange(handlePathChanged)
|
|
396
|
+
},
|
|
397
|
+
parameters: {
|
|
398
|
+
accessIndexName: definition.name + '_Access_byObjectExtended',
|
|
399
|
+
publicAccessTableName: definition.name + '_PublicAccess',
|
|
400
|
+
pathsIndexName: definition.name + '_pathsByAncestorDescendantRelation'
|
|
401
|
+
}
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
definition.index({
|
|
405
|
+
name: 'roleByOwnerAndObject',
|
|
406
|
+
async function(input, output, { expandedRolesIndexName }) {
|
|
407
|
+
const expandedRolesIndex = await input.index(expandedRolesIndexName)
|
|
408
|
+
await expandedRolesIndex.onChange(async (expandedRole, oldExpandedRole) => {
|
|
409
|
+
const existingExpandedRole = expandedRole || oldExpandedRole
|
|
410
|
+
const { sessionOrUserType, sessionOrUser, role, objectType, object } = existingExpandedRole
|
|
411
|
+
const sourcePrefix = existingExpandedRole.id.slice(0, existingExpandedRole.id.lastIndexOf('_'))
|
|
412
|
+
// counting needed only when delete detected!
|
|
413
|
+
const count = (expandedRole && 1) || (await expandedRolesIndex.count({
|
|
414
|
+
gte: sourcePrefix + ':',
|
|
415
|
+
lte: sourcePrefix + '_\xFF\xFF\xFF\xFF',
|
|
416
|
+
limit: 1
|
|
417
|
+
}))
|
|
418
|
+
if(count) {
|
|
419
|
+
await output.put({
|
|
420
|
+
id: [sessionOrUserType, sessionOrUser, objectType, object, role]
|
|
421
|
+
.map(v => JSON.stringify(v)).join(':'),
|
|
422
|
+
sessionOrUserType, sessionOrUser, role, objectType, object
|
|
423
|
+
})
|
|
424
|
+
} else {
|
|
425
|
+
await output.delete({
|
|
426
|
+
id: [sessionOrUserType, sessionOrUser, objectType, object, role]
|
|
427
|
+
.map(v => JSON.stringify(v)).join(':')
|
|
428
|
+
})
|
|
429
|
+
}
|
|
430
|
+
})
|
|
431
|
+
},
|
|
432
|
+
parameters: {
|
|
433
|
+
expandedRolesIndexName: definition.name + '_expandedRoles'
|
|
434
|
+
}
|
|
435
|
+
})
|
|
436
|
+
definition.index({
|
|
437
|
+
name: 'objectByOwnerAndRole',
|
|
438
|
+
async function(input, output, { rolesIndexName }) {
|
|
439
|
+
const rolesIndex = await input.index(rolesIndexName)
|
|
440
|
+
const mapper = (source) => {
|
|
441
|
+
if(!source) return null
|
|
442
|
+
const { sessionOrUserType, sessionOrUser, role, objectType, object } = source
|
|
443
|
+
return {
|
|
444
|
+
id: [sessionOrUserType, sessionOrUser, role, objectType, object]
|
|
445
|
+
.map(v => JSON.stringify(v)).join(':'),
|
|
446
|
+
sessionOrUserType, sessionOrUser, role, objectType, object
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
await rolesIndex.onChange(async (role, oldRole) => {
|
|
450
|
+
await output.change(mapper(role), mapper(oldRole))
|
|
451
|
+
})
|
|
452
|
+
},
|
|
453
|
+
parameters: {
|
|
454
|
+
rolesIndexName: definition.name + '_roleByOwnerAndObject'
|
|
455
|
+
}
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
definition.index({
|
|
459
|
+
name: 'ownerByObjectAndRole',
|
|
460
|
+
async function(input, output, { rolesIndexName }) {
|
|
461
|
+
const rolesIndex = await input.index(rolesIndexName)
|
|
462
|
+
const mapper = (source) => {
|
|
463
|
+
if(!source) return null
|
|
464
|
+
const { sessionOrUserType, sessionOrUser, role, objectType, object } = source
|
|
465
|
+
return {
|
|
466
|
+
id: [objectType, object, role, sessionOrUserType, sessionOrUser]
|
|
467
|
+
.map(v => JSON.stringify(v)).join(':'),
|
|
468
|
+
sessionOrUserType, sessionOrUser, role, objectType, object
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
await rolesIndex.onChange(async (role, oldRole) => {
|
|
472
|
+
await output.change(mapper(role), mapper(oldRole))
|
|
473
|
+
})
|
|
474
|
+
},
|
|
475
|
+
parameters: {
|
|
476
|
+
rolesIndexName: definition.name + '_roleByOwnerAndObject'
|
|
477
|
+
}
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
}
|
package/invite.js
CHANGED
package/model.js
CHANGED
|
@@ -30,6 +30,29 @@ const Access = definition.model({
|
|
|
30
30
|
byOwnerRoleAndObject: {
|
|
31
31
|
property: ['sessionOrUserType', 'sessionOrUser', 'roles', 'objectType', 'object'],
|
|
32
32
|
multi: true
|
|
33
|
+
},
|
|
34
|
+
byObjectExtended: {
|
|
35
|
+
function: async function(input, output, { tableName }) {
|
|
36
|
+
function mapper(access) {
|
|
37
|
+
return access && {
|
|
38
|
+
id: JSON.stringify(access.objectType) + ':' + JSON.stringify(access.object)
|
|
39
|
+
+ '_' + sha1(access.id, 'base64'),
|
|
40
|
+
objectType: access.objectType,
|
|
41
|
+
object: access.object,
|
|
42
|
+
sessionOrUserType: access.sessionOrUserType,
|
|
43
|
+
sessionOrUser: access.sessionOrUser,
|
|
44
|
+
roles: access.roles,
|
|
45
|
+
lastUpdate: access.lastUpdate
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const table = await input.table(tableName)
|
|
49
|
+
await table.onChange(
|
|
50
|
+
async (access, oldAccess) => output.change(mapper(access), mapper(oldAccess))
|
|
51
|
+
)
|
|
52
|
+
},
|
|
53
|
+
parameters: {
|
|
54
|
+
tableName: definition.name + '_Access'
|
|
55
|
+
}
|
|
33
56
|
}
|
|
34
57
|
},
|
|
35
58
|
properties: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-change/access-control-service",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.29",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"url": "https://www.viamage.com/"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@live-change/framework": "^0.9.
|
|
24
|
+
"@live-change/framework": "^0.9.29"
|
|
25
25
|
},
|
|
26
|
-
"gitHead": "
|
|
26
|
+
"gitHead": "425e1d43a7359693059b41692d1fe04e12d5f0b1",
|
|
27
27
|
"type": "module"
|
|
28
28
|
}
|