Zum Hauptinhalt springen
connic
Starte kostenlos
Apps

Zugriffsregeln

Lege fest, was die Besucher einer App in Datenbank, Dateien, Runs, Sessions und Freigaben ihres Environments lesen und ändern dürfen und welche Retrieval-Namespaces sie durchsuchen dürfen. Die Regeln laufen bei jeder Anfrage, und alles ohne Regel ist verboten.

Zuletzt aktualisiert

Die Data API gibt dem Browser-Code einer App die Projektdaten ihres Environments. access in apps/<key>.yaml enthält eine Regel pro Ressource und Operation: einen Ausdruck über den Besucher und den Datensatz. Eine App ohne access kann keine Daten lesen oder ändern. Der Tab Access der App im Dashboard zeigt die deployten Regeln.

Regeln

apps/portal.yaml
lookups:
  member:
    collection: members
    where: doc.email == user.email
access:
  database:
    tickets:
      read: doc.author_id == user.id or member.role == "agent"
      insert: user.signed_in == true
      update: doc.author_id == user.id and doc.status == "open"
      delete: member.role == "admin"
      set:
        author_id: user.id
        status: '"open"'
  files:
    attachments:
      read: member.role == "agent"
  runs:
    read: run.context.end_user.id == user.id
  retrieval:
    help:
      search: true
RessourceKeysOperationen
databaseName der Collectionread, insert, update, delete, set
filesOrdnerread, write, update, delete
runsread, cancel
sessionsread, delete
approvalsread, decide
retrievalNamespacesearch

Eine fehlende Ressource, Collection, ein fehlender Ordner, Namespace oder eine fehlende Operation ist verboten. Auch YAML-true und false sind Regeln: read: true lässt jeden Besucher lesen. Setze eine Regel, die mit einem Anführungszeichen beginnt, in Quotes, etwa read: '"finance" in user.groups', damit YAML sie als Text liest.

Namen

Jede Regel sieht user, die Lookups und den Datensatz ihrer Operation. Ein Name, der nicht verfügbar ist, lässt connic lint scheitern.

NameVerfügbar inFelder
userjede Regelid, email, name, groups, claims, signed_in
<lookup>jede Regel außer dem where eines Lookupsdie Felder des Dokuments plus id, created_at, updated_at; null, wenn nichts gefunden wurde
docdatabase read, update, deletedie gespeicherten Felder plus id, created_at, updated_at
newdatabase insert, update; files write, updateinsert: jedes gesendete Dokument; update: das Update-Objekt; files write: path, size, mime_type, metadata des Uploads; files update: dieselben Felder nach der Änderung
filefiles read, update, deletepath, folder, name, size, mime_type, metadata, created_at, updated_at
runruns read, cancelid, agent, status, context, input, output, queued_at, ended_at, duration_ms
sessionsessions read, deleteid, agent, state, created_at, updated_at
approvalapprovals read, decideid, agent, tool, params, status, context (der Kontext des Runs), created_at

user ist der angemeldete Besucher; was id, groups und claims in jedem Modus enthalten, steht unter Anmeldung. run.input und run.output lassen sich als JSON lesen, etwa run.input.order_id. Runs, die eine App startet, tragen den Besucher in run.context.end_user, Freigaben in approval.context.end_user.

Syntax

Regeln nutzen die Ausdruckssprache von connic, dieselbe wie Approval-Regeln und bedingte Tools: Vergleiche, and, or, not, in, not in, Pfade mit Punkten oder Subscripts und Literale, ohne Funktionsaufrufe und Arithmetik, bis zu 2.000 Zeichen. Weil connic den Teil einer Regel, der den Datensatz betrifft, in einen Abfragefilter übersetzt, gelten für die Datensatznamen doc, file, run, session und approval diese Grenzen:

  • Vergleiche ein Feld mit einem Wert, etwa doc.done == true. Ein bloßes doc.done wird abgelehnt.
  • Eine Regel kann weder zwei Felder des Datensatzes noch ein Feld des Datensatzes mit new vergleichen.
  • doc.id, run.id, run.status, approval.id und approval.status erlauben nur ==, !=, in und not in.
  • Feldnamen dürfen nicht mit _ oder $ beginnen und keine Punkte enthalten. Die gespeicherten Systemfelder heißen id, created_at und updated_at.

