scimgateway 5.0.7 → 5.0.8

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/README.md CHANGED
@@ -258,8 +258,8 @@ Below shows an example of config\plugin-saphana.json
258
258
  ],
259
259
  "bearerOAuth": [
260
260
  {
261
- "client_id": null,
262
- "client_secret": null,
261
+ "clientId": null,
262
+ "clientSecret": null,
263
263
  "readOnly": false,
264
264
  "baseEntities": []
265
265
  }
@@ -398,7 +398,7 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
398
398
 
399
399
  - **auth.bearerJwt** - Array of one or more standard JWT objects. Using **secret** or **publicKey** for signature verification. publicKey should be set to the filename of public key or certificate pem-file located in `<package-root>\config\certs` or absolute path being used. Clear text secret will become encrypted when gateway is started. **options.issuer** is mandatory. Other options may also be included according to jsonwebtoken npm package definition.
400
400
 
401
- - **auth.bearerOAuth** - Array of one or more Client Credentials OAuth configuration objects. **`client_id`** and **`client_secret`** are mandatory. client_secret value will become encrypted when gateway is started. OAuth token request url is **/oauth/token** e.g. http://localhost:8880/oauth/token
401
+ - **auth.bearerOAuth** - Array of one or more Client Credentials OAuth configuration objects. **`clientId`** and **`clientSecret`** are mandatory. clientSecret value will become encrypted when gateway is started. OAuth token request url is **/oauth/token** e.g. http://localhost:8880/oauth/token
402
402
 
403
403
  - **auth.passThrough** - Setting **auth.passThrough.enabled=true** will bypass SCIM Gateway authentication. Gateway will instead pass ctx containing authentication header to the plugin. Plugin could then use this information for endpoint authentication and we don't have any password/token stored at the gateway. Note, this also requires plugin binary having `scimgateway.authPassThroughAllowed = true` and endpoint logic for handling/passing ctx.request.header.authorization
404
404
 
@@ -462,18 +462,18 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
462
462
 
463
463
  Configuration notes when using default configuration oauth and tenantIdGUID - Microsoft Exchange Online (ExO):
464
464
 
465
- - Entra ID application must have application permissions "**Mail.Send**"
466
- - To prevent the sending of emails from any defined mailboxes, an ExO **ApplicationAccessPolicy** must be defined through PowerShell.
465
+ - Entra ID application must have application permissions `Mail.Send`
466
+ - To prevent the sending of emails from any defined mailboxes, an ExO `ApplicationAccessPolicy` must be defined through PowerShell.
467
467
 
468
468
  First create a mail-enabled security-group that only includes those users (mailboxes) the application is allowed to send from
469
- Note, "mail enabled security" group cannot be created from portal, only from admin or admin.exchange console
469
+ Note, `mail enabled security group` cannot be created from portal, only from admin or admin.exchange console
470
470
 
471
471
  ##Connect to Exchange
472
472
  Install-Module -Name ExchangeOnlineManagement
473
473
  Connect-ExchangeOnline
474
474
 
475
475
  ##Create ApplicationAccessPolicy
476
- New-ApplicationAccessPolicy -AppId $AppClientID -PolicyScopeGroupId $MailEnabledSecurityGrpId -AccessRight RestrictAccess -Description "Restrict app to specific mailboxes"
476
+ New-ApplicationAccessPolicy -AppId <AppClientID> -PolicyScopeGroupId <MailEnabledSecurityGrpId> -AccessRight RestrictAccess -Description "Restrict app to specific mailboxes"
477
477
 
478
478
  - **stream** - See [SCIM Stream](https://elshaug.xyz/docs/scim-stream) for configuration details
479
479
 
@@ -1111,6 +1111,15 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
1111
1111
 
1112
1112
  ## Change log
1113
1113
 
1114
+ ### v5.0.8
1115
+
1116
+ [Fixed]
1117
+
1118
+ - Ensure Bun compatibility with Azure Reverse Proxy for large and long running response
1119
+ - HelperRest was not compatible with Node.js
1120
+ - plugin-mssql, some error handling should not throw an error
1121
+ - Configuration files updated according to the v5 configuration syntax of `scimgateway.auth.bearerOAuth` - `clientId/clientSecret` now replacing deprecated `client_id/client_secret`
1122
+
1114
1123
  ### v5.0.7
1115
1124
 
1116
1125
  [Improved]
@@ -1155,9 +1164,7 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
1155
1164
 
1156
1165
 
1157
1166
  **new configuration:**
1158
- Using Microsoft Exchange Online and oauth authencation which also is default and recommended by Microsoft
1159
- For other mail servers and options like SMTP AUTH (basic/oauth), please see configuration description
1160
- Plugin may also send mail using method scimgateway.sendMail()
1167
+ Using Microsoft Exchange Online and oauth authencation which also is default and recommended by Microsoft. For other mail servers and options like SMTP AUTH (basic/oauth), please see configuration description. Plugin may also send mail using method scimgateway.sendMail()
1161
1168
 
1162
1169
  {
1163
1170
  "scimgateway": {
@@ -1184,18 +1191,18 @@ Plugin may also send mail using method scimgateway.sendMail()
1184
1191
 
1185
1192
  Configuration notes when using oauth and tenantIdGUID - Microsoft Exchange Online (ExO):
1186
1193
 
1187
- - Entra ID application must have application permissions "**Mail.Send**"
1188
- - To prevent the sending of emails from any defined mailboxes, an ExO **ApplicationAccessPolicy** must be defined through PowerShell.
1194
+ - Entra ID application must have application permissions `Mail.Send`
1195
+ - To prevent the sending of emails from any defined mailboxes, an ExO `ApplicationAccessPolicy` must be defined through PowerShell.
1189
1196
 
1190
1197
  First create a mail-enabled security-group that only includes those users (mailboxes) the application is allowed to send from
1191
- Note, "mail enabled security" group cannot be created from portal, only from admin or admin.exchange console
1198
+ Note, `mail enabled security group` cannot be created from portal, only from admin or admin.exchange console
1192
1199
 
1193
1200
  ##Connect to Exchange
1194
1201
  Install-Module -Name ExchangeOnlineManagement
1195
1202
  Connect-ExchangeOnline
1196
1203
 
1197
1204
  ##Create ApplicationAccessPolicy
1198
- New-ApplicationAccessPolicy -AppId $AppClientID -PolicyScopeGroupId $MailEnabledSecurityGrpId -AccessRight RestrictAccess -Description "Restrict app to specific mailboxes"
1205
+ New-ApplicationAccessPolicy -AppId <AppClientID> -PolicyScopeGroupId <MailEnabledSecurityGrpId> -AccessRight RestrictAccess -Description "Restrict app to specific mailboxes"
1199
1206
 
1200
1207
 
1201
1208
  ### v5.0.5
@@ -1308,7 +1315,7 @@ Besides going from JavaScript to TypeScript, following can be mentioned:
1308
1315
 
1309
1316
  * Use scimgateway.HelperRest() for REST functionlity, also supports Auth PassThrough
1310
1317
  * scimgateway.endpointMapper() may be used for inbound/outbound attribute mappings
1311
- * In general when using TypeScript, variables should be type defined: `let isDone: boolean = false`, `catch (err: any)`, ...
1318
+ * In general when using TypeScript, variables should be type-defined: `let isDone: boolean = false`, `catch (err: any)`, ...
1312
1319
 
1313
1320
  ### v4.5.12
1314
1321
 
@@ -52,8 +52,8 @@
52
52
  ],
53
53
  "bearerOAuth": [
54
54
  {
55
- "client_id": null,
56
- "client_secret": null,
55
+ "clientId": null,
56
+ "clientSecret": null,
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
@@ -52,8 +52,8 @@
52
52
  ],
53
53
  "bearerOAuth": [
54
54
  {
55
- "client_id": null,
56
- "client_secret": null,
55
+ "clientId": null,
56
+ "clientSecret": null,
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
@@ -52,8 +52,8 @@
52
52
  ],
53
53
  "bearerOAuth": [
54
54
  {
55
- "client_id": null,
56
- "client_secret": null,
55
+ "clientId": null,
56
+ "clientSecret": null,
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
@@ -52,8 +52,8 @@
52
52
  ],
53
53
  "bearerOAuth": [
54
54
  {
55
- "client_id": null,
56
- "client_secret": null,
55
+ "clientId": null,
56
+ "clientSecret": null,
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
@@ -58,8 +58,8 @@
58
58
  ],
59
59
  "bearerOAuth": [
60
60
  {
61
- "client_id": null,
62
- "client_secret": null,
61
+ "clientId": null,
62
+ "clientSecret": null,
63
63
  "readOnly": false,
64
64
  "baseEntities": []
65
65
  }
@@ -52,8 +52,8 @@
52
52
  ],
53
53
  "bearerOAuth": [
54
54
  {
55
- "client_id": null,
56
- "client_secret": null,
55
+ "clientId": null,
56
+ "clientSecret": null,
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
@@ -52,8 +52,8 @@
52
52
  ],
53
53
  "bearerOAuth": [
54
54
  {
55
- "client_id": null,
56
- "client_secret": null,
55
+ "clientId": null,
56
+ "clientSecret": null,
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
@@ -52,8 +52,8 @@
52
52
  ],
53
53
  "bearerOAuth": [
54
54
  {
55
- "client_id": null,
56
- "client_secret": null,
55
+ "clientId": null,
56
+ "clientSecret": null,
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
@@ -52,8 +52,8 @@
52
52
  ],
53
53
  "bearerOAuth": [
54
54
  {
55
- "client_id": null,
56
- "client_secret": null,
55
+ "clientId": null,
56
+ "clientSecret": null,
57
57
  "readOnly": false,
58
58
  "baseEntities": []
59
59
  }
@@ -4,7 +4,7 @@ import { Buffer } from 'node:buffer'
4
4
  import fs from 'node:fs'
5
5
  import querystring from 'querystring'
6
6
  import * as utils from './utils.ts'
7
- import ScimGateway from 'scimgateway'
7
+ // import type { ScimGateway } from 'scimgateway' // comment out for supporting Node.js, using type any and no IntelliSense
8
8
 
9
9
  /**
10
10
  * HelperRest includes function doRequest() for doing REST calls
@@ -13,12 +13,12 @@ export class HelperRest {
13
13
  private lock = new utils.Lock()
14
14
  private _serviceClient: Record<string, any> = {}
15
15
  private config_entity: any
16
- private scimgateway: ScimGateway
16
+ private scimgateway: any
17
17
  private idleTimeout: number
18
18
  private graphUrl = 'https://graph.microsoft.com/beta' // beta instead of 'v1.0' gives all user attributes when no $select
19
19
 
20
- constructor(scimgateway: ScimGateway, optionalEntities?: Record<string, any>) {
21
- if (!(scimgateway instanceof ScimGateway)) throw new Error('HelperRest initialization error: argument scimgateway is not of type ScimGateway')
20
+ constructor(scimgateway: any, optionalEntities?: Record<string, any>) {
21
+ if (!scimgateway || !scimgateway.gwName) throw new Error('HelperRest initialization error: argument scimgateway is not of type ScimGateway')
22
22
  this.scimgateway = scimgateway
23
23
  this.idleTimeout = (scimgateway as any)?.config?.scimgateway.idleTimeout || 120
24
24
  this.idleTimeout = this.idleTimeout - 1
@@ -6,7 +6,7 @@
6
6
  // Purpose: SQL user-provisioning
7
7
  //
8
8
  // Prereq:
9
- // CREATE TABLE [User] (
9
+ // CREATE TABLE [Users] (
10
10
  // [UserID] VARCHAR(50) NOT NULL,
11
11
  // [Enabled] VARCHAR(50) NULL,
12
12
  // [Password] VARCHAR(50) NULL,
@@ -19,7 +19,7 @@
19
19
  // PRIMARY KEY ([UserID])
20
20
  // );
21
21
  //
22
- // CREATE TABLE [Group] (
22
+ // CREATE TABLE [Groups] (
23
23
  // [GroupID] VARCHAR(50) NOT NULL,
24
24
  // [Enabled] VARCHAR(50) NULL,
25
25
  // CONSTRAINT [PK_Group]
@@ -33,11 +33,11 @@
33
33
  // PRIMARY KEY ([GroupID],[UserID]),
34
34
  // CONSTRAINT [FK_U2G_Group]
35
35
  // FOREIGN KEY ([GroupID])
36
- // REFERENCES [Group]([GroupID])
36
+ // REFERENCES [Groups]([GroupID])
37
37
  // ON DELETE CASCADE,
38
38
  // CONSTRAINT [FK_U2G_Users]
39
39
  // FOREIGN KEY ([UserID])
40
- // REFERENCES [User]([UserID])
40
+ // REFERENCES [Users]([UserID])
41
41
  // ON DELETE CASCADE
42
42
  // );
43
43
  //
@@ -66,7 +66,7 @@ import { Connection, Request } from 'tedious'
66
66
  const ScimGateway: typeof import('scimgateway').ScimGateway = await (async () => {
67
67
  try {
68
68
  return (await import('scimgateway')).ScimGateway
69
- } catch (err) {
69
+ } catch (err: any) {
70
70
  const source = './scimgateway.ts'
71
71
  return (await import(source)).ScimGateway
72
72
  }
@@ -113,17 +113,13 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
113
113
  if (!sqlQuery) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
114
114
 
115
115
  try {
116
- return await new Promise(async (resolve, reject) => {
116
+ return await new Promise(async (resolve) => {
117
117
  const ret: any = { // itemsPerPage will be set by scimgateway
118
118
  Resources: [],
119
119
  totalResults: null,
120
120
  }
121
121
 
122
- const users: any[] = await query(sqlQuery, ctx).catch((err: any) => {
123
- const e = new Error(`${action} error: ${err.message}`)
124
- return reject(e)
125
- })
126
-
122
+ const users: any[] = await query(sqlQuery, ctx).catch(err => scimgateway.logWarn(baseEntity, `${action} warning: ${err.message}`))
127
123
  for (const user of users) {
128
124
  const scimUser = {
129
125
  id: user.UserID.value ? user.UserID.value : undefined,
@@ -155,7 +151,7 @@ scimgateway.createUser = async (baseEntity, userObj, ctx) => {
155
151
  scimgateway.logDebug(baseEntity, `handling "${action}" userObj=${JSON.stringify(userObj)}`)
156
152
 
157
153
  try {
158
- return await new Promise(async (resolve, reject) => {
154
+ return await new Promise(async (resolve) => {
159
155
  if (!userObj.name) userObj.name = {}
160
156
  if (!userObj.emails) userObj.emails = { work: {} }
161
157
  if (!userObj.phoneNumbers) userObj.phoneNumbers = { work: {} }
@@ -174,10 +170,7 @@ scimgateway.createUser = async (baseEntity, userObj, ctx) => {
174
170
  const sqlQuery = `insert into [Users] (UserID, Enabled, Password, FirstName, MiddleName, LastName, Email, MobilePhone)
175
171
  values (${insert.UserID}, ${insert.Enabled}, ${insert.Password}, ${insert.FirstName}, ${insert.MiddleName}, ${insert.LastName}, ${insert.Email}, ${insert.MobilePhone})`
176
172
 
177
- await query(sqlQuery, ctx).catch((err: any) => {
178
- const e = new Error(`${action} error: ${err.message}`)
179
- return reject(e)
180
- })
173
+ await query(sqlQuery, ctx).catch(err => scimgateway.logWarn(baseEntity, `${action} warning: ${err.message}`))
181
174
 
182
175
  resolve(null)
183
176
  }) // Promise
@@ -194,12 +187,9 @@ scimgateway.deleteUser = async (baseEntity, id, ctx) => {
194
187
  scimgateway.logDebug(baseEntity, `handling "${action}" id=${id}`)
195
188
 
196
189
  try {
197
- return await new Promise(async (resolve, reject) => {
190
+ return await new Promise(async (resolve) => {
198
191
  const sqlQuery = `delete from [Users] where UserID = '${id}'`
199
- await query(sqlQuery, ctx).catch((err: any) => {
200
- const e = new Error(`${action} error: ${err.message}`)
201
- return reject(e)
202
- })
192
+ await query(sqlQuery, ctx).catch(err => scimgateway.logWarn(baseEntity, `${action} warning: ${err.message}`))
203
193
 
204
194
  resolve(null)
205
195
  }) // Promise
@@ -216,7 +206,7 @@ scimgateway.modifyUser = async (baseEntity, id, attrObj, ctx) => {
216
206
  scimgateway.logDebug(baseEntity, `handling "${action}" id=${id} attrObj=${JSON.stringify(attrObj)}`)
217
207
 
218
208
  try {
219
- return await new Promise(async (resolve, reject) => {
209
+ return await new Promise(async (resolve) => {
220
210
  if (!attrObj.name) attrObj.name = {}
221
211
  if (!attrObj.emails) attrObj.emails = { work: {} }
222
212
  if (!attrObj.phoneNumbers) attrObj.phoneNumbers = { work: {} }
@@ -252,10 +242,7 @@ scimgateway.modifyUser = async (baseEntity, id, attrObj, ctx) => {
252
242
  sql = sql.substr(0, sql.length - 1) // remove trailing ","
253
243
 
254
244
  const sqlQuery = `update [Users] set ${sql} where UserID like '${id}'`
255
- await query(sqlQuery, ctx).catch((err: any) => {
256
- const e = new Error(`${action} error: ${err.message}`)
257
- return reject(e)
258
- })
245
+ await query(sqlQuery, ctx).catch(err => scimgateway.logWarn(baseEntity, `${action} warning: ${err.message}`))
259
246
 
260
247
  resolve(null)
261
248
  }) // Promise
@@ -296,16 +283,13 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
296
283
  if (!sqlQuery) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
297
284
 
298
285
  try {
299
- return await new Promise(async (resolve, reject) => {
286
+ return await new Promise(async (resolve) => {
300
287
  const ret: any = { // itemsPerPage will be set by scimgateway
301
288
  Resources: [],
302
289
  totalResults: null,
303
290
  }
304
291
 
305
- const groups = await query(sqlQuery, ctx).catch((err: any) => {
306
- const e = new Error(`${action} error: ${err.message}`)
307
- return reject(e)
308
- })
292
+ const groups: any[] = await query(sqlQuery, ctx).catch(err => scimgateway.logWarn(baseEntity, `${action} warning: ${err.message}`))
309
293
 
310
294
  for (const group of groups) {
311
295
  const scimGroup: Record<string, any> = {
@@ -316,7 +300,7 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
316
300
  }
317
301
 
318
302
  const sqlQuery = `select UserID from [Users2Group] where GroupID = '${scimGroup.id}'`
319
- const members = await query(sqlQuery, ctx).catch(e => console.warn(`${e}`))
303
+ const members = await query(sqlQuery, ctx).catch(err => scimgateway.logWarn(baseEntity, `${action} warning: ${err.message}`))
320
304
  for (const member of members) {
321
305
  const scimMember = {
322
306
  value: member.UserID.value,
@@ -343,21 +327,20 @@ scimgateway.createGroup = async (baseEntity, groupObj, ctx) => {
343
327
  scimgateway.logDebug(baseEntity, `handling "${action}" groupObj=${JSON.stringify(groupObj)}`)
344
328
 
345
329
  try {
346
- return await new Promise(async (resolve, reject) => {
330
+ return await new Promise(async (resolve) => {
347
331
  const insert = {
348
332
  GroupID: `'${groupObj.displayName}'`,
349
333
  Enabled: (groupObj.active) ? `'${groupObj.active}'` : '\'false\'',
350
334
  }
351
335
 
352
336
  const sqlQuery = `insert into [Groups] (GroupID, Enabled) values (${insert.GroupID}, ${insert.Enabled})`
353
- await query(sqlQuery, ctx).catch(e => console.warn(`${e}`))
354
-
355
- for (const member of groupObj.members) {
356
- const sqlQuery = `insert into [Users2Group] (UserID, GroupID) values ('${member.value}', ${insert.GroupID})`
357
- await query(sqlQuery, ctx).catch((err: any) => {
358
- const e = new Error(`${action} error: ${err.message}`)
359
- return reject(e)
360
- })
337
+ await query(sqlQuery, ctx).catch(err => scimgateway.logWarn(baseEntity, `${action} warning: ${err.message}`))
338
+
339
+ if (Array.isArray(groupObj.members) && groupObj.members) {
340
+ for (const member of groupObj.members) {
341
+ const sqlQuery = `insert into [Users2Group] (UserID, GroupID) values ('${member.value}', ${insert.GroupID})`
342
+ await query(sqlQuery, ctx).catch(err => scimgateway.logWarn(baseEntity, `${action} warning: ${err.message}`))
343
+ }
361
344
  }
362
345
 
363
346
  resolve(null)
@@ -375,12 +358,9 @@ scimgateway.deleteGroup = async (baseEntity, id, ctx) => {
375
358
  scimgateway.logDebug(baseEntity, `handling "${action}" id=${id}`)
376
359
 
377
360
  try {
378
- return await new Promise(async (resolve, reject) => {
361
+ return await new Promise(async (resolve) => {
379
362
  const sqlQuery = `delete from [Groups] where GroupID = '${id}'`
380
- await query(sqlQuery, ctx).catch((err: any) => {
381
- const e = new Error(`${action} error: ${err.message}`)
382
- return reject(e)
383
- })
363
+ await query(sqlQuery, ctx).catch(err => scimgateway.logWarn(baseEntity, `${action} warning: ${err.message}`))
384
364
 
385
365
  resolve(null)
386
366
  }) // Promise
@@ -409,25 +389,22 @@ scimgateway.modifyGroup = async (baseEntity, id, attrObj, ctx) => {
409
389
  // This BLINDLY inserts all user/groups and gracefully breaks on PK violation
410
390
  // for each existing membership
411
391
  if (Array.isArray(attrObj.members) && attrObj.members) {
412
- attrObj.members.forEach((member) => {
392
+ for (const member of attrObj.members) {
413
393
  if (member.operation == 'delete') {
414
394
  queries.push(`delete from [Users2Group] where GroupID='${id}' and UserID='${member.value}'`)
415
395
  } else {
416
396
  queries.push(`insert into [Users2Group] (UserID, GroupID) values ('${member.value}','${id}')`)
417
397
  }
418
- })
398
+ }
419
399
  }
420
400
 
421
401
  const sqlQuery = queries.join(';')
422
402
 
423
403
  try {
424
- return await new Promise(async (resolve, reject) => {
404
+ return await new Promise(async (resolve) => {
425
405
  if (sqlQuery) {
426
406
  scimgateway.logDebug(baseEntity, `sqlQuery: ${sqlQuery}`)
427
- await query(sqlQuery, ctx).catch((err: any) => {
428
- const e = new Error(`${action} error: ${err.message}`)
429
- return reject(e)
430
- })
407
+ await query(sqlQuery, ctx).catch(err => scimgateway.logWarn(baseEntity, `${action} warning: ${err.message}`))
431
408
  }
432
409
 
433
410
  resolve(null)
@@ -2367,13 +2367,25 @@ export class ScimGateway {
2367
2367
  if (!ctx.response.body) ctx.response.body = 'NOT_FOUND'
2368
2368
  break
2369
2369
  }
2370
- const response = new Response(ctx.response.body, { status: ctx.response.status, headers: ctx.response.headers })
2371
- if (ctx.response.body) {
2370
+ const body = ctx.response.body
2371
+ if (body) {
2372
2372
  try {
2373
- JSON.parse(ctx.response.body)
2374
- response.headers.set('content-type', 'application/scim+json; charset=utf-8')
2373
+ JSON.parse(body)
2374
+ ctx.response.headers.set('content-type', 'application/scim+json; charset=utf-8')
2375
2375
  } catch (err) { void 0 }
2376
2376
  }
2377
+ let response: Response
2378
+ if (typeof Bun !== 'undefined') {
2379
+ const stream = new ReadableStream({ // ensure Bun compatibility with Azure Reverse Proxy for large and long running response - header set by Bun: Transfer-Encoding: 'chunked'
2380
+ start(controller) {
2381
+ controller.enqueue(body)
2382
+ controller.close()
2383
+ },
2384
+ })
2385
+ response = new Response(body ? stream : undefined, { status: ctx.response.status, headers: ctx.response.headers })
2386
+ } else {
2387
+ response = new Response(body ? body : undefined, { status: ctx.response.status, headers: ctx.response.headers })
2388
+ }
2377
2389
  logResult(ctx)
2378
2390
  return response
2379
2391
  }
@@ -2698,6 +2710,13 @@ export class ScimGateway {
2698
2710
  this.logger.info(`${this.pluginName}[${baseEntity}] ${msg}`)
2699
2711
  }
2700
2712
 
2713
+ /**
2714
+ * logWarn logs warning message
2715
+ **/
2716
+ logWarn(baseEntity: string | undefined, msg: string) {
2717
+ this.logger.warn(`${this.pluginName}[${baseEntity}] ${msg}`)
2718
+ }
2719
+
2701
2720
  /**
2702
2721
  * logError logs error message
2703
2722
  **/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scimgateway",
3
- "version": "5.0.7",
3
+ "version": "5.0.8",
4
4
  "type": "module",
5
5
  "description": "Using SCIM protocol as a gateway for user provisioning to other endpoints",
6
6
  "author": "Jarle Elshaug <jarle.elshaug@gmail.com> (https://elshaug.xyz)",