mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-19 09:15:19 +03:00
refactor(api)!: remove the shared operator secret, and let hosts sync their own
A single secret that turned any request into an operator was the only credential several routes accepted, and it had no caller left: the device pairing it existed for is on hold, and nothing in this tree or any client sent it. What remained was a key that bypassed authentication entirely, required to boot, and checked by nobody. Every route behind it had a better answer available: - Library and game sync move to host credentials. Both took a `userId` in the body, which meant one secret could write into anybody's library. A host now says which of its enrolled users a batch is for, and that claim is checked against the Steam sign-ins it actually holds — one box carries several people's accounts, so the pair is the unit. - Download-state reporting narrows to hosts alone, and the body that could name a different host is gone. Which host is reporting comes from its own credentials, and a body that still names one is refused rather than ignored. - Linking a Steam account is always for the caller. - Creating a game by hand is deleted; syncing already upserts the catalogue. - Reading the waitlist is deleted. Every address on it belongs to someone who has not agreed to anything, and answering it over HTTP made that list something a leaked key could drain. - The pairing-code routes are deleted with the flow they served. The domain module and its table stay, so returning to it is a route file rather than a migration. Nothing in the API now accepts a credential that stands for more than one caller: every request resolves to a specific user or a specific host, which is what lets a route say "the caller's own library" and mean it. BREAKING CHANGE: the `x-nestri-admin-token` header is no longer accepted and `ADMIN_SHARED_SECRET` is no longer read. `POST /games`, `GET /waitlist` and the `/pairing-code` routes are gone; `POST /games/sync` and `POST /library/sync` now require host credentials and take `userId` in the body; `POST /steam/link` no longer accepts `userId`; `POST /games/download-state` no longer accepts `hostId`.
This commit is contained in:
@@ -33,11 +33,6 @@ const System = z.object({
|
||||
})
|
||||
});
|
||||
|
||||
const Admin = z.object({
|
||||
type: z.literal('admin'),
|
||||
properties: z.object({})
|
||||
});
|
||||
|
||||
/**
|
||||
* A registered nessh host, authenticated by its own credentials.
|
||||
*
|
||||
@@ -58,7 +53,7 @@ const Machine = z.object({
|
||||
})
|
||||
});
|
||||
|
||||
const ActorInfo = z.discriminatedUnion('type', [Public, User, Member, System, Admin, Machine]);
|
||||
const ActorInfo = z.discriminatedUnion('type', [Public, User, Member, System, Machine]);
|
||||
type ActorInfo = z.infer<typeof ActorInfo>;
|
||||
|
||||
const _context = Context.create<ActorInfo>();
|
||||
|
||||
@@ -27,10 +27,6 @@ export namespace Env {
|
||||
*/
|
||||
AUTH_INTERNAL_URL: z.string().optional(),
|
||||
|
||||
SSH_AUTH_KEY: z.string().optional(),
|
||||
|
||||
ADMIN_SHARED_SECRET: z.string().optional(),
|
||||
|
||||
DATABASE_URL: z.string().optional()
|
||||
});
|
||||
|
||||
|
||||
@@ -148,6 +148,37 @@ export namespace Enrolment {
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* The enrolment a host holds for one user, or null.
|
||||
*
|
||||
* This is the question "may this host speak about this user's Steam
|
||||
* library?", and it is answered from the record of sign-ins rather than
|
||||
* from team membership: holding a refresh token for somebody is what makes
|
||||
* a host able to enumerate their games in the first place. One host carries
|
||||
* several people's sign-ins, so the pair is the unit and neither half of it
|
||||
* is enough on its own.
|
||||
*/
|
||||
export const findByMachineAndUser = fn(
|
||||
Info.pick({ machineId: true, userId: true }),
|
||||
async (input) => {
|
||||
return Database.use(async (tx) => {
|
||||
return tx
|
||||
.select()
|
||||
.from(SteamEnrolmentTable)
|
||||
.where(
|
||||
and(
|
||||
eq(SteamEnrolmentTable.machineId, input.machineId),
|
||||
eq(SteamEnrolmentTable.userId, input.userId)
|
||||
)
|
||||
)
|
||||
.then((rows) => {
|
||||
const row = rows.at(0);
|
||||
return row ? serialize(row) : null;
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Every enrolment the control plane believes this host has.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user