-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: redirect to login even if logout requests fail
Refs: SHELL-248 (#513)
- Loading branch information
Showing
4 changed files
with
251 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2024 Zextras <https://www.zextras.com> | ||
* | ||
* SPDX-License-Identifier: AGPL-3.0-only | ||
*/ | ||
import { userAgent } from './user-agent'; | ||
import { JSNS } from '../constants'; | ||
import { useAccountStore } from '../store/account'; | ||
import { useNetworkStore } from '../store/network'; | ||
import type { Account } from '../types/account'; | ||
import type { RawSoapResponse } from '../types/network'; | ||
|
||
const getAccount = ( | ||
acc?: Account, | ||
otherAccount?: string | ||
): { by: string; _content: string } | undefined => { | ||
if (otherAccount) { | ||
return { | ||
by: 'name', | ||
_content: otherAccount | ||
}; | ||
} | ||
if (acc) { | ||
if (acc.name) { | ||
return { | ||
by: 'name', | ||
_content: acc.name | ||
}; | ||
} | ||
if (acc.id) { | ||
return { | ||
by: 'id', | ||
_content: acc.id | ||
}; | ||
} | ||
} | ||
return undefined; | ||
}; | ||
|
||
export const soapFetch = async <Request, Response extends Record<string, unknown>>( | ||
api: string, | ||
body: Request, | ||
otherAccount?: string, | ||
signal?: AbortSignal | ||
): Promise<RawSoapResponse<Response>> => { | ||
const { zimbraVersion, account } = useAccountStore.getState(); | ||
const { notify, session } = useNetworkStore.getState(); | ||
const res = await fetch(`/service/soap/${api}Request`, { | ||
signal, | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json' | ||
}, | ||
body: JSON.stringify({ | ||
Body: { | ||
[`${api}Request`]: body | ||
}, | ||
Header: { | ||
context: { | ||
_jsns: JSNS.all, | ||
notify: notify?.[0]?.seq | ||
? { | ||
seq: notify?.[0]?.seq | ||
} | ||
: undefined, | ||
session: session ?? {}, | ||
account: getAccount(account, otherAccount), | ||
userAgent: { | ||
name: userAgent, | ||
version: zimbraVersion | ||
} | ||
} | ||
} | ||
}) | ||
}); | ||
return res.json(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2024 Zextras <https://www.zextras.com> | ||
* | ||
* SPDX-License-Identifier: AGPL-3.0-only | ||
*/ | ||
import { http, HttpResponse } from 'msw'; | ||
|
||
import { logout } from './logout'; | ||
import * as utils from './utils'; | ||
import server from '../mocks/server'; | ||
import { useLoginConfigStore } from '../store/login/store'; | ||
import { controlConsoleError } from '../tests/utils'; | ||
import type { ErrorSoapResponse } from '../types/network'; | ||
|
||
describe('Logout', () => { | ||
it('should redirect to login page if EndSession request fails', async () => { | ||
const goToLoginFn = jest.spyOn(utils, 'goToLogin').mockImplementation(); | ||
server.use( | ||
http.post('/service/soap/EndSessionRequest', () => HttpResponse.json({}, { status: 500 })) | ||
); | ||
await logout(); | ||
await jest.advanceTimersToNextTimerAsync(); | ||
expect(goToLoginFn).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should redirect to login page if /logout request fails', async () => { | ||
const goToLoginFn = jest.spyOn(utils, 'goToLogin').mockImplementation(); | ||
server.use(http.get('/logout', () => HttpResponse.json({}, { status: 500 }))); | ||
await logout(); | ||
await jest.advanceTimersToNextTimerAsync(); | ||
expect(goToLoginFn).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should redirect to login page if EndSession throws error', async () => { | ||
controlConsoleError('Failed to fetch'); | ||
const goToLoginFn = jest.spyOn(utils, 'goToLogin').mockImplementation(); | ||
server.use(http.post('/service/soap/EndSessionRequest', () => HttpResponse.error())); | ||
await logout(); | ||
await jest.advanceTimersToNextTimerAsync(); | ||
expect(goToLoginFn).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should redirect to login page if /logout throws error', async () => { | ||
controlConsoleError('Failed to fetch'); | ||
const goToLoginFn = jest.spyOn(utils, 'goToLogin').mockImplementation(); | ||
server.use(http.get('/logout', () => HttpResponse.error())); | ||
await logout(); | ||
await jest.advanceTimersToNextTimerAsync(); | ||
expect(goToLoginFn).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should redirect to login page if EndSession request succeeded with Fault', async () => { | ||
const goToLoginFn = jest.spyOn(utils, 'goToLogin').mockImplementation(); | ||
server.use( | ||
http.post('/service/soap/EndSessionRequest', () => | ||
HttpResponse.json<ErrorSoapResponse>( | ||
{ | ||
Header: { context: {} }, | ||
Body: { | ||
Fault: { | ||
Code: { Value: '' }, | ||
Detail: { | ||
Error: { | ||
Code: '', | ||
Trace: '' | ||
} | ||
}, | ||
Reason: { | ||
Text: '' | ||
} | ||
} | ||
} | ||
}, | ||
{ status: 200 } | ||
) | ||
) | ||
); | ||
await logout(); | ||
await jest.advanceTimersToNextTimerAsync(); | ||
expect(goToLoginFn).toHaveBeenCalled(); | ||
}); | ||
|
||
describe('with custom logout url', () => { | ||
it('should redirect to login page if EndSession request fails', async () => { | ||
useLoginConfigStore.setState({ carbonioWebUiLogoutURL: 'custom logout' }); | ||
const goToFn = jest.spyOn(utils, 'goTo').mockImplementation(); | ||
server.use( | ||
http.post('/service/soap/EndSessionRequest', () => HttpResponse.json({}, { status: 500 })) | ||
); | ||
await logout(); | ||
await jest.advanceTimersToNextTimerAsync(); | ||
expect(goToFn).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should redirect to login page if /logout request fails', async () => { | ||
useLoginConfigStore.setState({ carbonioWebUiLogoutURL: 'custom logout' }); | ||
const goToFn = jest.spyOn(utils, 'goTo').mockImplementation(); | ||
server.use(http.get('/logout', () => HttpResponse.json({}, { status: 500 }))); | ||
await logout(); | ||
await jest.advanceTimersToNextTimerAsync(); | ||
expect(goToFn).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should redirect to login page if EndSession throws error', async () => { | ||
useLoginConfigStore.setState({ carbonioWebUiLogoutURL: 'custom logout' }); | ||
controlConsoleError('Failed to fetch'); | ||
const goToFn = jest.spyOn(utils, 'goTo').mockImplementation(); | ||
server.use(http.post('/service/soap/EndSessionRequest', () => HttpResponse.error())); | ||
await logout(); | ||
await jest.advanceTimersToNextTimerAsync(); | ||
expect(goToFn).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should redirect to login page if /logout throws error', async () => { | ||
useLoginConfigStore.setState({ carbonioWebUiLogoutURL: 'custom logout' }); | ||
controlConsoleError('Failed to fetch'); | ||
const goToFn = jest.spyOn(utils, 'goTo').mockImplementation(); | ||
server.use(http.get('/logout', () => HttpResponse.error())); | ||
await logout(); | ||
await jest.advanceTimersToNextTimerAsync(); | ||
expect(goToFn).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should redirect to login page if EndSession request succeeded with Fault', async () => { | ||
useLoginConfigStore.setState({ carbonioWebUiLogoutURL: 'custom logout' }); | ||
const goToFn = jest.spyOn(utils, 'goTo').mockImplementation(); | ||
server.use( | ||
http.post('/service/soap/EndSessionRequest', () => | ||
HttpResponse.json<ErrorSoapResponse>( | ||
{ | ||
Header: { context: {} }, | ||
Body: { | ||
Fault: { | ||
Code: { Value: '' }, | ||
Detail: { | ||
Error: { | ||
Code: '', | ||
Trace: '' | ||
} | ||
}, | ||
Reason: { | ||
Text: '' | ||
} | ||
} | ||
} | ||
}, | ||
{ status: 200 } | ||
) | ||
) | ||
); | ||
await logout(); | ||
await jest.advanceTimersToNextTimerAsync(); | ||
expect(goToFn).toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters