Multiple people: an admin adds users and chooses which tabs each may use
build-container / build (push) Successful in 2m20s
build-container / build (push) Successful in 2m20s
- Accounts: the setup code (or an existing single-user install, migrated automatically) creates the admin; the admin adds people in Settings -> People and gives each a one-time invite code to enrol their own passkey - Each person's tasks, NE Hub events and subscribe link, settings, calendar links, iCloud connection and Plex sign-in are private, stored in their own folder (/data/users/<id>/); Plex sign-in moved from browser cookies into that folder (encrypted), so it also follows them across devices - Tab access is enforced by the server (403) and reflected in the app (nav, Home, Settings, search, /api/config hides addresses of tabs a person cannot use); browser storage is namespaced per person - Admin tools: change tabs, invite a device, sign out everywhere (session epoch), turn off, make/remove admin, delete a person and their data; the last admin cannot be removed - Sessions carry the person (uid.epoch.expiry.signature); old single-user cookies still work as the admin - tools/users-test.js (83 checks: isolation, access limits, invites, sign-out/disable/delete, Plex and iCloud per person, migration) now runs in the Gitea workflow before the image is built Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
489ea0aaf0
commit
f18d9dd7a9
16 files changed
+703
-165
No files matched your search
@@ -0,0 +1,12 @@
|
||||
// Small helper to keep a secret value encrypted at rest (AES-256-GCM, key derived from the server secret).
|
||||
const crypto = require('crypto');
|
||||
const auth = require('./auth');
|
||||
module.exports = label => {
|
||||
let key; const k = () => key || (key = crypto.scryptSync(auth.secret, label, 32));
|
||||
return {
|
||||
seal(obj) { const iv = crypto.randomBytes(12), c = crypto.createCipheriv('aes-256-gcm', k(), iv);
|
||||
const d = Buffer.concat([c.update(JSON.stringify(obj), 'utf8'), c.final()]); return Buffer.concat([iv, c.getAuthTag(), d]).toString('base64'); },
|
||||
unseal(s) { try { const b = Buffer.from(s, 'base64'), d = crypto.createDecipheriv('aes-256-gcm', k(), b.subarray(0, 12));
|
||||
d.setAuthTag(b.subarray(12, 28)); return JSON.parse(Buffer.concat([d.update(b.subarray(28)), d.final()]).toString('utf8')); } catch { return null; } },
|
||||
};
|
||||
};
|
||||
Reference in new issue
Block a user