@govish/shared-services 1.3.0 → 1.5.1

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.
@@ -39,6 +39,16 @@ var AuditEventType;
39
39
  AuditEventType["ENDPOINT_ACCESSED"] = "endpoint_accessed";
40
40
  AuditEventType["UNAUTHORIZED_ACCESS"] = "unauthorized_access";
41
41
  AuditEventType["FORBIDDEN_ACCESS"] = "forbidden_access";
42
+ // IPRS events
43
+ AuditEventType["IPRS_PERSON_SEARCHED"] = "iprs_person_searched";
44
+ AuditEventType["IPRS_PERSON_FETCHED_FROM_VPS"] = "iprs_person_fetched_from_vps";
45
+ AuditEventType["IPRS_PERSON_STORED"] = "iprs_person_stored";
46
+ AuditEventType["IPRS_PERSON_UPDATED"] = "iprs_person_updated";
47
+ // Vehicle events
48
+ AuditEventType["VEHICLE_SEARCHED"] = "vehicle_searched";
49
+ AuditEventType["VEHICLE_FETCHED_FROM_IPRS"] = "vehicle_fetched_from_iprs";
50
+ AuditEventType["VEHICLE_STORED"] = "vehicle_stored";
51
+ AuditEventType["VEHICLE_UPDATED"] = "vehicle_updated";
42
52
  })(AuditEventType || (exports.AuditEventType = AuditEventType = {}));
