Plex: edit a title's details (fields locked on save) and choose its poster or background, owner only, through a narrow write rule in the proxy
build-container / build (push) Successful in 19m31s
build-container / build (push) Successful in 19m31s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b171f35be6
commit
5c5a7b5155
5 files changed
+113
-5
No files matched your search
+14
-1
@@ -36,7 +36,7 @@ const jsonOut = (rs, code, o) => { rs.writeHead(code, { 'content-type': 'applica
|
||||
tv.resourceCalls++;
|
||||
const who = rq.headers['x-plex-token'], other = { KIDTOKEN: 'KIDSRV', GUESTTOKEN: 'GUESTSRV' }[who];
|
||||
if (who !== tv.accountToken && !other) return jsonOut(rs, 401, { error: 'bad token' });
|
||||
return jsonOut(rs, 200, [{ name: 'E-va', clientIdentifier: 'sid1', provides: 'server', owned: true, accessToken: other || tv.serverToken,
|
||||
return jsonOut(rs, 200, [{ name: 'E-va', clientIdentifier: 'sid1', provides: 'server', owned: who === tv.accountToken, accessToken: other || tv.serverToken,
|
||||
connections: [{ uri: `http://127.0.0.1:${tv.plexPort}`, local: false, relay: false, address: '127.0.0.1', port: tv.plexPort }] }]);
|
||||
}
|
||||
rs.writeHead(404); rs.end();
|
||||
@@ -95,6 +95,18 @@ const jsonOut = (rs, code, o) => { rs.writeHead(code, { 'content-type': 'applica
|
||||
const bad = [['DELETE', '/plex/library/metadata/5'], ['PUT', '/plex/library/sections/1/refresh'], ['POST', '/plex/playlists/7/../library/metadata/5'], ['DELETE', '/plex/library/sections/1']];
|
||||
const refused = []; for (const [m, p] of bad) { const x = await call(p, undefined, m); refused.push(x.status); }
|
||||
ok('but nothing else on the Plex server can be changed through the hub', refused.every(x => x >= 400) && !plexReqs.length, [refused, plexReqs]);
|
||||
// ---- editing a title's details (what Plex's Edit dialog has) and choosing its poster
|
||||
r = await call('/api/plex/status'); ok('the owner is told they can edit details', r.json.canEdit === true, r.json);
|
||||
const E = '/plex/library/sections/1/all?type=1&id=5&title.value=' + encodeURIComponent('New & Title') + '&title.locked=1&summary.value=Hi&summary.locked=1&genre%5B0%5D.tag.tag=Drama&genre%5B1%5D.tag.tag=Comedy&genre.locked=1&originallyAvailableAt.value=2020-01-02&originallyAvailableAt.locked=1';
|
||||
plexReqs.length = 0; r = await call(E, undefined, 'PUT');
|
||||
ok('the owner can save a title\'s details, locked so a refresh keeps them', r.status === 200 && plexReqs.length === 1 && plexReqs[0].startsWith('PUT /library/sections/1/all?type=1&id=5&title.value=New') && plexReqs[0].includes('title.locked=1'), [r.status, plexReqs]);
|
||||
plexReqs.length = 0; r = await call('/plex/library/metadata/5/poster?url=' + encodeURIComponent('/library/metadata/5/thumb/123'), undefined, 'PUT'); ok('and pick one of its posters', r.status === 200 && plexReqs.length === 1, [r.status, plexReqs]);
|
||||
plexReqs.length = 0; r = await call('/plex/library/metadata/5/posters?url=' + encodeURIComponent('https://img.example/p.jpg'), undefined, 'POST'); ok('or add one from an image link', r.status === 200 && plexReqs.length === 1, [r.status, plexReqs]);
|
||||
plexReqs.length = 0;
|
||||
const badEdit = [['PUT', '/plex/library/sections/1/all?type=1&id=5&mediaType=9&title.value=x'], ['PUT', '/plex/library/sections/1/all?type=1&id=5&deleteAll=1'], ['PUT', '/plex/library/sections/1/all?type=1&title.value=x'], ['PUT', '/plex/library/sections/1/all?type=9&id=5'],
|
||||
['DELETE', '/plex/library/sections/1/all?type=1&id=5&title.value=x'], ['PUT', '/plex/library/metadata/5/poster?url=file:///etc/passwd'], ['PUT', '/plex/library/metadata/5/poster?url=http://10.0.0.1/x.jpg'], ['PUT', '/plex/library/metadata/5/poster?url=https://a.example/x.jpg&extra=1'], ['POST', '/plex/library/metadata/5/poster?url=https://a.example/x.jpg'], ['PUT', '/plex/library/metadata/5/posters?url=https://a.example/x.jpg']];
|
||||
const bst = []; for (const [m, p] of badEdit) bst.push((await call(p, undefined, m)).status);
|
||||
ok('anything else in an edit request is refused before it reaches Plex', bst.every(x => x >= 400) && !plexReqs.length, [bst, plexReqs]);
|
||||
|
||||
// ================= Plex Home: using Plex as someone else in the Home =================
|
||||
r = await call('/api/plex/users'); ok('the Plex Home is listed (read from plex.tv\'s XML), with who is using NE Hub now', r.status === 200 && r.json.users.length === 3 && r.json.users.find(x => x.id === '1').current && r.json.users.find(x => x.id === '2').protected && r.json.users.find(x => x.id === '2').restricted && r.json.users.find(x => x.id === '1').admin, r.json);
|
||||
@@ -106,6 +118,7 @@ const jsonOut = (rs, code, o) => { rs.writeHead(code, { 'content-type': 'applica
|
||||
seen.length = 0; r = await call('/api/plex/switch', { id: '2', pin: '1234' }); ok('with the right PIN the hub switches to that person', r.status === 200 && r.json.user.title === 'Kid', r);
|
||||
r = await call('/api/plex/status'); ok('status says who is using Plex now', r.json.user && r.json.user.title === 'Kid' && r.json.signedIn && r.json.server === 'E-va', r.json);
|
||||
r = await call('/plex/library/sections'); ok('Plex requests now use that person\'s own server token', r.status === 200 && seen[seen.length - 1] === 'KIDSRV', [r.status, seen]);
|
||||
plexReqs.length = 0; r = await call('/api/plex/status'); const ke = await call(E, undefined, 'PUT'); ok('a Home member who is not the owner cannot edit (no Edit button, and the hub refuses)', r.json.canEdit === false && ke.status >= 400 && !plexReqs.length, [r.json, ke.status, plexReqs]);
|
||||
s = savedPlex(); ok('the owner\'s sign-in is kept (to switch back, and to list the Home) and the new one is saved', s && s.mainToken === 'ACCOUNT' && s.token === 'KIDTOKEN' && s.stk === 'KIDSRV' && s.user.title === 'Kid', s);
|
||||
r = await call('/api/plex/users'); ok('the list still works as the owner and marks the new current person', r.json.users.find(x => x.id === '2').current && !r.json.users.find(x => x.id === '1').current, r.json);
|
||||
r = await call('/api/plex/switch', { id: '3' }); ok('switching straight to another person works (no PIN needed for them)', r.status === 200 && r.json.user.title === 'Guest', r);
|
||||
|
||||
Reference in new issue
Block a user