Skip to content

Commit c8e2502

Browse files
authored
Bugfix: Add safe navigation operator on LoginHistory from authSession records (#831)
* Added safe navigation operator on LoginHistory from AuthSession records * Removed a unuseful safe navigation operator * Updated LogEntryEventHandler to set Log__c.ImpersonatedBy__c when AuthSession data is queried async
1 parent d897e3f commit c8e2502

File tree

9 files changed

+118
-18
lines changed

9 files changed

+118
-18
lines changed

Diff for: README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55

66
The most robust observability solution for Salesforce experts. Built 100% natively on the platform, and designed to work seamlessly with Apex, Lightning Components, Flow, OmniStudio, and integrations.
77

8-
## Unlocked Package - v4.15.4
8+
## Unlocked Package - v4.15.5
99

10-
[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015oklQAA)
11-
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015oklQAA)
10+
[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015p5jQAA)
11+
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015p5jQAA)
1212
[![View Documentation](./images/btn-view-documentation.png)](https://github.com/jongpie/NebulaLogger/wiki)
1313

14-
`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000015oklQAA`
14+
`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000015p5jQAA`
1515

1616
---
1717

Diff for: nebula-logger/core/main/log-management/classes/LogEntryEventHandler.cls

+7-3
Original file line numberDiff line numberDiff line change
@@ -617,17 +617,21 @@ public without sharing class LogEntryEventHandler extends LoggerSObjectHandler {
617617
continue;
618618
}
619619

620-
log.LoginApplication__c = matchingAuthSessionProxy.LoginHistory.Application;
621-
log.LoginBrowser__c = matchingAuthSessionProxy.LoginHistory.Browser;
620+
log.LoginApplication__c = matchingAuthSessionProxy.LoginHistory?.Application;
621+
log.LoginBrowser__c = matchingAuthSessionProxy.LoginHistory?.Browser;
622622
log.LoginHistoryId__c = matchingAuthSessionProxy.LoginHistoryId;
623-
log.LoginPlatform__c = matchingAuthSessionProxy.LoginHistory.Platform;
623+
log.LoginPlatform__c = matchingAuthSessionProxy.LoginHistory?.Platform;
624624
log.LoginType__c = matchingAuthSessionProxy.LoginType;
625625
log.LogoutUrl__c = matchingAuthSessionProxy.LogoutUrl;
626626
log.ParentSessionId__c = matchingAuthSessionProxy.ParentId;
627627
log.SessionId__c = matchingAuthSessionProxy.Id;
628628
log.SessionSecurityLevel__c = matchingAuthSessionProxy.SessionSecurityLevel;
629629
log.SessionType__c = matchingAuthSessionProxy.SessionType;
630630
log.SourceIp__c = matchingAuthSessionProxy.SourceIp;
631+
632+
if (matchingAuthSessionProxy.LoginHistory?.UserId != log.LoggedBy__c) {
633+
log.ImpersonatedBy__c = matchingAuthSessionProxy.LoginHistory?.UserId;
634+
}
631635
}
632636
}
633637

Diff for: nebula-logger/core/main/logger-engine/classes/LogEntryEventBuilder.cls

+5-5
Original file line numberDiff line numberDiff line change
@@ -980,10 +980,10 @@ global with sharing class LogEntryEventBuilder {
980980
return;
981981
}
982982

