From e7f9947d488ef531438efbeb86aee61332c5b99a Mon Sep 17 00:00:00 2001 From: Yoganandan Pandiyan Date: Fri, 2 Oct 2026 16:58:12 +0200 Subject: [PATCH 1/2] fix: prevent timezone shifts in visit registration dates by adjusting date handling --- .../syncVisitToOneIdentityHandler.spec.ts | 48 ++++++++++++------- .../syncVisitToOneIdentityHandler.ts | 38 ++++++++++----- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.spec.ts b/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.spec.ts index 8d827690..9720e73c 100644 --- a/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.spec.ts +++ b/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.spec.ts @@ -114,9 +114,14 @@ describe('syncVisitToOneIdentityHandler', () => { describe('VISIT_CREATED', () => { it('should create site access and system access in One Identity for science users and connect to proposal', async () => { // Mock the current time to a fixed value for testing - const mockNowDate = new Date('2022-12-15T00:00:00.000Z'); + const mockNowDate = new Date('2022-12-15T12:30:00.000Z'); const originalDateNow = Date.now; Date.now = jest.fn(() => mockNowDate.getTime()); + const visitMessageWithTimes = { + ...visitMessage, + startAt: '2023-01-01T10:30:00.000Z', + endAt: '2023-01-10T14:45:00.000Z', + }; // Mock person that is a science user const mockPerson = { @@ -141,14 +146,17 @@ describe('syncVisitToOneIdentityHandler', () => { .mockResolvedValueOnce([mockSiteAccess]) .mockResolvedValueOnce([mockSystemAccess]); - await syncVisitToOneIdentityHandler(visitMessage, Event.VISIT_CREATED); + await syncVisitToOneIdentityHandler( + visitMessageWithTimes, + Event.VISIT_CREATED + ); expect(mockOneIdentity.login).toHaveBeenCalled(); expect(mockOneIdentity.getPerson).toHaveBeenCalledWith( 'visitor-oidc-sub' ); expect(mockOneIdentity.getProposal).toHaveBeenCalledWith( - visitMessage.proposal + visitMessageWithTimes.proposal ); expect(mockOneIdentity.getProposalPersonConnections).toHaveBeenCalledWith( mockUidESet @@ -159,29 +167,30 @@ describe('syncVisitToOneIdentityHandler', () => { expect(mockOneIdentity.upsertPersonWantsOrg).toHaveBeenNthCalledWith( 1, PersonWantsOrgRole.SITE_ACCESS, - visitMessage.visitorId, - visitMessage.startAt, - visitMessage.endAt, - visitMessage.id + visitMessageWithTimes.visitorId, + '2023-01-01T00:00:00.000Z', + '2023-01-10T23:59:59.999Z', + visitMessageWithTimes.id ); // Calculate expected system access dates // validFrom should be the current mock date - const expectedValidFrom = mockNowDate.toISOString(); - const expectedEndDate = new Date(visitMessage.endAt); - expectedEndDate.setDate( - expectedEndDate.getDate() + + const expectedValidFrom = '2022-12-15T00:00:00.000Z'; + const expectedEndDate = new Date(visitMessageWithTimes.endAt); + expectedEndDate.setUTCDate( + expectedEndDate.getUTCDate() + parseInt(ONE_IDENTITY_SYSTEM_ACCESS_LASTS_FOR_DAYS) ); + expectedEndDate.setUTCHours(23, 59, 59, 999); // Verify system access creation expect(mockOneIdentity.upsertPersonWantsOrg).toHaveBeenNthCalledWith( 2, PersonWantsOrgRole.SYSTEM_ACCESS, - visitMessage.visitorId, + visitMessageWithTimes.visitorId, expectedValidFrom, expectedEndDate.toISOString(), - visitMessage.id + visitMessageWithTimes.id ); expect(logger.logInfo).toHaveBeenCalledWith( @@ -754,7 +763,7 @@ describe('syncVisitToOneIdentityHandler', () => { PersonWantsOrgRole.SITE_ACCESS, 'visitor-oidc-sub', '2023-02-01T00:00:00.000Z', - '2023-02-15T00:00:00.000Z', + '2023-02-15T23:59:59.999Z', '1', 'site-access-uid' ); @@ -769,15 +778,18 @@ describe('syncVisitToOneIdentityHandler', () => { const expectedSystemAccessValidUntil = new Date( '2023-02-15T00:00:00.000Z' ); - expectedSystemAccessValidUntil.setDate( - expectedSystemAccessValidUntil.getDate() + + expectedSystemAccessValidUntil.setUTCDate( + expectedSystemAccessValidUntil.getUTCDate() + parseInt(ONE_IDENTITY_SYSTEM_ACCESS_LASTS_FOR_DAYS) ); + expectedSystemAccessValidUntil.setUTCHours(23, 59, 59, 999); + const expectedSystemAccessValidFrom = new Date(mockNowDate); + expectedSystemAccessValidFrom.setUTCHours(0, 0, 0, 0); expect(mockOneIdentity.upsertPersonWantsOrg).toHaveBeenNthCalledWith( 2, PersonWantsOrgRole.SYSTEM_ACCESS, 'visitor-oidc-sub', - mockNowDate.toISOString(), + expectedSystemAccessValidFrom.toISOString(), expectedSystemAccessValidUntil.toISOString(), '1', 'system-access-uid' @@ -807,7 +819,7 @@ describe('syncVisitToOneIdentityHandler', () => { UID_PersonOrdered: 'visitor-uid', DisplayOrg: PersonWantsOrgRole.SITE_ACCESS, ValidFrom: visitMessage.startAt, - ValidUntil: visitMessage.endAt, + ValidUntil: '2023-01-10T23:59:59.999Z', CustomProperty04: '1', OrderState: OrderState.GRANTED, } as PersonWantsOrg; diff --git a/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.ts b/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.ts index add0ad9a..620b609b 100644 --- a/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.ts +++ b/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.ts @@ -114,8 +114,8 @@ async function createAccessInOneIdentity( const [pwoSite] = await oneIdentity.upsertPersonWantsOrg( PersonWantsOrgRole.SITE_ACCESS, centralAccount, - toIsoString(startAt), - toIsoString(endAt), + toStartOfDayIsoString(startAt), + toEndOfDayIsoString(endAt), visitId // CustomProperty04 - We store the visit ID for the site access to be able to find it later ); @@ -125,16 +125,16 @@ async function createAccessInOneIdentity( // validFrom in One Identity should be in the future so that the access is not immediately available const validFrom = Date.now(); - const validUntil = new Date(endAt).setDate( - new Date(endAt).getDate() + ONE_IDENTITY_SYSTEM_ACCESS_LASTS_FOR_DAYS + const validUntil = new Date(endAt).setUTCDate( + new Date(endAt).getUTCDate() + ONE_IDENTITY_SYSTEM_ACCESS_LASTS_FOR_DAYS ); // Create system access const [pwoSystem] = await oneIdentity.upsertPersonWantsOrg( PersonWantsOrgRole.SYSTEM_ACCESS, centralAccount, - toIsoString(validFrom), - toIsoString(validUntil), + toStartOfDayIsoString(validFrom), + toEndOfDayIsoString(validUntil), visitId // CustomProperty04 - We store the visit ID for the system access to be able to find it later ); @@ -180,8 +180,8 @@ async function updateAccessInOneIdentity( return; } - const validFrom = toIsoString(startAt); - const validUntil = toIsoString(endAt); + const validFrom = toStartOfDayIsoString(startAt); + const validUntil = toEndOfDayIsoString(endAt); // Find system access for the site access (CustomProperty04 is the visit ID) const systemAccess = personWantsOrgs.find( @@ -225,10 +225,10 @@ async function updateAccessInOneIdentity( UID_PersonWantsOrg: siteAccess.UID_PersonWantsOrg, }); - const systemAccessValidFrom = toIsoString(Date.now()); - const systemAccessValidUntil = toIsoString( - new Date(endAt).setDate( - new Date(endAt).getDate() + ONE_IDENTITY_SYSTEM_ACCESS_LASTS_FOR_DAYS + const systemAccessValidFrom = toStartOfDayIsoString(Date.now()); + const systemAccessValidUntil = toEndOfDayIsoString( + new Date(endAt).setUTCDate( + new Date(endAt).getUTCDate() + ONE_IDENTITY_SYSTEM_ACCESS_LASTS_FOR_DAYS ) ); @@ -361,6 +361,20 @@ function toIsoString(date: string | number) { return parsedDate.toISOString(); } +function toStartOfDayIsoString(date: string | number) { + const parsedDate = new Date(toIsoString(date)); + parsedDate.setUTCHours(0, 0, 0, 0); + + return parsedDate.toISOString(); +} + +function toEndOfDayIsoString(date: string | number) { + const parsedDate = new Date(toIsoString(date)); + parsedDate.setUTCHours(23, 59, 59, 999); + + return parsedDate.toISOString(); +} + function isSameDateTime(left: string, right: string) { const leftTime = new Date(left).getTime(); const rightTime = new Date(right).getTime(); From 5a8e351875228ff8bf4f50ec8899d8944befaa83 Mon Sep 17 00:00:00 2001 From: Yoganandan Pandiyan Date: Mon, 5 Oct 2026 13:18:05 +0200 Subject: [PATCH 2/2] fix: adjust date handling to prevent timezone shifts in visit registration and access validity --- .../syncVisitToOneIdentityHandler.spec.ts | 16 +++++++++------- .../syncVisitToOneIdentityHandler.ts | 13 +++++++------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.spec.ts b/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.spec.ts index 9720e73c..d148af7b 100644 --- a/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.spec.ts +++ b/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.spec.ts @@ -169,7 +169,7 @@ describe('syncVisitToOneIdentityHandler', () => { PersonWantsOrgRole.SITE_ACCESS, visitMessageWithTimes.visitorId, '2023-01-01T00:00:00.000Z', - '2023-01-10T23:59:59.999Z', + '2023-01-11T00:00:00.000Z', visitMessageWithTimes.id ); @@ -179,9 +179,10 @@ describe('syncVisitToOneIdentityHandler', () => { const expectedEndDate = new Date(visitMessageWithTimes.endAt); expectedEndDate.setUTCDate( expectedEndDate.getUTCDate() + - parseInt(ONE_IDENTITY_SYSTEM_ACCESS_LASTS_FOR_DAYS) + parseInt(ONE_IDENTITY_SYSTEM_ACCESS_LASTS_FOR_DAYS) + + 1 ); - expectedEndDate.setUTCHours(23, 59, 59, 999); + expectedEndDate.setUTCHours(0, 0, 0, 0); // Verify system access creation expect(mockOneIdentity.upsertPersonWantsOrg).toHaveBeenNthCalledWith( @@ -763,7 +764,7 @@ describe('syncVisitToOneIdentityHandler', () => { PersonWantsOrgRole.SITE_ACCESS, 'visitor-oidc-sub', '2023-02-01T00:00:00.000Z', - '2023-02-15T23:59:59.999Z', + '2023-02-16T00:00:00.000Z', '1', 'site-access-uid' ); @@ -780,9 +781,10 @@ describe('syncVisitToOneIdentityHandler', () => { ); expectedSystemAccessValidUntil.setUTCDate( expectedSystemAccessValidUntil.getUTCDate() + - parseInt(ONE_IDENTITY_SYSTEM_ACCESS_LASTS_FOR_DAYS) + parseInt(ONE_IDENTITY_SYSTEM_ACCESS_LASTS_FOR_DAYS) + + 1 ); - expectedSystemAccessValidUntil.setUTCHours(23, 59, 59, 999); + expectedSystemAccessValidUntil.setUTCHours(0, 0, 0, 0); const expectedSystemAccessValidFrom = new Date(mockNowDate); expectedSystemAccessValidFrom.setUTCHours(0, 0, 0, 0); expect(mockOneIdentity.upsertPersonWantsOrg).toHaveBeenNthCalledWith( @@ -819,7 +821,7 @@ describe('syncVisitToOneIdentityHandler', () => { UID_PersonOrdered: 'visitor-uid', DisplayOrg: PersonWantsOrgRole.SITE_ACCESS, ValidFrom: visitMessage.startAt, - ValidUntil: '2023-01-10T23:59:59.999Z', + ValidUntil: '2023-01-11T00:00:00.000Z', CustomProperty04: '1', OrderState: OrderState.GRANTED, } as PersonWantsOrg; diff --git a/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.ts b/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.ts index 620b609b..3806722f 100644 --- a/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.ts +++ b/src/queue/consumers/oneidentity/consumerCallbacks/syncVisitToOneIdentityHandler.ts @@ -115,7 +115,7 @@ async function createAccessInOneIdentity( PersonWantsOrgRole.SITE_ACCESS, centralAccount, toStartOfDayIsoString(startAt), - toEndOfDayIsoString(endAt), + toStartOfNextDayIsoString(endAt), visitId // CustomProperty04 - We store the visit ID for the site access to be able to find it later ); @@ -134,7 +134,7 @@ async function createAccessInOneIdentity( PersonWantsOrgRole.SYSTEM_ACCESS, centralAccount, toStartOfDayIsoString(validFrom), - toEndOfDayIsoString(validUntil), + toStartOfNextDayIsoString(validUntil), visitId // CustomProperty04 - We store the visit ID for the system access to be able to find it later ); @@ -181,7 +181,7 @@ async function updateAccessInOneIdentity( } const validFrom = toStartOfDayIsoString(startAt); - const validUntil = toEndOfDayIsoString(endAt); + const validUntil = toStartOfNextDayIsoString(endAt); // Find system access for the site access (CustomProperty04 is the visit ID) const systemAccess = personWantsOrgs.find( @@ -226,7 +226,7 @@ async function updateAccessInOneIdentity( }); const systemAccessValidFrom = toStartOfDayIsoString(Date.now()); - const systemAccessValidUntil = toEndOfDayIsoString( + const systemAccessValidUntil = toStartOfNextDayIsoString( new Date(endAt).setUTCDate( new Date(endAt).getUTCDate() + ONE_IDENTITY_SYSTEM_ACCESS_LASTS_FOR_DAYS ) @@ -368,9 +368,10 @@ function toStartOfDayIsoString(date: string | number) { return parsedDate.toISOString(); } -function toEndOfDayIsoString(date: string | number) { +function toStartOfNextDayIsoString(date: string | number) { const parsedDate = new Date(toIsoString(date)); - parsedDate.setUTCHours(23, 59, 59, 999); + parsedDate.setUTCHours(0, 0, 0, 0); + parsedDate.setUTCDate(parsedDate.getUTCDate() + 1); return parsedDate.toISOString(); }