43
53
  class AuditService {
44
54
  constructor(deps) {
@@ -294,6 +304,7 @@ class AuditService {
294
304
  const officer = req.officer; // Use officer directly, as it has all the properties
295
305
  const device = req.device;
296
306
  const apiKey = req.apiKey;
307
+ console.log('🔑 [Audit] API Key:', apiKey);
297
308
  const authenticatedMicroservice = req.microservice;
298
309
  // Get microservice name from environment or default
299
310
  const microserviceName = this.serverName || 'Package Name';
@@ -303,9 +314,11 @@ class AuditService {
303
314
  eventType = AuditService.determineEventType(req);
304
315
  }
305
316
  // Build audit payload - start with base fields
317
+ // Note: API key fields will be set later if API key is detected
318
+ // microservice should always be the receiving service (from SERVER_NAME), not the source microservice
306
319
  const auditPayload = {
307
320
  timestamp: new Date().toISOString(),
308
- microservice: authenticatedMicroservice || microserviceName,
321
+ microservice: microserviceName, // Always use receiving service name from SERVER_NAME
309
322
  event_type: eventType,
310
323
  endpoint: req.originalUrl || req.url,
311
324
  method: req.method,
@@ -313,6 +326,12 @@ class AuditService {
313
326
  query_params: Object.keys(req.query).length > 0 ? req.query : undefined,
314
327
  ip_address,
315
328
  user_agent,
329
+ api_key_id: apiKey === null || apiKey === void 0 ? void 0 : apiKey.id,
330
+ api_key_name: apiKey === null || apiKey === void 0 ? void 0 : apiKey.description,
331
+ api_key_description: apiKey === null || apiKey === void 0 ? void 0 : apiKey.description,
332
+ api_key_preview: (apiKey === null || apiKey === void 0 ? void 0 : apiKey.key) ? apiKey.key.substring(0, 15) + '...' : undefined,
333
+ source_microservice: apiKey === null || apiKey === void 0 ? void 0 : apiKey.microservice,
334
+ authenticated_microservice: apiKey === null || apiKey === void 0 ? void 0 : apiKey.microservice,
316
335
  };
317
336
  // Add officer information if authenticated
318
337
  if (officer) {
@@ -336,25 +355,92 @@ class AuditService {
336
355
  auditPayload.device_device_id = device.device_id;
337
356
  }
338
357
  // Set user type and add API Key / Microservice information
339
- if (authType === 'api_key' || authType === 'microservice') {
340
- auditPayload.user_type = 'microservice';
341
- auditPayload.authenticated_microservice = authenticatedMicroservice || (apiKey === null || apiKey === void 0 ? void 0 : apiKey.microservice);
342
- auditPayload.api_key_id = apiKey === null || apiKey === void 0 ? void 0 : apiKey.id;
343
- // Set microservice name in the main microservice field
344
- if (authenticatedMicroservice || (apiKey === null || apiKey === void 0 ? void 0 : apiKey.microservice)) {
345
- auditPayload.microservice = authenticatedMicroservice || apiKey.microservice;
358
+ // IMPORTANT: Always check for API key presence regardless of authType, as API key might be present
359
+ // even when authType is set to 'officer' or 'both' due to priority logic
360
+ const hasApiKey = !!(apiKey || authenticatedMicroservice);
361
+ const sourceMicroservice = (apiKey === null || apiKey === void 0 ? void 0 : apiKey.microservice) || authenticatedMicroservice;
362
+ // Always include API key information if present
363
+ if (hasApiKey) {
364
+ // Include API key object fields only if apiKey object exists
365
+ if (apiKey) {
366
+ auditPayload.api_key_id = apiKey.id;
367
+ auditPayload.api_key_name = apiKey.description || undefined; // API key name (from description field)
368
+ auditPayload.api_key_description = apiKey.description || undefined; // Keep description for backward compatibility
369
+ auditPayload.api_key_preview = apiKey.key ? apiKey.key.substring(0, 15) + '...' : undefined;
370
+ }
371
+ // ALWAYS include microservice information if authenticatedMicroservice is detected
372
+ // This works even if apiKey object is not attached
373
+ // Note: microservice field should always be the receiving service (from SERVER_NAME)
374
+ // source_microservice and authenticated_microservice are the API key's microservice
375
+ if (sourceMicroservice) {
376
+ auditPayload.source_microservice = sourceMicroservice;
377
+ auditPayload.authenticated_microservice = sourceMicroservice;
378
+ // Do NOT overwrite microservice - it should always be the receiving service
379
+ }
380
+ else if (authenticatedMicroservice) {
381
+ // Fallback: use authenticatedMicroservice directly if sourceMicroservice is not set
382
+ auditPayload.source_microservice = authenticatedMicroservice;
383
+ auditPayload.authenticated_microservice = authenticatedMicroservice;
384
+ // Do NOT overwrite microservice - it should always be the receiving service
346
385
  }
347
- // For microservice authentication, we don't have a user_id
386
+ // Log API key presence with full details
387
+ this.debugLog('API Key Detected', {
388
+ apiKeyId: (apiKey === null || apiKey === void 0 ? void 0 : apiKey.id) || 'N/A',
389
+ apiKeyName: (apiKey === null || apiKey === void 0 ? void 0 : apiKey.description) || sourceMicroservice || authenticatedMicroservice || 'N/A',
390
+ apiKeyPreview: (apiKey === null || apiKey === void 0 ? void 0 : apiKey.key) ? apiKey.key.substring(0, 15) + '...' : 'N/A',
391
+ sourceMicroservice: sourceMicroservice || authenticatedMicroservice,
392
+ hasApiKeyObject: !!apiKey,
393
+ endpoint: auditPayload.endpoint
394
+ });
395
+ }
396
+ // Now set user_type based on authType and what's actually present
397
+ if (authType === 'api_key_and_officer') {
398
+ // API Key + Officer authentication (may also have device)
399
+ auditPayload.user_type = 'api_key_and_officer';
400
+ // Include officer information (officer is already set above)
401
+ if (officer) {
402
+ auditPayload.user_id = officer.id;
403
+ auditPayload.officer_id = officer.id;
404
+ auditPayload.officer_name = officer.name || undefined;
405
+ auditPayload.officer_service_number = officer.service_number || undefined;
406
+ auditPayload.officer_email = officer.email || undefined;
407
+ }
408
+ // Explicitly preserve device information if present (device info was added earlier, but ensure it's preserved)
409
+ if (device) {
410
+ const deviceId = typeof device.id === 'string' ? parseInt(device.id, 10) : device.id;
411
+ if (!isNaN(deviceId)) {
412
+ auditPayload.device_id = deviceId;
413
+ }
414
+ auditPayload.device_device_id = device.device_id;
415
+ }
416
+ }
417
+ else if (authType === 'api_key' || authType === 'microservice') {
418
+ auditPayload.user_type = 'microservice';
419
+ // For microservice authentication, we don't have a user_id unless device is present
420
+ // (device info is already set above if present)
348
421
  }
349
422
  else if (authType === 'both') {
350
- auditPayload.user_type = 'both';
423
+ // Device + Officer - but check if API key is also present
424
+ if (hasApiKey) {
425
+ // All three: API Key + Device + Officer
426
+ auditPayload.user_type = 'api_key_and_officer'; // Upgrade to api_key_and_officer since API key is present
427
+ }
428
+ else {
429
+ auditPayload.user_type = 'both';
430
+ }
351
431
  // When both are authenticated, prioritize officer as user_id
352
432
  if (officer) {
353
433
  auditPayload.user_id = officer.id;
354
434
  }
355
435
  }
356
436
  else if (authType === 'device') {
357
- auditPayload.user_type = 'device';
437
+ // Device only - but check if API key is also present
438
+ if (hasApiKey) {
439
+ auditPayload.user_type = 'api_key'; // Upgrade to api_key since API key is present
440
+ }
441
+ else {
442
+ auditPayload.user_type = 'device';
443
+ }
358
444
  // Use device id as user_id when only device is authenticated
359
445
  if (device) {
360
446
  const deviceId = typeof device.id === 'string' ? parseInt(device.id, 10) : device.id;
@@ -364,12 +450,85 @@ class AuditService {
364
450
  }
365
451
  }
366
452
  else if (authType === 'officer') {
367
- auditPayload.user_type = 'officer';
453
+ // Officer only - but check if API key is also present
454
+ if (hasApiKey) {
455
+ auditPayload.user_type = 'api_key_and_officer'; // Upgrade to api_key_and_officer since API key is present
456
+ this.debugLog('Upgraded user_type from "officer" to "api_key_and_officer" because API key detected', {
457
+ sourceMicroservice: sourceMicroservice,
458
+ apiKeyId: apiKey === null || apiKey === void 0 ? void 0 : apiKey.id,
459
+ hasApiKeyObject: !!apiKey
460
+ });
461
+ // Explicitly preserve device information if present when upgrading from officer to api_key_and_officer
462
+ if (device) {
463
+ const deviceId = typeof device.id === 'string' ? parseInt(device.id, 10) : device.id;
464
+ if (!isNaN(deviceId)) {
465
+ auditPayload.device_id = deviceId;
466
+ }
467
+ auditPayload.device_device_id = device.device_id;
468
+ }
469
+ }
470
+ else {
471
+ auditPayload.user_type = 'officer';
472
+ }
368
473
  // Use officer id as user_id when only officer is authenticated
369
474
  if (officer) {
370
475
  auditPayload.user_id = officer.id;
371
476
  }
372
477
  }
478
+ // Log when API key is present but wasn't reflected in authType
479
+ if (hasApiKey && authType !== 'api_key' && authType !== 'api_key_and_officer' && authType !== 'microservice') {
480
+ this.debugLog('API Key detected but authType was different', {
481
+ authType,
482
+ apiKeyId: apiKey === null || apiKey === void 0 ? void 0 : apiKey.id,
483
+ sourceMicroservice: sourceMicroservice,
484
+ finalUserType: auditPayload.user_type,
485
+ hasApiKeyObject: !!apiKey,
486
+ authenticatedMicroservice: authenticatedMicroservice
487
+ });
488
+ }
489
+ // Final check: Ensure user_type matches what we actually have
490
+ if (hasApiKey && officer && auditPayload.user_type !== 'api_key_and_officer') {
491
+ this.debugLog('Force upgrading user_type to api_key_and_officer (final check)', {
492
+ currentUserType: auditPayload.user_type,
493
+ hasApiKey: hasApiKey,
494
+ hasOfficer: !!officer,
495
+ sourceMicroservice: sourceMicroservice
496
+ });
497
+ auditPayload.user_type = 'api_key_and_officer';
498
+ // Explicitly preserve device information when force upgrading to api_key_and_officer
499
+ if (device) {
500
+ const deviceId = typeof device.id === 'string' ? parseInt(device.id, 10) : device.id;
501
+ if (!isNaN(deviceId)) {
502
+ auditPayload.device_id = deviceId;
503
+ }
504
+ auditPayload.device_device_id = device.device_id;
505
+ }
506
+ }
507
+ // Final validation: Ensure API key information is included if payload.microservice was provided
508
+ // This is a safety check in case payload parameter contained microservice info that wasn't processed
509
+ // Note: auditPayload.microservice should always be microserviceName (receiving service) at this point
510
+ if (payload.microservice && payload.microservice !== microserviceName && !auditPayload.source_microservice) {
511
+ // Payload contained a microservice different from receiving service, use it as source
512
+ auditPayload.source_microservice = payload.microservice;
513
+ auditPayload.authenticated_microservice = payload.microservice;
514
+ // Upgrade user_type if officer is present
515
+ if (auditPayload.officer_id && auditPayload.user_type === 'officer') {
516
+ auditPayload.user_type = 'api_key_and_officer';
517
+ this.debugLog('Final safety check: Upgraded user_type to api_key_and_officer', {
518
+ microservice: auditPayload.microservice,
519
+ receivingMicroservice: microserviceName,
520
+ officerId: auditPayload.officer_id
521
+ });
522
+ // Explicitly preserve device information when upgrading from officer to api_key_and_officer in final validation
523
+ if (device) {
524
+ const deviceId = typeof device.id === 'string' ? parseInt(device.id, 10) : device.id;
525
+ if (!isNaN(deviceId)) {
526
+ auditPayload.device_id = deviceId;
527
+ }
528
+ auditPayload.device_device_id = device.device_id;
529
+ }
530
+ }
531
+ }
373
532
  // Extract arrest-specific information from request body or response
374
533
  this.addArrestInformation(req, auditPayload, payload);
375
534
  // Merge custom keys from payload
@@ -382,6 +541,69 @@ class AuditService {
382
541
  auditPayload[key] = payload[key];
383
542
  }
384
543
  });
544
+ // CRITICAL: Final check AFTER payload merge to ensure API key detection is not overridden
545
+ // If microservice indicates API key usage but user_type doesn't reflect it, fix it
546
+ // Check if API key was used by checking if authenticatedMicroservice or apiKey exists
547
+ // This works regardless of SERVER_NAME configuration
548
+ const finalHasApiKey = !!(apiKey || authenticatedMicroservice || auditPayload.source_microservice);
549
+ const finalSourceMicroservice = sourceMicroservice || auditPayload.source_microservice || auditPayload.microservice;
550
+ // CRITICAL: Preserve device information after payload merge (in case payload merge cleared it)
551
+ // Device information should always be preserved if device was authenticated
552
+ if (device) {
553
+ const deviceId = typeof device.id === 'string' ? parseInt(device.id, 10) : device.id;
554
+ if (!isNaN(deviceId)) {
555
+ auditPayload.device_id = deviceId;
556
+ }
557
+ auditPayload.device_device_id = device.device_id;
558
+ }
559
+ if (finalHasApiKey && (authenticatedMicroservice || apiKey)) {
560
+ // ALWAYS set microservice fields when API key is detected
561
+ // Don't check if they already exist - ensure they're set (in case payload merge cleared them)
562
+ // Note: microservice field should always be the receiving service (from SERVER_NAME)
563
+ auditPayload.source_microservice = finalSourceMicroservice;
564
+ auditPayload.authenticated_microservice = finalSourceMicroservice;
565
+ // Do NOT overwrite microservice - it should always be the receiving service
566
+ // Include API key object fields if available
567
+ if (apiKey) {
568
+ // Always set these if apiKey exists, even if already set (in case payload merge cleared them)
569
+ auditPayload.api_key_id = apiKey.id;
570
+ auditPayload.api_key_name = apiKey.description || undefined;
571
+ auditPayload.api_key_description = apiKey.description || undefined;
572
+ auditPayload.api_key_preview = apiKey.key ? apiKey.key.substring(0, 15) + '...' : undefined;
573
+ }
574
+ // ALWAYS upgrade user_type when API key is detected and officer is present
575
+ if (auditPayload.officer_id && auditPayload.user_type === 'officer') {
576
+ auditPayload.user_type = 'api_key_and_officer';
577
+ this.debugLog('🔧 [FINAL] Upgraded user_type to api_key_and_officer after payload merge', {
578
+ microservice: auditPayload.microservice,
579
+ receivingMicroservice: microserviceName,
580
+ officerId: auditPayload.officer_id,
581
+ sourceMicroservice: finalSourceMicroservice
582
+ });
583
+ // Explicitly preserve device information when upgrading from officer to api_key_and_officer after payload merge
584
+ if (device) {
585
+ const deviceId = typeof device.id === 'string' ? parseInt(device.id, 10) : device.id;
586
+ if (!isNaN(deviceId)) {
587
+ auditPayload.device_id = deviceId;
588
+ }
589
+ auditPayload.device_device_id = device.device_id;
590
+ }
591
+ }
592
+ else if (auditPayload.user_type === 'both' && finalHasApiKey) {
593
+ auditPayload.user_type = 'api_key_and_officer';
594
+ this.debugLog('🔧 [FINAL] Upgraded user_type from "both" to "api_key_and_officer" after payload merge', {
595
+ microservice: auditPayload.microservice,
596
+ sourceMicroservice: finalSourceMicroservice
597
+ });
598
+ }
599
+ else if (!auditPayload.officer_id && auditPayload.user_type === 'device' && finalHasApiKey) {
600
+ auditPayload.user_type = 'api_key';
601
+ this.debugLog('🔧 [FINAL] Upgraded user_type from "device" to "api_key" after payload merge', {
602
+ microservice: auditPayload.microservice,
603
+ sourceMicroservice: finalSourceMicroservice
604
+ });
605
+ }
606
+ }
385
607
  // Build user info string for logging
386
608
  const userInfo = auditPayload.officer_name
387
609
  ? `${auditPayload.officer_name} (Officer ID: ${auditPayload.officer_id})`
@@ -460,6 +682,31 @@ class AuditService {
460
682
  return;
461
683
  }
462
684
  this.debugLog('All checks passed - proceeding to send');
685
+ // Final safety check: If microservice indicates API key but fields are missing, add them
686
+ // This catches any edge cases where fields might have been cleared
687
+ if (auditPayload.microservice &&
688
+ auditPayload.microservice !== microserviceName &&
689
+ (!auditPayload.source_microservice || !auditPayload.authenticated_microservice)) {
690
+ auditPayload.source_microservice = auditPayload.microservice;
691
+ auditPayload.authenticated_microservice = auditPayload.microservice;
692
+ // Upgrade user_type if needed
693
+ if (auditPayload.officer_id && auditPayload.user_type === 'officer') {
694
+ auditPayload.user_type = 'api_key_and_officer';
695
+ this.debugLog('🔧 [PRE-SEND] Final safety check: Upgraded user_type to api_key_and_officer', {
696
+ microservice: auditPayload.microservice,
697
+ receivingMicroservice: microserviceName,
698
+ officerId: auditPayload.officer_id
699
+ });
700
+ // Explicitly preserve device information when upgrading from officer to api_key_and_officer in pre-send check
701
+ if (device) {
702
+ const deviceId = typeof device.id === 'string' ? parseInt(device.id, 10) : device.id;
703
+ if (!isNaN(deviceId)) {
704
+ auditPayload.device_id = deviceId;
705
+ }
706
+ auditPayload.device_device_id = device.device_id;
707
+ }
708
+ }
709
+ }
463
710
  const topic = this.auditTopic || 'audit-log';
464
711
  const kafkaMessage = {
465
712
  key: `${auditPayload.method}-${auditPayload.endpoint}-${Date.now()}`,
@@ -654,6 +901,36 @@ class AuditService {
654
901
  return AuditEventType.ARRESTS_LISTED;
655
902
  }
656
903
  }
904
+ // IPRS events
905
+ if (fullPath.includes('/iprs') || path.includes('/iprs')) {
906
+ if (method === 'POST' && (fullPath.includes('/search') || path.includes('/search'))) {
907
+ return AuditEventType.IPRS_PERSON_SEARCHED;
908
+ }
909
+ else if (method === 'GET' && (fullPath.includes('/fetch') || path.includes('/fetch'))) {
910
+ return AuditEventType.IPRS_PERSON_FETCHED_FROM_VPS;
911
+ }
912
+ else if (method === 'POST' && (fullPath.includes('/store') || path.includes('/store'))) {
913
+ return AuditEventType.IPRS_PERSON_STORED;
914
+ }
915
+ else if (method === 'PUT') {
916
+ return AuditEventType.IPRS_PERSON_UPDATED;
917
+ }
918
+ }
919
+ // Vehicle events
920
+ if (fullPath.includes('/vehicle') || path.includes('/vehicles')) {
921
+ if (method === 'GET' && (fullPath.includes('/search') || path.includes('/search'))) {
922
+ return AuditEventType.VEHICLE_SEARCHED;
923
+ }
924
+ else if (method === 'GET' && (fullPath.includes('/fetch') || path.includes('/fetch'))) {
925
+ return AuditEventType.VEHICLE_FETCHED_FROM_IPRS;
926
+ }
927
+ else if (method === 'POST' && (fullPath.includes('/store') || path.includes('/store'))) {
928
+ return AuditEventType.VEHICLE_STORED;
929
+ }
930
+ else if (method === 'PUT') {
931
+ return AuditEventType.VEHICLE_UPDATED;
932
+ }
933
+ }
657
934
  // Default to endpoint accessed
658
935
  return AuditEventType.ENDPOINT_ACCESSED;
659
936
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@govish/shared-services",
3
- "version": "1.3.0",
4
- "description": "Govish shared services package - officer, device, station, penal code, station-duty block, and rank leave days cache services; API key service; audit logging; authentication middleware",
3
+ "version": "1.5.1",
4
+ "description": "Govish shared services package - officer, device, station, penal code, station-duty block, and rank leave days cache services; API key service; audit logging; authentication middleware with support for API key + officer combined authentication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "files": [
@@ -23,6 +23,11 @@
23
23
  "audit",
24
24
  "authentication",
25
25
  "middleware",
26
+ "api-key",
27
+ "jwt",
28
+ "device-authentication",
29
+ "officer-authentication",
30
+ "microservice-authentication",
26
31
  "typescript"
27
32
  ],
28
33
  "author": "",