From 8407b453677890d39772a4b65c69d0c97ad5ba41 Mon Sep 17 00:00:00 2001 From: Do Siki Date: Thu, 10 Sep 2026 13:26:41 +0200 Subject: [PATCH] feat(cms): Users access control + lockout policy (MITHOME-90) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explicit, documented decisions instead of relying on implicit Payload defaults: - auth.maxLoginAttempts: 5, lockTime: 10 min — codifies the lockout policy rather than leaving it as an unstated library default. - auth.cookies: { secure: NODE_ENV === 'production', sameSite: 'Lax' } — secure cookies once behind HTTPS (MITHOME-15), harmless over plain HTTP in local dev. - access.{create,read,update,delete,unlock}: explicit requireAuthenticatedUser (== Payload's defaultAccess, Boolean(user)). Investigated the known open advisory flagged in MITHOME-86 (GHSA-jg8r-5jh2-v2xj — any authenticated user can unlock any other account) by reading Payload's unlock operation source: the gap only matters when a less-privileged authenticated identity exists that needs protecting from a more-privileged one. This project's single "admin" role model (no role hierarchy — MITHOME-85 epic decision) has no such identity, so the default is accepted as-is, with the reasoning and a MITHOME-46 (central IDM/SSO) revisit trigger written into the code comment rather than left implicit. - Added an optional `name` field for a nicer admin identity than a bare email (audit trail, header display). Verified live: existing dev@mozdit.hu user unaffected (name column shows "", backward compatible). Reproduced the lockout for real — 5 wrong POST /api/users/login attempts, 6th attempt with the *correct* password still rejected ("locked due to too many failed login attempts"), unlocked via Local API (overrideAccess), then the correct password logged in successfully. build/lint/tsc/test (58 passed) all clean. Co-Authored-By: Claude Sonnet 5 --- proto/src/collections/Users.ts | 57 ++++++++++++++++++++++++++++++---- proto/src/payload.config.ts | 4 +-- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/proto/src/collections/Users.ts b/proto/src/collections/Users.ts index e495a5f..055854a 100644 --- a/proto/src/collections/Users.ts +++ b/proto/src/collections/Users.ts @@ -1,15 +1,60 @@ -import type { CollectionConfig } from 'payload' +import type { Access, CollectionConfig } from 'payload' /** - * Admin bejelentkezés (MITHOME-86 bootstrap). A teljes access control / - * jelszó-politika finomítása MITHOME-90 feladata — itt csak a Payload - * indulásához szükséges minimális auth collection. + * Admin bejelentkezés + access control (MITHOME-90). A projekt egyetlen + * "admin" szerepkörrel dolgozik (nincs role-hierarchia — lásd MITHOME-85 + * epic döntés); ez a collection az ügyfél és a fejlesztők közös, teljes + * jogú admin bejelentkezését adja. */ + +/** Bejelentkezett felhasználó — a REST/GraphQL create endpoint így nem + * használható publikus regisztrációra. Az admin UI "create first user" + * folyamata ettől függetlenül működik (Payload kivételt kezel, ha még + * nincs egyetlen user sem). */ +const requireAuthenticatedUser: Access = ({ req: { user } }) => Boolean(user) + export const Users: CollectionConfig = { slug: 'users', - auth: true, + auth: { + // WHY explicit (nem csak a Payload defaultra hagyva): a fiókzárolás + // szabályait tudatos döntésnek szánjuk, nem implicit könyvtár-alapértéknek. + maxLoginAttempts: 5, + lockTime: 10 * 60 * 1000, // 10 perc + cookies: { + // WHY NODE_ENV-alapú: helyi fejlesztésben (http://localhost) a secure + // cookie sosem menne át; staging/production HTTPS mögött viszont + // kötelező. MITHOME-15 (production domain/HTTPS) előfeltétele ennek + // valódi hatásba lépéséhez. + secure: process.env.NODE_ENV === 'production', + sameSite: 'Lax', + }, + }, admin: { useAsTitle: 'email', }, - fields: [], + // WHY minden op explicit requireAuthenticatedUser, holott ez a Payload + // defaultAccess-szel (Boolean(user)) megegyezik: az egyetlen "admin" + // szerepkör modellben ez helyes és elégséges — bármely bejelentkezett + // user már admin. Ismert, nyitott Payload advisory (GHSA-jg8r-5jh2-v2xj, + // <=3.88.0): az "unlock" op alapból bármely bejelentkezett usernek + // engedi más fiókok zárolásának feloldását — TÖBB szerepkör esetén ez + // jogosultság-átlépés lenne, de itt nincs "kevésbé jogosult" bejelentkezett + // user, akitől védeni kellene. Ha a MITHOME-46 (központi IDM/SSO) miatt + // több szerepkör/collection jön be, ezt itt újra kell értékelni. + access: { + create: requireAuthenticatedUser, + read: requireAuthenticatedUser, + update: requireAuthenticatedUser, + delete: requireAuthenticatedUser, + unlock: requireAuthenticatedUser, + }, + fields: [ + { + name: 'name', + type: 'text', + admin: { + description: 'Opcionális megjelenítendő név (pl. audit naplóban, admin fejlécben).', + }, + }, + ], } diff --git a/proto/src/payload.config.ts b/proto/src/payload.config.ts index 7759263..d8ad47c 100644 --- a/proto/src/payload.config.ts +++ b/proto/src/payload.config.ts @@ -36,8 +36,8 @@ const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) export default buildConfig({ - // Admin felület — bejelentkezés az Users collection-nel (MITHOME-90-ben - // finomodik: access control, jelszó-politika stb.) + // Admin felület — bejelentkezés az Users collection-nel. Access control / + // jelszó-politika: MITHOME-90 (src/collections/Users.ts). admin: { user: Users.slug, },