mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-15 21:56:18 -08:00
fix(stalker): retain abandoned auth fences
This commit is contained in:
1 parent
344182014f
commit
bfbd252ea2
7 files changed
+176
-30
No files matched your search
@@ -209,6 +209,37 @@ describe('AppStalkerPlaylistConnectionEditorService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps an abandoned authentication fenced until it settles', async () => {
|
||||
let settleAuthentication: () => void = () => undefined;
|
||||
const abandonedAuthenticationSettled = new Promise<void>((resolve) => {
|
||||
settleAuthentication = resolve;
|
||||
});
|
||||
discovery.discover.mockResolvedValue({
|
||||
status: 'auth-rejected',
|
||||
portalUrl: 'https://portal.example.com/server/load.php',
|
||||
abandonedInFlight: true,
|
||||
abandonedAuthenticationSettled,
|
||||
});
|
||||
|
||||
await expect(service.resolveConnection(draft)).resolves.toMatchObject({
|
||||
status: STALKER_PLAYLIST_CONNECTION_EDITOR_STATUS.AUTH_REJECTED,
|
||||
});
|
||||
|
||||
expect(stalkerSession.cancelEditDiscovery).not.toHaveBeenCalled();
|
||||
expect(portalRepair.releasePlaylistEdit).not.toHaveBeenCalled();
|
||||
|
||||
settleAuthentication();
|
||||
await abandonedAuthenticationSettled;
|
||||
await Promise.resolve();
|
||||
|
||||
expect(stalkerSession.cancelEditDiscovery).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ playlistId: draft._id })
|
||||
);
|
||||
expect(portalRepair.releasePlaylistEdit).toHaveBeenCalledWith(
|
||||
draft._id
|
||||
);
|
||||
});
|
||||
|
||||
it('returns a dedicated error when no endpoint can be reached', async () => {
|
||||
discovery.discover.mockResolvedValue({ status: 'unreachable' });
|
||||
|
||||
|
||||
@@ -136,7 +136,15 @@ export class AppStalkerPlaylistConnectionEditorService implements StalkerPlaylis
|
||||
}
|
||||
|
||||
if (outcome.status === 'auth-rejected') {
|
||||
this.releaseEditFence(playlist._id, fence);
|
||||
if (outcome.abandonedInFlight) {
|
||||
this.releaseEditFenceAfterAuthenticationSettles(
|
||||
playlist._id,
|
||||
fence,
|
||||
outcome.abandonedAuthenticationSettled
|
||||
);
|
||||
} else {
|
||||
this.releaseEditFence(playlist._id, fence);
|
||||
}
|
||||
return {
|
||||
status: STALKER_PLAYLIST_CONNECTION_EDITOR_STATUS.AUTH_REJECTED,
|
||||
message: this.buildAuthErrorMessage(outcome.error),
|
||||
@@ -230,6 +238,28 @@ export class AppStalkerPlaylistConnectionEditorService implements StalkerPlaylis
|
||||
this.portalRepair.releasePlaylistEdit(playlistId);
|
||||
}
|
||||
|
||||
private releaseEditFenceAfterAuthenticationSettles(
|
||||
playlistId: string,
|
||||
fence: StalkerEditFence,
|
||||
authenticationSettled: Promise<void> | undefined
|
||||
): void {
|
||||
// Fail closed if a producer ever reports an abandoned request without
|
||||
// its lifetime. Releasing here would let a fresh session authenticate
|
||||
// while the old get_profile can still land and invalidate its token.
|
||||
if (!authenticationSettled) {
|
||||
return;
|
||||
}
|
||||
|
||||
void authenticationSettled.then(() => {
|
||||
// The dialog may have been closed or a later owner may already
|
||||
// have retired this exact reservation. Never release a different
|
||||
// edit's counted repair fence from this late continuation.
|
||||
if (this.editFences.get(playlistId) === fence) {
|
||||
this.releaseEditFence(playlistId, fence);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private buildAuthErrorMessage(error: unknown): string {
|
||||
const portalError = asStalkerPortalError(error);
|
||||
const headline = this.translate.instant(
|
||||
|
||||
Reference in new issue
Block a user