983-
logEntryEvent.LoginApplication__c = CACHED_AUTH_SESSION_PROXY.LoginHistory.Application;
984-
logEntryEvent.LoginBrowser__c = CACHED_AUTH_SESSION_PROXY.LoginHistory.Browser;
983+
logEntryEvent.LoginApplication__c = CACHED_AUTH_SESSION_PROXY.LoginHistory?.Application;
984+
logEntryEvent.LoginBrowser__c = CACHED_AUTH_SESSION_PROXY.LoginHistory?.Browser;
985985
logEntryEvent.LoginHistoryId__c = CACHED_AUTH_SESSION_PROXY.LoginHistoryId;
986-
logEntryEvent.LoginPlatform__c = CACHED_AUTH_SESSION_PROXY.LoginHistory.Platform;
986+
logEntryEvent.LoginPlatform__c = CACHED_AUTH_SESSION_PROXY.LoginHistory?.Platform;
987987
logEntryEvent.LoginType__c = CACHED_AUTH_SESSION_PROXY.LoginType;
988988
logEntryEvent.LogoutUrl__c = CACHED_AUTH_SESSION_PROXY.LogoutUrl;
989989
logEntryEvent.ParentSessionId__c = CACHED_AUTH_SESSION_PROXY.ParentId;
@@ -992,8 +992,8 @@ global with sharing class LogEntryEventBuilder {
992992
logEntryEvent.SessionType__c = CACHED_AUTH_SESSION_PROXY.SessionType;
993993
logEntryEvent.SourceIp__c = CACHED_AUTH_SESSION_PROXY.SourceIp;
994994

995-
if (CACHED_AUTH_SESSION_PROXY.LoginHistory.UserId != CURRENT_USER.Id) {
996-
logEntryEvent.ImpersonatedById__c = CACHED_AUTH_SESSION_PROXY.LoginHistory.UserId;
995+
if (CACHED_AUTH_SESSION_PROXY.LoginHistory?.UserId != CURRENT_USER.Id) {
996+
logEntryEvent.ImpersonatedById__c = CACHED_AUTH_SESSION_PROXY.LoginHistory?.UserId;
997997
}
998998
}
999999

Diff for: nebula-logger/core/main/logger-engine/classes/Logger.cls

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
global with sharing class Logger {
1616
// There's no reliable way to get the version number dynamically in Apex
1717
@TestVisible
18-
private static final String CURRENT_VERSION_NUMBER = 'v4.15.4';
18+
private static final String CURRENT_VERSION_NUMBER = 'v4.15.5';
1919
private static final System.LoggingLevel FALLBACK_LOGGING_LEVEL = System.LoggingLevel.DEBUG;
2020
private static final List<LogEntryEventBuilder> LOG_ENTRIES_BUFFER = new List<LogEntryEventBuilder>();
2121
private static final String MISSING_SCENARIO_ERROR_MESSAGE = 'No logger scenario specified. A scenario is required for logging in this org.';

Diff for: nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import LoggerServiceTaskQueue from './loggerServiceTaskQueue';
1010
import getSettings from '@salesforce/apex/ComponentLogger.getSettings';
1111
import saveComponentLogEntries from '@salesforce/apex/ComponentLogger.saveComponentLogEntries';
1212

13-
const CURRENT_VERSION_NUMBER = 'v4.15.4';
13+
const CURRENT_VERSION_NUMBER = 'v4.15.5';
1414

1515
const CONSOLE_OUTPUT_CONFIG = {
1616
messagePrefix: `%c Nebula Logger ${CURRENT_VERSION_NUMBER} `,

Diff for: nebula-logger/core/tests/log-management/classes/LogEntryEventHandler_Tests.cls

+59
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,9 @@ private class LogEntryEventHandler_Tests {
648648
mockLoginHistoryProxy.Application = 'Application';
649649
mockLoginHistoryProxy.Browser = 'Browser';
650650
mockLoginHistoryProxy.Platform = 'Platform';
651+
// TODO also test for when this UserId is a different user when an admin is impersonating another user
652+
// This could be a bit tricky because creating a User in tests could fail in some orgs, and querying existing
653+
// users in tests could fail in other orgs 🙃
651654
mockLoginHistoryProxy.UserId = System.UserInfo.getUserId();
652655
LoggerSObjectProxy.AuthSession mockAuthSessionProxy = new LoggerSObjectProxy.AuthSession();
653656
mockAuthSessionProxy.Id = LoggerMockDataCreator.createId(Schema.AuthSession.SObjectType);
@@ -680,6 +683,62 @@ private class LogEntryEventHandler_Tests {
680683
System.Assert.areEqual(mockAuthSessionProxy.SourceIp, log.SourceIp__c);
681684
}
682685

686+
@IsTest
687+
static void it_should_query_and_handle_authSession_when_logingHistory_is_null() {
688+
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());
689+
LoggerTestConfigurator.setupMockSObjectHandlerConfigurations();
690+
LoggerTestConfigurator.getSObjectHandlerConfiguration(Schema.LoggerScenario__c.SObjectType).IsEnabled__c = false;
691+
LoggerTestConfigurator.getSObjectHandlerConfiguration(Schema.Log__c.SObjectType).IsEnabled__c = false;
692+
LoggerTestConfigurator.getSObjectHandlerConfiguration(Schema.LogEntry__c.SObjectType).IsEnabled__c = false;
693+
LogEntryEvent__e logEntryEvent = createLogEntryEvent();
694+
// The createLogEntryEvent() method sets all fields on LogEntryEvent__e, so clear the relevant fields to ensure
695+
// that the values are only set based on the queried Schema.AuthSession record
696+
logEntryEvent.LoginApplication__c = null;
697+
logEntryEvent.LoginBrowser__c = null;
698+
logEntryEvent.LoginHistoryId__c = null;
699+
logEntryEvent.LoginPlatform__c = null;
700+
logEntryEvent.LoginType__c = null;
701+
logEntryEvent.LogoutUrl__c = null;
702+
logEntryEvent.ParentSessionId__c = null;
703+
logEntryEvent.SessionId__c = null;
704+
logEntryEvent.SessionSecurityLevel__c = null;
705+
logEntryEvent.SessionType__c = null;
706+
logEntryEvent.SourceIp__c = null;
707+
LoggerTestConfigurator.setMock(new LoggerParameter__mdt(DeveloperName = 'QueryAuthSessionDataSynchronously', Value__c = String.valueOf(false)));
708+
System.Assert.isFalse(LoggerParameter.QUERY_AUTH_SESSION_DATA_SYNCHRONOUSLY);
709+
MockLoggerEngineDataSelector mockSelector = new MockLoggerEngineDataSelector();
710+
LoggerEngineDataSelector.setMock(mockSelector);
711+
LoggerSObjectProxy.AuthSession mockAuthSessionProxy = new LoggerSObjectProxy.AuthSession();
712+
mockAuthSessionProxy.Id = LoggerMockDataCreator.createId(Schema.AuthSession.SObjectType);
713+
mockAuthSessionProxy.LoginHistory = null;
714+
mockAuthSessionProxy.LoginHistoryId = null;
715+
mockAuthSessionProxy.LoginType = 'LoginType';
716+
mockAuthSessionProxy.LogoutUrl = 'LogoutUrl';
717+
mockAuthSessionProxy.ParentId = LoggerMockDataCreator.createId(Schema.AuthSession.SObjectType);
718+
mockAuthSessionProxy.SessionSecurityLevel = 'SessionSecurityLevel';
719+
mockAuthSessionProxy.SessionType = 'SessionType';
720+
mockAuthSessionProxy.SourceIp = 'SourceIp';
721+
mockAuthSessionProxy.UsersId = System.UserInfo.getUserId();
722+
mockSelector.setCachedAuthSessionProxy(mockAuthSessionProxy);
723+
System.Assert.areEqual(0, mockSelector.getCachedAuthSessionQueryCount());
724+
725+
LoggerMockDataStore.getEventBus().publishRecord(logEntryEvent);
726+
LoggerMockDataStore.getEventBus().deliver(new LogEntryEventHandler());
727+
728+
Log__c log = getLog();
729+
System.Assert.areEqual(mockAuthSessionProxy.Id, log.SessionId__c);
730+
System.Assert.isNull(log.LoginApplication__c);
731+
System.Assert.isNull(log.LoginBrowser__c);
732+
System.Assert.isNull(log.LoginPlatform__c);
733+
System.Assert.isNull(log.LoginHistoryId__c);
734+
System.Assert.areEqual(mockAuthSessionProxy.LoginType, log.LoginType__c);
735+
System.Assert.areEqual(mockAuthSessionProxy.LogoutUrl, log.LogoutUrl__c);
736+
System.Assert.areEqual(mockAuthSessionProxy.ParentId, log.ParentSessionId__c);
737+
System.Assert.areEqual(mockAuthSessionProxy.SessionSecurityLevel, log.SessionSecurityLevel__c);
738+
System.Assert.areEqual(mockAuthSessionProxy.SessionType, log.SessionType__c);
739+
System.Assert.areEqual(mockAuthSessionProxy.SourceIp, log.SourceIp__c);
740+
}
741+
683742
@IsTest
684743
static void it_should_query_and_set_organization_data_when_synchronous_querying_is_disabled() {
685744
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());

Diff for: nebula-logger/core/tests/logger-engine/classes/LogEntryEventBuilder_Tests.cls

+36
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,42 @@ private class LogEntryEventBuilder_Tests {
257257
System.Assert.areEqual(mockAuthSessionProxy.SourceIp, logEntryEvent.SourceIp__c);
258258
}
259259

260+
@IsTest
261+
static void it_should_query_and_handle_authSession_when_logingHistory_is_null() {
262+
LoggerParameter.setMock(new LoggerParameter__mdt(DeveloperName = 'QueryAuthSessionDataSynchronously', Value__c = String.valueOf(true)));
263+
System.Assert.isTrue(LoggerParameter.QUERY_AUTH_SESSION_DATA_SYNCHRONOUSLY);
264+
MockLoggerEngineDataSelector mockSelector = new MockLoggerEngineDataSelector();
265+
LoggerEngineDataSelector.setMock(mockSelector);
266+
LoggerSObjectProxy.AuthSession mockAuthSessionProxy = new LoggerSObjectProxy.AuthSession();
267+
mockAuthSessionProxy.Id = LoggerMockDataCreator.createId(Schema.AuthSession.SObjectType);
268+
mockAuthSessionProxy.LoginHistory = null;
269+
mockAuthSessionProxy.LoginHistoryId = null;
270+
mockAuthSessionProxy.LoginType = 'LoginType';
271+
mockAuthSessionProxy.LogoutUrl = 'LogoutUrl';
272+
mockAuthSessionProxy.ParentId = LoggerMockDataCreator.createId(Schema.AuthSession.SObjectType);
273+
mockAuthSessionProxy.SessionSecurityLevel = 'SessionSecurityLevel';
274+
mockAuthSessionProxy.SessionType = 'SessionType';
275+
mockAuthSessionProxy.SourceIp = 'SourceIp';
276+
mockAuthSessionProxy.UsersId = System.UserInfo.getUserId();
277+
mockSelector.setCachedAuthSessionProxy(mockAuthSessionProxy);
278+
System.Assert.areEqual(0, mockSelector.getCachedAuthSessionQueryCount());
279+
280+
LogEntryEvent__e logEntryEvent = new LogEntryEventBuilder(getUserSettings(), System.LoggingLevel.INFO, true).setMessage('some message').getLogEntryEvent();
281+
282+
System.Assert.areEqual(1, mockSelector.getCachedAuthSessionQueryCount());
283+
System.Assert.isNull(logEntryEvent.LoginApplication__c);
284+
System.Assert.isNull(logEntryEvent.LoginBrowser__c);
285+
System.Assert.isNull(logEntryEvent.LoginPlatform__c);
286+
System.Assert.isNull(logEntryEvent.LoginHistoryId__c);
287+
System.Assert.areEqual(mockAuthSessionProxy.LoginType, logEntryEvent.LoginType__c);
288+
System.Assert.areEqual(mockAuthSessionProxy.LogoutUrl, logEntryEvent.LogoutUrl__c);
289+
System.Assert.areEqual(mockAuthSessionProxy.ParentId, logEntryEvent.ParentSessionId__c);
290+
System.Assert.areEqual(mockAuthSessionProxy.Id, logEntryEvent.SessionId__c);
291+
System.Assert.areEqual(mockAuthSessionProxy.SessionSecurityLevel, logEntryEvent.SessionSecurityLevel__c);
292+
System.Assert.areEqual(mockAuthSessionProxy.SessionType, logEntryEvent.SessionType__c);
293+
System.Assert.areEqual(mockAuthSessionProxy.SourceIp, logEntryEvent.SourceIp__c);
294+
}
295+
260296
@IsTest
261297
static void it_should_not_run_organization_query_when_disabled_via_logger_parameter() {
262298
LoggerParameter.setMock(new LoggerParameter__mdt(DeveloperName = 'QueryOrganizationDataSynchronously', Value__c = String.valueOf(false)));

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nebula-logger",
3-
"version": "4.15.4",
3+
"version": "4.15.5",
44
"description": "The most robust logger for Salesforce. Works with Apex, Lightning Components, Flow, Process Builder & Integrations. Designed for Salesforce admins, developers & architects.",
55
"author": "Jonathan Gillespie",
66
"license": "MIT",

Diff for: sfdx-project.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
"path": "./nebula-logger/core",
1010
"definitionFile": "./config/scratch-orgs/base-scratch-def.json",
1111
"scopeProfiles": true,
12-
"versionNumber": "4.15.4.NEXT",
13-
"versionName": "FlowLogger exception message handling",
14-
"versionDescription": "FlowLogger now properly truncates the LogEntryEvent__e.ExceptionMessage__c field",
12+
"versionNumber": "4.15.5.NEXT",
13+
"versionName": "Bugfix for AuthSession with null LoginHistory exception message handling",
14+
"versionDescription": "Added safe navigation operator on LoginHistory from authSession records to avoid a NullPointerException",
1515
"postInstallUrl": "https://github.com/jongpie/NebulaLogger/wiki",
1616
"releaseNotesUrl": "https://github.com/jongpie/NebulaLogger/releases",
1717
"unpackagedMetadata": {
@@ -209,6 +209,7 @@
209209
"Nebula Logger - [email protected]": "04t5Y0000015oifQAA",
210210
"Nebula Logger - [email protected]": "04t5Y0000015ok2QAA",
211211
"Nebula Logger - [email protected]": "04t5Y0000015oklQAA",
212+
"Nebula Logger - Core@4.15.5-bugfix-for-authsession-with-null-loginhistory-exception-message-handling": "04t5Y0000015p5jQAA",
212213
"Nebula Logger - Core Plugin - Async Failure Additions": "0Ho5Y000000blO4SAI",
213214
"Nebula Logger - Core Plugin - Async Failure [email protected]": "04t5Y0000015lhiQAA",
214215
"Nebula Logger - Core Plugin - Async Failure [email protected]": "04t5Y0000015lhsQAA",

0 commit comments

Comments
 (0)