Außerdem gelten für jede Regel diese Grenzen:

  • is und is not funktionieren nur mit den Literalen null, true und false.
  • Ein Vergleich kann keine andere Bedingung als Wert nehmen, deshalb wird (doc.done == true) == false abgelehnt. Verknüpfe Vergleiche mit and, or und not.
  • Regeln für Datenbank und Dateien sowie das where von Lookups werden zu Datenbankfiltern mit diesen Grenzen: 10 Verschachtelungsebenen, von denen der Filter, den connic um Zugriffsregeln legt, eine belegt, 100 Zweige pro and oder or, 256 Terme und 1.000 Werte pro in-Liste.

connic lint prüft Syntax und Namen. Bei öffentlichen Apps warnt das Deployment im Log vor Regeln, die jeden Besucher alles lesen lassen, Schreibzugriffe nicht vom Besucher abhängig machen oder Uploads ohne Größengrenze erlauben. Das Deployment prüft, ob connic jede Regel durchsetzen kann, und scheitert mit dem Pfad der Regel, etwa mit use is and is not only with null, true or false oder it compiles to a database filter that is too large: <reason>:

Deployment-Log
App validation failed:
  - apps/portal.yaml: access.database.tickets.read: can't compare two fields of doc

So wirken die Regeln

Bei jeder Anfrage wertet connic alle Teile einer Regel aus, die den gespeicherten Datensatz nicht betreffen: user, die Lookups und new. Was über den Datensatz übrig bleibt, wird zu einem Filter, den connic dem Filter des Browsers hinzufügt. Listen, Zählungen, Summen, Updates und Löschungen erreichen deshalb nur erlaubte Datensätze.

  • Eine Regel, die unabhängig vom Datensatz falsch ist, lehnt die Anfrage mit 403 ab. Bei einem einzelnen Run, einer Datei, Session oder Freigabe antwortet ein Datensatz, den die Regel nicht erlaubt, mit 404, als gäbe es ihn nicht.
  • insert und write bei Dateien prüfen new vor dem Schreiben. Jedes Dokument eines Inserts muss die Regel erfüllen, sonst wird nichts eingefügt.
  • update in der Datenbank ändert nur die Dokumente, die der doc-Teil erlaubt, und der new-Teil prüft das Update. Eine Regel auf new.status scheitert, wenn das Update status nicht setzt.
  • update bei Dateien sieht die Datei vor der Änderung als file und danach als new. Wer eine Datei in einen anderen Ordner verschiebt, braucht zusätzlich dessen write-Regel.
  • Eine Regel kann für einen einzelnen Besucher trotzdem einen zu großen Filter ergeben, etwa doc.team in user.groups für jemanden in mehr als 1.000 Gruppen. Die Anfrage scheitert dann mit 500 This app's access rule makes a database filter that is too large: <reason>. Simplify the rule or the values it compares with.

Fehlende Werte und null

Ein Vergleich ist unbestimmt, wenn eine seiner Seiten fehlt, etwa ein nicht vorhandenes Feld oder ein Feld eines Lookups ohne Treffer, oder wenn sie null ist. not lässt Unbestimmtes unbestimmt. and ist falsch, sobald ein Teil falsch ist, or ist wahr, sobald ein Teil wahr ist, und sonst macht ein unbestimmter Teil das Ergebnis unbestimmt. Eine Regel, die unbestimmt endet, verbietet. not (member.blocked == true) verbietet deshalb einem Besucher ohne Member-Dokument den Zugriff.

Nur ein Vergleich mit dem Literal null prüft auf null. doc.x == null und doc.x is null sind wahr, wenn das Feld existiert und null ist, falsch, wenn es einen anderen Wert hat, und unbestimmt, wenn es fehlt; != und is not liefern für ein vorhandenes Feld das Gegenteil. Ein Lookup ohne Treffer gilt als vorhanden und null, member == null ist also wahr. doc.status != "archived" trifft nur Dokumente, deren status einen anderen Wert hat; mit or doc.status == null kommen die hinzu, bei denen er null ist.

Vergleiche

  • Werte werden wie JSON verglichen. Jeder Typ ist nur sich selbst gleich: true ist nicht 1 und "1" nicht 1, aber 1 ist gleich 1.0.
  • <, <=, > und >= vergleichen zwei Zahlen oder zwei Texte, Texte nach Codepoints. Jedes andere Paar ist unbestimmt.
  • x in list prüft die Einträge der Liste mit derselben typgenauen Gleichheit, und eine leere Liste ergibt false. x in object prüft die Schlüssel des Objekts. x in text ist nur wahr, wenn x dem ganzen Text entspricht, nie für einen Teil davon; "vip" in doc.tags passt also zu einer Liste von Tags. In allem anderen ist in unbestimmt.
  • Zeitstempel wie doc.created_at, file.updated_at, run.queued_at oder approval.created_at werden als Zeitpunkte mit ISO-8601-Text verglichen, etwa doc.created_at >= "2026-01-01T00:00:00Z". Text ohne Zeitzonen-Offset gilt als UTC.
  • Ein einzelner Wert als Bedingung, etwa user.signed_in, muss true oder false sein; alles andere ist unbestimmt.

set

set listet Felder, die connic bei jedem Insert schreibt, über das hinweg, was der Browser geschickt hat. Jeder Wert ist ein Ausdruck über user und die Lookups: author_id: user.id hält fest, wer ein Dokument geschrieben hat, und status: '"open"' schreibt einen festen Text. Lässt sich ein Wert nicht auswerten, etwa weil sein Lookup nichts gefunden hat, wird der Insert abgelehnt. Ein Update, das ein set-Feld nennt, scheitert mit 400 Field 'author_id' is set by the app and can't be changed.

Lookups

Ein Lookup findet ein Dokument in einer Collection für Regeln, die von gespeicherten Daten abhängen, etwa der Rolle eines Mitglieds. collection nennt die Collection, und where ist ein Ausdruck über doc und user. connic führt einen Lookup nur aus, wenn eine Regel der Anfrage ihn nutzt, höchstens einmal pro Anfrage, und nimmt das älteste passende Dokument. Die Collection des Lookups braucht keine eigene Zugriffsregel, und der Browser kann sie nur lesen, wenn eine Regel es erlaubt.

Lookup-Namen sind Bezeichner wie member und dürfen nicht user, doc, new, file, run, session, approval, true, false oder null heißen.

Ordner

Die Keys unter files sind Ordner, mit oder ohne Schrägstriche. Eine Regel gilt für ihren Ordner und alles darunter, und der genaueste Ordner gewinnt: Mit Regeln für reports und reports/private folgen Dateien in reports/private/2026 der Regel von reports/private. Der Wurzelordner kann keine Regel haben, Dateien direkt darin bleiben also unerreichbar.

Eine Liste zeigt eine Ebene: die Dateien des Ordners, die seine Regel den Besucher lesen lässt, und die Unterordner mit mindestens einer Datei, die der Besucher nach der Regel dieser Datei lesen darf. Dateien unter einem Ordner, dessen Regel das Lesen verbietet, bleiben verborgen, außer ein genauerer Ordner darunter hat eine Regel, die sie erlaubt. Ein Ordner ohne eigene Regel, etwa der Wurzelordner, liefert keine Dateien, aber die Unterordner mit lesbaren Dateien. Nur wenn keine Regel auf oder unter dem Ordner das Lesen erlauben kann, wird die Liste mit 403 abgelehnt.

Retrieval-Namespaces

Die Keys unter retrieval sind Namespaces in Retrieval, etwa help oder docs.public. Eine Regel search gilt für ihren Namespace und alle Namespaces darunter, denn eine Suche liefert Textstellen aus allen. Die Regel sieht user und die Lookups, nicht die Einträge. Sie entscheidet also, ob der Besucher den ganzen Namespace durchsuchen darf.

Ein genannter Namespace darf nicht in einem anderen genannten liegen: Neben help lässt help.internal das Deployment mit help already covers it; declare only one of them scheitern. Lege Inhalte für unterschiedliche Zielgruppen in getrennte Namespaces, etwa public.help und internal.help. Einträge ohne Namespace lassen sich aus einer App nicht durchsuchen.

Öffentliche Besucher

In einer öffentlichen App ist user.signed_in false, user.email und user.name sind null, user.groups ist leer, und user.id ist die Besucher-ID des Browsers.

Vergleiche mit der E-Mail-Adresse eines öffentlichen Besuchers sind unbestimmt. doc.owner == user.email trifft deshalb kein Dokument, auch keines, dessen owner null ist, und ein Lookup mit where: doc.email == user.email findet nichts.

connic leitet die Besucher-ID für jede App getrennt aus einem Secret im Cookie des Browsers ab. Wer die ID eines anderen kennt, kann also nicht in dessen Namen handeln. Die ID ändert sich, wenn jemand die Cookies löscht, und jeder kann sich eine neue holen. Regeln auf user.id passen zu Daten, die zu einem Browser gehören, etwa einem Entwurf, nicht zu Daten, die ein Konto brauchen.

Öffentliche Formulare schützen

