@live-change/access-control-service 0.8.118 → 0.8.120

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.
Files changed (2) hide show
  1. package/invite.js +173 -44
  2. package/package.json +3 -3
package/invite.js CHANGED
@@ -2,6 +2,7 @@ import App from '@live-change/framework'
2
2
  const app = App.app()
3
3
  import definition from './definition.js'
4
4
  const config = definition.config
5
+ import pluralize from 'pluralize'
5
6
 
6
7
  const inviteMessageActionByObjectType = config.inviteMessageActionByObjectType ?? {}
7
8
 
@@ -190,6 +191,9 @@ definition.action({
190
191
  }
191
192
  })
192
193
 
194
+ import task from '@live-change/task-service/task.ts' // need to import taks.js to avoid circular dependency
195
+
196
+
193
197
  for(const contactType of config.contactTypes) {
194
198
 
195
199
  const contactTypeUpperCaseName = contactType[0].toUpperCase() + contactType.slice(1)
@@ -206,6 +210,52 @@ for(const contactType of config.contactTypes) {
206
210
  }
207
211
  }
208
212
 
213
+ async function doInvite(contact, objectType, object, invitationData, emit, trigger = app.trigger) {
214
+ const contactData = await app.viewGet('get' + contactTypeUName, { [contactType]: contact })
215
+ if(contactData?.user) { // user exists
216
+ const { user } = contactData
217
+ await trigger({ type: 'notify' }, {
218
+ sessionOrUserType: 'user_User',
219
+ sessionOrUser: user,
220
+ notificationType: 'accessControl_Invitation',
221
+ objectType,
222
+ object,
223
+ ...invitationData, id: undefined
224
+ })
225
+ emit({
226
+ type: 'userInvited',
227
+ user,
228
+ objectType, object,
229
+ ...invitationData, id: undefined
230
+ })
231
+ return 'userInvited'
232
+ } else {
233
+ // Authenticate with message because we will create account later
234
+ const messageData = {
235
+ objectType, object,
236
+ ...invitationData, id: undefined,
237
+ action: inviteMessageActionByObjectType[objectType] ?? 'inviteWithMessage'
238
+ }
239
+ await trigger({ type: 'authenticateWithMessage' }, {
240
+ contactType,
241
+ contact,
242
+ messageData,
243
+ action: 'inviteWithMessage',
244
+ actionProperties: { objectType, object },
245
+ targetPage: { name: 'accessControl:invitationAccepted', params: { objectType, object } },
246
+ fallbackPage: { name: 'accessControl:invitationFallback', params: { objectType, object } }
247
+ })
248
+ emit({
249
+ type: 'contactInvited',
250
+ contactType: contactTypeName + '_' + contactTypeUName,
251
+ contact,
252
+ objectType, object,
253
+ ...invitationData, id: undefined
254
+ })
255
+ return 'contactInvited'
256
+ }
257
+ }
258
+
209
259
  definition.action({
210
260
  name: 'invite' + contactTypeUpperCaseName,
211
261
  waitForEvents: true,
@@ -221,9 +271,9 @@ for(const contactType of config.contactTypes) {
221
271
  ...contactTypeProperties,
222
272
  ...invitationProperties
223
273
  },
224
- access: (params, { client, context, visibilityTest }) =>
274
+ access: (params, { client, context, visibilityTest}) =>
225
275
  visibilityTest || access.clientCanInvite(client, params),
226
- async execute(params, { client, service }, emit) {
276
+ async execute(params, { client, service, trigger }, emit) {
227
277
  const { [contactTypeName]: contact } = params
228
278
  const { objectType, object } = params
229
279
  const { roles } = params
@@ -236,52 +286,131 @@ for(const contactType of config.contactTypes) {
236
286
  }
237
287
 
238
288
  const [ fromType, from ] = client.user ? ['user_User', client.user] : ['session_Session', client.session]
239
- const invitationData = { fromType, from }
289
+ const invitationData = { fromType, from, roles }
290
+ for(const propertyName in invitationProperties) invitationData[propertyName] = params[propertyName]
291
+ await doInvite(contact, objectType, object, invitationData, emit, trigger)
292
+ }
293
+ })
294
+
295
+ const inviteOneTask = task({
296
+ name: "invite" + contactTypeUpperCaseName,
297
+ properties: {
298
+ objectType: {
299
+ type: String,
300
+ validation: ['nonEmpty']
301
+ },
302
+ object: {
303
+ type: String,
304
+ validation: ['nonEmpty']
305
+ },
306
+ fromType: {
307
+ type: String,
308
+ validation: ['nonEmpty']
309
+ },
310
+ from: {
311
+ type: String,
312
+ validation: ['nonEmpty']
313
+ },
314
+ ...contactTypeProperties,
315
+ ...invitationProperties
316
+ },
317
+ maxRetries: 1,
318
+ async execute(params, { service, task }, emit) {
319
+ const { [contactTypeName]: contact } = params
320
+ const { objectType, object } = params
321
+ const { roles } = params
322
+ const { fromType, from } = params
323
+ const invitationData = { fromType, from, roles }
324
+ return await doInvite(contact, objectType, object, invitationData, emit, trigger)
325
+ }
326
+ }, definition)
327
+
328
+ const inviteManyTask = task({
329
+ name: "inviteMany" + contactTypeUpperCaseName,
330
+ properties: {
331
+ objectType: {
332
+ type: String,
333
+ validation: ['nonEmpty']
334
+ },
335
+ object: {
336
+ type: String,
337
+ validation: ['nonEmpty']
338
+ },
339
+ fromType: {
340
+ type: String,
341
+ validation: ['nonEmpty']
342
+ },
343
+ from: {
344
+ type: String,
345
+ validation: ['nonEmpty']
346
+ },
347
+ ...invitationProperties,
348
+ contacts: {
349
+ type: Array,
350
+ of: {
351
+ type: Object,
352
+ properties: contactTypeProperties
353
+ }
354
+ }
355
+ },
356
+ async execute(params, { service, task }, emit) {
357
+ const { objectType, object } = params
358
+ const { roles } = params
359
+ const { fromType, from } = params
360
+ const invitationData = { fromType, from, roles }
240
361
  for(const propertyName in invitationProperties) invitationData[propertyName] = params[propertyName]
362
+ for(const contact of params.contacts) {
363
+ try {
364
+ await doInvite(contact[contactTypeName], objectType, object, invitationData, emit, trigger)
365
+ } catch(e) {
366
+ // ignore errors
367
+ }
368
+ }
369
+ }
370
+ }, definition)
241
371
 
242
- const contactData = (await service.trigger({ type: 'get' + contactTypeUName + 'OrNull' }, {
243
- [contactType]: contact,
244
- }))[0]
245
- if(contactData?.user) { // user exists
246
- const { user } = contactData
247
- await service.trigger({ type: 'notify' }, {
248
- sessionOrUserType: 'user_User',
249
- sessionOrUser: user,
250
- notificationType: 'accessControl_Invitation',
251
- objectType,
252
- object,
253
- ...invitationData, id: undefined
254
- })
255
- emit({
256
- type: 'userInvited',
257
- user,
258
- objectType, object,
259
- ...invitationData, id: undefined
260
- })
261
- } else {
262
- // Authenticate with message because we will create account later
263
- const messageData = {
264
- objectType, object,
265
- ...invitationData, id: undefined,
266
- action: inviteMessageActionByObjectType[objectType] ?? 'inviteWithMessage',
372
+ definition.action({
373
+ name: 'inviteMany' + pluralize(contactTypeUpperCaseName),
374
+ waitForEvents: true,
375
+ properties: {
376
+ objectType: {
377
+ type: String,
378
+ validation: ['nonEmpty']
379
+ },
380
+ object: {
381
+ type: String,
382
+ validation: ['nonEmpty']
383
+ },
384
+ ...invitationProperties,
385
+ contacts: {
386
+ type: Array,
387
+ of: {
388
+ type: Object,
389
+ properties: contactTypeProperties
267
390
  }
268
- await service.trigger({ type: 'authenticateWithMessage' }, {
269
- contactType,
270
- contact,
271
- messageData,
272
- action: 'inviteWithMessage',
273
- actionProperties: { objectType, object },
274
- targetPage: { name: 'accessControl:invitationAccepted', params: { objectType, object } },
275
- fallbackPage: { name: 'accessControl:invitationFallback', params: { objectType, object } }
276
- })
277
- emit({
278
- type: 'contactInvited',
279
- contactType: contactTypeName + '_' + contactTypeUName,
280
- contact,
281
- objectType, object,
282
- ...invitationData, id: undefined
283
- })
284
391
  }
392
+ },
393
+ access: (params, { client, context, visibilityTest}) =>
394
+ visibilityTest || access.clientCanInvite(client, params),
395
+ async execute(params, { client, service, trigger, command }, emit) {
396
+ const { [contactTypeName]: contact } = params
397
+ const { objectType, object } = params
398
+
399
+ const myRoles = await access.getClientObjectRoles(client, { objectType, object }, true)
400
+ if(!myRoles.includes('admin')) {
401
+ for(const requestedRole of roles) {
402
+ if(!myRoles.includes(requestedRole)) throw 'notAuthorized'
403
+ }
404
+ }
405
+
406
+ const [ fromType, from ] = client.user ? ['user_User', client.user] : ['session_Session', client.session]
407
+
408
+ await inviteManyTask.start({
409
+ ...params,
410
+ fromType, from,
411
+ ownerType: objectType,
412
+ owner: object,
413
+ }, 'action', command.id )
285
414
  }
286
415
  })
287
416
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/access-control-service",
3
- "version": "0.8.118",
3
+ "version": "0.8.120",
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.8.118"
24
+ "@live-change/framework": "^0.8.120"
25
25
  },
26
- "gitHead": "cfccde9b1d340cbb23df96a2f0fba72da0a6e3ab",
26
+ "gitHead": "adf31fd8365df0655d88293f96a93734058d3dd4",
27
27
  "type": "module"
28
28
  }