mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-14 05:06:16 -08:00
fix(stalker): check the session fingerprint on the playback token path too
Two more places where the endpoint/identity/credential binding was defined but not enforced: - Collection playback read the RAW token cache, which skips the fingerprint check `ensureToken()` performs. After editing the MAC, endpoint or login, opening a direct-URL radio favorite before any other request put the previous account's token into the stream headers. It now always goes through `ensureToken()`, which returns the cached token when it is still valid, so a warm session costs nothing. - The repair's atomic row guard compared URL, mode and device identity but not credentials, so a login saved during a 45-second discovery let the outcome negotiated for the OLD account commit and adopt its token. The guard now matches `repairSourceFingerprint()`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9799558ad7
commit
2e5cc3d901
4 files changed
+46
-6
No files matched your search
@@ -794,7 +794,10 @@ describe('StreamResolverService', () => {
|
||||
isFullStalkerPortal: true,
|
||||
} satisfies Partial<Playlist>)
|
||||
);
|
||||
stalkerSession.getCachedToken.mockReturnValue('TOKEN77');
|
||||
// Playback goes through ensureToken, never the raw cache accessor:
|
||||
// that one skips the endpoint/identity/credential check, so an
|
||||
// edited playlist would put the old account's token in the headers.
|
||||
stalkerSession.ensureToken.mockResolvedValue({ token: 'TOKEN77' });
|
||||
stalkerSession.makeAuthenticatedRequest.mockResolvedValue({
|
||||
js: { cmd: 'ffmpeg https://stalker.example.com:8080/live/88.ts' },
|
||||
});
|
||||
@@ -810,7 +813,7 @@ describe('StreamResolverService', () => {
|
||||
stalkerCmd: 'ffrt3 http://stalker.example.com/media/88.mpg',
|
||||
} satisfies UnifiedCollectionItem);
|
||||
|
||||
expect(stalkerSession.getCachedToken).toHaveBeenCalledWith('stalker-1');
|
||||
expect(stalkerSession.ensureToken).toHaveBeenCalled();
|
||||
expect(playback.headers?.['Cookie']).toContain(
|
||||
'mac=00:11:22:33:44:55'
|
||||
);
|
||||
|
||||
@@ -568,15 +568,19 @@ export class StreamResolverService {
|
||||
playlistId: string,
|
||||
playlist: Playlist | undefined
|
||||
): Promise<string | null> {
|
||||
const cached = this.stalkerSession.getCachedToken(playlistId);
|
||||
// The shared mode contract, not the raw flag: a legacy row with an
|
||||
// absent flag but a canonical URL IS a full portal, and reading the
|
||||
// property directly would skip authentication for it — a restored
|
||||
// older backup opens a direct-URL radio favorite with no Bearer.
|
||||
if (cached || !playlist || !isFullStalkerPortalPlaylist(playlist)) {
|
||||
return cached;
|
||||
if (!playlist || !isFullStalkerPortalPlaylist(playlist)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Always through ensureToken, never the raw cache accessor: that one
|
||||
// skips the endpoint/identity/credential check, so after an edit this
|
||||
// playback path would put the previous account's token into the
|
||||
// stream headers. ensureToken returns the cached token when it is
|
||||
// still valid for this playlist, so a warm session costs nothing.
|
||||
try {
|
||||
const { token } = await this.stalkerSession.ensureToken(playlist);
|
||||
return token;
|
||||
|
||||
Reference in new issue
Block a user