Die Formulare einer öffentlichen App kann jeder absenden, auch Bots. Schreibzugriffe über die Data API prüft connic gegen deine Regeln, aber ohne Middleware, eine Challenge lässt sich dort also nicht prüfen. Für Formulare, die Bots anziehen, schützt du das Formular selbst und lässt einen Agenten schreiben:

  1. Binde einen Challenge-Dienst wie ein CAPTCHA in das Formular ein. Er fügt dem Absenden ein Token hinzu.

  2. Schicke das Formular an einen Sync-Webhook-Connector mit apps_only: true unter /_connic/c/<key>, statt es über die Data API einzufügen.

  3. Prüfe das Token in der Middleware des Agenten beim Challenge-Dienst und stoppe den Run, wenn die Prüfung scheitert. Danach schreibt der Agent mit Database Tools.

  4. Lass insert in den Regeln der Collection weg, damit der Browser nicht an der Prüfung vorbei schreiben kann.

middleware/contact-form.py
import os

import httpx
from connic import StopProcessing


async def before(content, context):
    token = context["payload"].get("challenge_token")
    async with httpx.AsyncClient(timeout=10) as client:
        response = await client.post(
            os.environ["CHALLENGE_VERIFY_URL"],
            data={"secret": os.environ["CHALLENGE_SECRET"], "response": token},
        )
    if not response.json().get("success"):
        raise StopProcessing("Die Formularprüfung ist fehlgeschlagen. Lade die Seite neu und versuche es noch einmal.")
    return content

Wie die Prüfung aussieht, hängt vom Challenge-Dienst ab; das Beispiel schickt das Token und ein Secret an die Prüf-URL des Dienstes, beide als Variablen gespeichert. StopProcessing beendet den Run, bevor der Agent startet, und das Formular bekommt die Meldung als Ausgabe des Runs. Ein rate_limit am Connector begrenzt, wie viele Runs das Formular pro Minute starten kann. connic prüft Challenges nicht selbst.

Beispiele

Eigene Datensätze

Angemeldete Besucher sehen und ändern nur die Notizen, die sie angelegt haben. set trägt beim Insert den Besitzer ein, und Updates können ihn nicht ändern.

apps/notes.yaml
access:
  database:
    notes:
      read: doc.owner_id == user.id
      insert: user.signed_in == true
      update: doc.owner_id == user.id
      delete: doc.owner_id == user.id
      set:
        owner_id: user.id

Admins über einen Lookup

In einer App mit Anmeldung hält eine Collection members, gepflegt von Agenten oder importiert, Kunde und Rolle jeder Person. Kunden sehen ihre eigenen Bestellungen, Admins sehen und ändern alle.

apps/orders.yaml
lookups:
  member:
    collection: members
    where: doc.email == user.email
access:
  database:
    orders:
      read: doc.customer_id == member.customer_id or member.role == "admin"
      update: member.role == "admin"

Veröffentlichte Inhalte in einer öffentlichen App

Jeder kann veröffentlichte Artikel lesen. Entwürfe bleiben verborgen, und niemand kann schreiben.

apps/help.yaml
auth:
  mode: public
access:
  database:
    articles:
      read: doc.status == "published"

Dateien nach Gruppe und Metadaten

Die Gruppe billing liest Rechnungen. Jeder Besucher lädt PDFs bis 10 MB nach uploads hoch, markiert mit seiner eigenen ID aus /_connic/me, und sieht nur seine eigenen Uploads.

apps/portal.yaml
access:
  files:
    invoices:
      read: '"billing" in user.groups'
    uploads:
      read: file.metadata.uploaded_by == user.id
      write: >-
        new.metadata.uploaded_by == user.id
        and new.mime_type == "application/pdf"
        and new.size <= 10000000

Runs, die der Besucher gestartet hat

Besucher verfolgen die Runs, die sie über die Connectors der App gestartet haben, und brechen sie ab, solange sie warten oder laufen.

apps/portal.yaml
access:
  runs:
    read: run.context.end_user.id == user.id
    cancel: run.context.end_user.id == user.id and run.status in ["queued", "started"]

Freigaben für eine Gruppe

Die Gruppe finance sieht jede Freigabe des Environments und entscheidet über Erstattungen unter 5.000.

apps/finance.yaml
access:
  approvals:
    read: '"finance" in user.groups'
    decide: '"finance" in user.groups and approval.tool == "issue_refund" and approval.params.amount < 5000'

Jeder kann die Artikel in help und den Namespaces darunter über retrieval/search durchsuchen. In einer App mit Anmeldung beschränkt eine Regel wie search: '"support" in user.groups' die Suche auf eine Gruppe.

apps/help.yaml
auth:
  mode: public
access:
  retrieval:
    help:
      search: true