mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 22:26:16 -08:00
feat(stalker): identity hardening (#1370)
MAC addresses are canonicalized to the uppercase colon form a real STB
sends and validated at the input boundary, with a hint when they fall
outside Infomir's OUI — which the stock server's default filter refuses
with a bare {status: 1} no user could diagnose. Normalization applies
only to a value the user actually edits: rewriting stored bytes would
move the session fingerprint for every existing playlist with no user
action, and the MAC is the account key.
Device IDs can optionally be derived from the MAC the way StbEmu and
stalker-to-m3u do — SHA256(MAC) and SHA256(MAC + "stalker"), which a
real box never reports as equal. The portal pins the first non-empty
device_id/device_id2 it sees to the MAC permanently, refuses a different
one, and treats a later empty value as an unrecoverable lockout, so
derived values are written into the visible fields and persisted as
literal strings, never recomputed at request time. The option is offered
at import only; the edit dialog warns instead once an ID has actually
reached the portal.
get_profile now reports one coherent MAG250 (ver, stb_type — previously
empty —, hw_version, image_version, client_type), and a device conflict
gets its own StalkerPortalError kind so the UI can explain it instead of
relaying the portal's "Your STB is damaged".
Closes the identity-fields cluster: #927, #860.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7111942509
commit
9ff1c6ae01
44 files changed
+2326
-72
No files matched your search
+36
-1
@@ -5,7 +5,10 @@ import { Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { of } from 'rxjs';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
import {
|
||||
StalkerPortalError,
|
||||
StalkerStore,
|
||||
} from '@iptvnator/portal/stalker/data-access';
|
||||
import { WORKSPACE_CATEGORY_SORT_STORAGE_KEY } from '@iptvnator/portal/shared/util';
|
||||
import { WorkspaceShellContextDrawerService } from '@iptvnator/workspace/shell/util';
|
||||
import {
|
||||
@@ -558,4 +561,36 @@ describe('WorkspaceContextPanelComponent', () => {
|
||||
expect(stalkerStore.clearSelectedItem).toHaveBeenCalled();
|
||||
expect(router.navigate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('Stalker category error description', () => {
|
||||
afterEach(() => {
|
||||
stalkerStore.isCategoryResourceFailed.set(false as never);
|
||||
});
|
||||
|
||||
it('leads with the remedy for a device conflict', () => {
|
||||
// The portal blames the hardware ("Your STB is damaged"), which
|
||||
// is the opposite of actionable — the explanation has to come
|
||||
// first, with the portal's own words kept after it.
|
||||
stalkerStore.isCategoryResourceFailed.set(
|
||||
new StalkerPortalError(
|
||||
'device-conflict',
|
||||
'device conflict - device_id mismatch — Your STB is damaged.'
|
||||
) as never
|
||||
);
|
||||
|
||||
expect(fixture.componentInstance.stalkerCategoryErrorDescription()).toBe(
|
||||
'PORTALS.ERROR_VIEW.STALKER_DEVICE_CONFLICT device conflict - device_id mismatch — Your STB is damaged.'
|
||||
);
|
||||
});
|
||||
|
||||
it('still relays any other refusal verbatim', () => {
|
||||
stalkerStore.isCategoryResourceFailed.set(
|
||||
new StalkerPortalError('blocked', 'Account disabled') as never
|
||||
);
|
||||
|
||||
expect(fixture.componentInstance.stalkerCategoryErrorDescription()).toBe(
|
||||
'Account disabled'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
+11
@@ -165,6 +165,17 @@ export class WorkspaceContextPanelComponent {
|
||||
const portalError = asStalkerPortalError(
|
||||
this.isStalkerCategoryFailed()
|
||||
);
|
||||
// Device conflicts are the exception to "the portal explains itself":
|
||||
// its own wording blames the hardware, so the actionable sentence
|
||||
// leads and the portal's text follows it.
|
||||
if (portalError?.kind === 'device-conflict') {
|
||||
const hint = this.translate.instant(
|
||||
'PORTALS.ERROR_VIEW.STALKER_DEVICE_CONFLICT'
|
||||
);
|
||||
return portalError.portalText
|
||||
? `${hint} ${portalError.portalText}`
|
||||
: hint;
|
||||
}
|
||||
if (portalError?.portalText) {
|
||||
return portalError.portalText;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user