/// // Jede Regel, die auf `@request.auth.id` zeigt, bekommt einen Login-Vorspann. // // ACHTUNG, das ist ein Loch und keine Kosmetik: Eine *leere* Relation ist in // PocketBase gleich dem *leeren* `@request.auth.id` einer anonymen Anfrage. // `team.admins.id ?= @request.auth.id` ist damit für jedes Team ohne Admins // wahr — auch ohne jeden Login. Dasselbe gilt für jede optionale Relation, // die leer sein kann: `created_by`, `stewards`, `rider.user`. // // Nachgewiesen an einer frischen Instanz mit einem Team ohne Admins: // Ein POST auf /api/collections/riders/records **ganz ohne Token** legte den // Fahrer an. Betroffen sind nach demselben Muster // // teams update (admins leer) // riders create/update/delete // trails update/delete, trail_versions create/delete // trail_flags create/update/delete // trail_markers update/delete (created_by, stewards) // trail_comments update/delete (created_by, stewards) // event_series update/delete (created_by) // event_participants create/update/delete (event.created_by) // // Der Vorspann `@request.auth.id != "" && (…)` kann nichts erlauben, was // vorher verboten war — er nimmt nur die anonyme Anfrage heraus, die keine // dieser Regeln je erfüllen sollte. Deshalb wird er stumpf vor jede Regel // gesetzt, die `@request.auth.id` erwähnt, statt die betroffenen Stellen // einzeln zu suchen: Was hier nicht auffällt, fällt sonst gar nicht auf. // // Gelesen werden die Regeln aus der Datenbank, nicht aus dieser Datei. Eine // im Admin-UI von Hand geänderte Regel wird also mit ihrem eigenen Wortlaut // abgesichert und nicht überschrieben. const GUARD = '@request.auth.id != ""' const COLLECTIONS = [ 'users', 'teams', 'events', 'runs', 'riders', 'times', 'trails', 'trail_versions', 'trail_flags', 'trail_markers', 'trail_comments', 'event_participants', 'event_series', 'team_invites', ] const RULES = ['listRule', 'viewRule', 'createRule', 'updateRule', 'deleteRule'] migrate((app) => { for (const name of COLLECTIONS) { let collection try { collection = app.findCollectionByNameOrId(name) } catch (err) { continue // Collection gibt es auf dieser Instanz (noch) nicht. } let changed = false for (const key of RULES) { const rule = collection[key] // Leere Regel heißt „nur Superuser" und braucht keinen Schutz; // eine Regel ohne @request.auth.id ist nicht gemeint. if (!rule || rule.indexOf('@request.auth.id') === -1) continue if (rule.indexOf(GUARD) === 0) continue collection[key] = GUARD + ' && (' + rule + ')' changed = true } if (changed) app.save(collection) } }, (app) => { const prefix = GUARD + ' && (' for (const name of COLLECTIONS) { // team_invites bringt den Vorspann aus 1754501600 mit; ihn hier // abzuräumen hieße, ein Loch zu graben, das es vorher nicht gab. if (name === 'team_invites') continue let collection try { collection = app.findCollectionByNameOrId(name) } catch (err) { continue } let changed = false for (const key of RULES) { const rule = collection[key] if (!rule || rule.indexOf(prefix) !== 0 || rule.charAt(rule.length - 1) !== ')') continue collection[key] = rule.slice(prefix.length, -1) changed = true } if (changed) app.save(collection) } })