Zugriffsmodifikatoren
CK Language 2 führt drei Modifikatoren ein. Alle erfordern ckLanguage: 2 (andernfalls Meldung 90).
| Modifikator | Gilt für | Werte | Standard in einem v2-Modell | Zweck |
|---|---|---|---|---|
visibility | Typ, Record, Enum, Attribut, Assoziationsrolle, Interface, Methode | Public, Internal | Public | Dürfen andere Modelle das Element referenzieren? |
derivable | Typ, Record | Model, Any | Model | Dürfen andere Modelle von dem Element ableiten? |
access | Attributzuweisung an einem Typ, Record oder einer Assoziationsrolle | ReadWrite, ReadOnly, MethodOnly, Hidden | ReadWrite | Wie darf die generische API das Attribut lesen und schreiben? |
In einem Modell mit CK Language 1 ist jedes Element faktisch Public, derivable: Any und access: ReadWrite. Die bestehenden Modifikatoren isAbstract und isFinal behalten in beiden Sprachversionen ihre Bedeutung.
visibility
Internal-Elemente können nur innerhalb ihres eigenen Modells referenziert werden. Jeder Verweis aus einem anderen Modell wird mit Meldung 112 abgelehnt – das Ableiten von einem internen Typ oder Record, das Zuweisen eines internen Attributs (an einem Typ, Record, einer Assoziationsrolle oder einem Interface-Member), die Verwendung eines internen Records oder Enums als Wertetyp, das Implementieren oder Erweitern eines internen Interfaces, die Verwendung einer internen Assoziationsrolle oder eines internen Ziels sowie Methodenparameter/-ergebnisse, die interne Records oder Enums verwenden.
attributes:
- id: InternalNote
valueType: String
visibility: Internal
Error 112 types/types.yaml: 'Acme.Plant-1.0.0/Note-1' references the internal attribute 'Acme.Assets-1.0.0/InternalNote-1'
of another model. Internal elements (visibility: Internal) can only be referenced inside their own model.
Innerhalb des eigenen Modells darf ein internes Element nur von anderen internen Elementen referenziert werden. Ein öffentliches Element, das ein internes Element desselben Modells referenziert, wird mit Meldung 129 („inconsistent visibility“) abgelehnt, weil andere Modelle darüber an das interne Element gelangen würden:
- ein öffentlicher Typ, der von einem internen Typ ableitet, ein internes Interface implementiert, ein internes Attribut zuweist oder eine interne Assoziationsrolle bzw. ein internes Ziel verwendet;
- ein öffentliches Attribut, dessen Wert-Record oder -Enum intern ist, ein öffentlicher Record, der von einem internen Record ableitet oder ein internes Attribut zuweist, eine öffentliche Assoziationsrolle, die ein internes Attribut zuweist;
- ein öffentliches Interface, das ein internes Interface erweitert, ein internes Member, eine interne Assoziationsrolle oder ein internes Ziel hat oder eine interne Methode deklariert;
- eine öffentliche Methode eines öffentlichen Typs, deren Parameter oder Ergebnis einen internen Record oder ein internes Enum verwendet;
- ein Typ, der eine Methode eines öffentlichen Interfaces, das er implementiert, als intern neu deklariert.
Error 129 types/types.yaml: Public 'Acme.Assets-1.0.0/Pump-1' references the internal type 'Acme.Assets-1.0.0/Helper-1'
(base type). A public element may only reference public elements of its own model (visibility consistency): make the
referenced element public or 'Acme.Assets-1.0.0/Pump-1' internal.
Erlaubt sind: intern → öffentlich, intern → intern, öffentlich → öffentlich sowie interne Methoden auf öffentlichen Typen (sie dürfen interne Records und Enums verwenden).
Interne Elemente sind ein Implementierungsdetail ihres Modells: Sie gehören nicht zur Kompatibilitätsoberfläche. Ihre Änderung ist höchstens Minor (siehe SemVer); Regel 129 macht dies sicher.
derivable
derivable steuert, welche Modelle von einem Typ oder Record ableiten dürfen:
Model– nur das deklarierende Modell darf davon ableiten;Any– jedes Modell darf davon ableiten.
Wird es weggelassen, ist der Wert in einem Modell mit ckLanguage: 2 Model und in einem v1-Modell Any. Deklarieren Sie derivable: Any an jedem Typ oder Record, der als Basis für andere Modelle gedacht ist:
types:
- typeId: Device
isAbstract: true
derivable: Any # other models may derive from Device
derivedFromCkTypeId: ${System}/Entity
- typeId: Sensor # derivable defaults to Model
derivedFromCkTypeId: ${this}/Device
Das Ableiten von Sensor in einem anderen Modell schlägt fehl – auch dann, wenn das andere Modell ein Modell mit CK Language 1 ist:
Error 113 types/types.yaml: Type 'Acme.Plant-1.0.0/Pump-1' derives from 'Acme.Assets-1.0.0/Sensor-1' of another model,
which only its own model may derive from (derivable: Model). The base model must declare 'derivable: Any' to allow it.
derivable und isFinal sind voneinander unabhängig: isFinal: true verbietet das Ableiten überall, derivable: Model nur außerhalb des Modells. Im generierten C# wird ein derivable: Model-Typ ohne Untertyp im eigenen Modell zu sealed.
Die Änderung von derivable von Any auf Model ist eine Major-Änderung, von Model auf Any eine Minor-Änderung. Wird ein bestehendes Modell von CK Language 1 auf 2 umgestellt, kippt der Standard jedes Typs und Records auf Model – eine Major-Änderung, sofern nicht jeder von ihnen derivable: Any deklariert. Siehe Versionierungsregeln.
Durchsetzung beim Import
visibility und derivable sind nicht nur Prüfungen zur Compile-Zeit. Die Engine wiederholt sie jedes Mal, wenn ein Modell aufgelöst wird – beim Compile, beim Publish und bei jedem Import in einen Tenant. Ein gefälschtes oder von einem älteren Compiler kompiliertes Modell kann sie nicht umgehen: Es scheitert mit 112/113 und wird nicht importiert.
Die Prüfung gilt für das Modell, das kompiliert, veröffentlicht oder importiert wird – nicht für bereits installierte Modelle. Macht ein Basismodell später ein Element Internal oder derivable: Model, werden seine installierten abhängigen Modelle nicht erneut geprüft und bleiben Available; sie scheitern erst mit 112/113, wenn sie erneut kompiliert oder importiert werden. Eine solche Änderung ist eine Major-Versionsänderung des Basismodells (siehe SemVer); veröffentlichen Sie sie daher nur mit einer neuen Major-Version.
access
access wird an einer Attribut-Zuweisung gesetzt – dem Eintrag unter attributes: eines Typs, Records oder einer Assoziationsrolle –, nicht an der Attributdefinition:
types:
- typeId: Device
attributes:
- id: ${this}/SerialNumber
name: SerialNumber
access: ReadOnly
- id: ${this}/Firmware
name: Firmware
isOptional: true
access: MethodOnly
- id: ${this}/ApiKeyHash
name: ApiKeyHash
isOptional: true
access: Hidden
| Wert | Bedeutung |
|---|---|
ReadWrite | Standard. Über die generische GraphQL-API les- und schreibbar. |
ReadOnly | Soll nur beim Erstellen gesetzt werden und danach schreibgeschützt sein. |
MethodOnly | Lesbar, aber nicht über die generischen create/update-Mutationen schreibbar – es soll ausschließlich durch Methoden geändert werden. Nicht Teil der generierten GraphQL-Eingabetypen. |
Hidden | Wird nie über die GraphQL-API bereitgestellt – kein Feld in Ausgabe- oder Eingabetypen, nicht filterbar, nicht sortierbar. |
ReadOnly und MethodOnly in dieser PhaseReadOnly wird durch Compile, Persistenz und die CK-Meta-API durchgereicht, aber noch nicht durchgesetzt: Die generischen GraphQL-Mutationen akzeptieren es beim Update weiterhin. Der Methodenaufruf ist noch nicht verfügbar, sodass ein MethodOnly-Attribut derzeit über GraphQL überhaupt nicht geändert werden kann – nur durch Blueprint-Seed-Daten, ImportRt oder Service-Code.
Compiler-Regeln für Hidden
Ein Hidden-Wert darf über keine andere Modellfunktion durchsickern. Der Compiler lehnt ab:
| Regel | Meldung |
|---|---|
Eine Anzeigeregel (displayNameRule, displayDescriptionRule) oder ein Text-Indexpfad erreicht ein Hidden-Attribut; ein ownerAttributePath erreicht ein Hidden- oder MethodOnly-Attribut (einschließlich Record-Segmenten) | 105 |
Ein anderer Index (Unique, UniqueNotDeleted, Ascending, ...) erreicht ein Hidden-Attribut – ein Unique-Index würde Werte über Duplicate-Key-Fehler preisgeben | 106 |
Eine Hidden-Zuweisung deklariert autoCompleteValues | 107 |
access: Hidden an einem Attribut einer Assoziationsrolle (nicht unterstützt; ReadOnly und MethodOnly sind es) | 108 |
| In einem v2-Modell ein Indexpfad-Segment, das kein Attribut benennt (Pfade werden ohne Beachtung der Groß-/Kleinschreibung abgeglichen, wie bei MongoDB) | 109 |
| Ein Typ weist ein Interface-Member als Hidden zu | 99 |
Index- und Regelpfade werden ohne Beachtung der Groß-/Kleinschreibung aufgelöst, sodass passwordHash PasswordHash erreicht. Da alle Typen einer Collection dieselben gespeicherten Attributnamen teilen, greifen die Regeln 105 und 106 auch dann, wenn der Index oder die Regel an einem Typ deklariert ist und ein anderer Typ derselben Collection (ein Geschwister- oder abgeleiteter Typ, möglicherweise in einem anderen Modell) dieses Attribut als Hidden zuweist.
Import-Absicherung für Indizes. Meldung 106 ist der primäre Schutz, vom Compiler geprüft. Als zweite Verteidigungslinie für handeditierte oder ältere kompilierte Modelle überspringt die Indexpflege beim Import in einen Tenant Indexpfade, die ein Hidden-Attribut erreichen, und protokolliert einen ERROR (Skipping Hidden attribute ...); ein Index ohne verbleibende Felder wird nicht angelegt. Betrachten Sie die Absicherung als Sicherheitsnetz, nicht als Ersatz für einen sauberen Compile.
Was Hidden schützt
Hidden wird von der GraphQL-API des Asset Repository durchgesetzt:
- Lesen: Hidden-Attribute haben kein Feld am Entitätstyp, an den Abstract-Type- und CK-Interface-Typen, an Record-Typen und an den generischen
attributes-Projektionen von Entitäten, Records und Assoziationen. Ist der CK-Typ einer Entität unbekannt, wird jeder Attributname verworfen, der irgendwo im Tenant Hidden ist (fail closed). - Schreiben: Die generischen
create/update-Mutationen und Query-Row-Schreibvorgänge lehnen Hidden- (undMethodOnly-)Attribute mitATTRIBUTE_NOT_WRITABLEab. - Filtern, Sortieren, Suchen, Aggregation, Group-by: Ein Hidden-Pfad wird mit
ATTRIBUTE_NOT_QUERYABLEabgelehnt. - Abfragespalten und Entitäts-Selektoren: Hidden-Spalten werden nicht angeboten und können nicht angefordert werden; Selektorschlüssel wie
members.someType[apiKeyHash='X']werden abgelehnt (sie wären ein Gleichheits-Orakel). - Assoziations- und Navigationspfade: Filter und Sortierungen, die an Assoziations-/Navigationsverbindungen übergeben werden, werden gegen den Zieltyp und jeden davon abgeleiteten Typ geprüft; Pfade über Navigationen hinweg werden nach Attributnamen mit jeder Hidden-Zuweisung im Tenant abgeglichen.
- Record-Pfade: Ein Hidden-Record-wertiges Attribut schützt auch jedes
record.fielddarunter. - Archive: Hidden-Attribute dürfen nicht archiviert werden – siehe unten.
Zugriffsfehler enthalten den Fehlercode, den Attributpfad und die Operation, aber nie einen Wert – auch nicht in der Umgebung Development; Werte in Entitäts-Selektor-Pfaden werden geschwärzt ([apiKeyHash=…]).
Was Hidden (noch) nicht schützt
Hidden ist eine Garantie für die API-Oberfläche, keine Verschlüsselung. Für Entitätsdaten wird es nur von der GraphQL-API des Asset Repository durchgesetzt (zuzüglich der obigen Compiler-Regeln); für Stream-Daten setzt die Engine die Archiv-Regel durch. Andere Services und Pfade lesen und schreiben Entitäten über die Engine ohne Zugriffsprüfungen. Insbesondere deckt es Folgendes nicht ab:
ImportRt– Runtime-Daten-Importe können Hidden- (undMethodOnly-)Attribute schreiben;- Runtime-Export – RT-Exporte enthalten Hidden- und
MethodOnly-Werte; - Identity-Felder –
System.Identity/User(Benutzer-Token, Zwei-Faktor- und Sperrfelder, Passwort-Hash) ist weiterhin ein Modell mit CK Language 1 und verwendet daher überhaupt keinaccess; - Services, die die Engine direkt nutzen – Pipelines (Mesh Adapter), Bots und andere Services werden durch
accessnicht eingeschränkt; - Berechnete Spalten von Abfragen und
targetCkAttributeIdsvon Assoziationen werden nicht gegen Hidden-Attribute geprüft; - Integrität von Hidden- und
MethodOnly-Werten bei generischen Schreibvorgängen – ein generisches Update, das einen ganzen Record schreibt, baut diesen aus den Eingabefeldern neu auf, sodass Hidden- oderMethodOnly-Unterattribute dieses Records gelöscht werden;clearSecretAttributeskann einSecret-Attribut löschen, das zugleich Hidden oderMethodOnlyist. Vermeiden Sie solche Kombinationen, bis dies behoben ist.
ImportRt, Export und die Identity-Felder sind zusammen mit der Umstellung von System.Identity auf CK Language 2 geplant. Verlassen Sie sich bis dahin nicht auf Hidden für Werte, die die Datenbank über diese Pfade nicht verlassen dürfen. Verwenden Sie für Zugangsdaten den Secret-Wertetyp, der im Ruhezustand verschlüsselt ist.
Hidden bei archivierten Attributen
Regel: Hidden-Attribute werden nie archiviert. Die Entitäts-Zugriffsschranken der GraphQL-API gelten nicht für Stream-Daten (CrateDB), daher hält die Engine Hidden-Werte aus Archiven fern. Da Archive Runtime-Entitäten sind, wird die Regel zur Laufzeit durchgesetzt, nicht vom Compiler:
- ein Archiv kann nicht aktiviert (oder wiederholt, neu bereitgestellt) werden, wenn eine erfasste Spalte ein Hidden-Attribut erreicht – direkt oder als ganzer Record (oder Record-Array), der in beliebiger Tiefe ein Hidden-Unterattribut enthält. Berechnete Spalten und Rollup-Spalten werden nicht geprüft;
- als zweite Verteidigungslinie verweigert der Column Builder Hidden-Spalten und lässt Hidden-Unterattribute in gespeicherten Records weg;
- Stream-Daten-Abfragen lehnen Spalten, Filter, Sortierungen und Group-bys ab, die ein Hidden-Attribut erreichen.
Nach einer Modelländerung. Daten werden beim Ingest nicht geprüft. Stattdessen werden aktive Archive nach jedem CK-Modell-Import in den Tenant erneut geprüft: Ein aktives Archiv, das nun ein Hidden-Attribut erfasst, wird auf Failed gesetzt. Ein Failed-Archiv verweigert alle Inserts und alle Abfragen – auch seiner sichtbaren Spalten –, bis es erneut aktiviert wird (RetryActivation), was erst gelingt, wenn das Hidden-Attribut nicht mehr erfasst wird. Die Historie bleibt erhalten. In dieser Phase läuft die erneute Prüfung nur in Services, die Stream-Daten verwenden, und es gibt keine Operation, um eine einzelne erfasste Spalte zu entfernen. Dies ist eine bekannte Einschränkung, die behoben wird, bevor System-Modelle Hidden an archivierten Attributen verwenden.
Entwerfen Sie Archive so, dass sie nie Zugangsdaten oder andere Hidden-Werte erfassen; verwenden Sie Secret für Zugangsdaten.
access und SemVer
access ist geordnet: ReadWrite < ReadOnly < MethodOnly < Hidden. Eine strengere Zugriffsstufe einer Zuweisung ist
MAJOR: Generische GraphQL-Clients und abhängige Modelle verlieren Lese- oder Schreibzugriff. Eine lockerere ist MINOR.
Beide erhalten im Changelog einen Hinweis „access/security“.
Sicherheitsausnahme. Das Verbergen eines Zugangsdatums darf keine Major-Kaskade über alle abhängigen Modelle erzwingen. Ist
die Attributdefinition in der veröffentlichten Version und in der neuen als securitySensitive: true markiert,
ist die Verschärfung ihres Zugriffs MINOR und die Änderung erfordert eine Bestätigung (Acknowledge) durch den Herausgeber (aufgeführt unter
„Behavioural changes“ im Verdict und im Changelog). Jede andere Änderung eines solchen Attributs – Entfernen, Änderung
des Typs – folgt den normalen Regeln.
Der Build schlägt mit OCTO-CK203 fehl, bis der Herausgeber die Änderung in ckModel.yaml bestätigt. Der Fehler gibt den
Änderungsschlüssel und einen kopierfertigen Eintrag aus:
compatibility:
acknowledge:
- change: "TypeAttribute:Account-1/PasswordHash#Modified:access"
reason: "Close accepted risk R13: password hash readable via GraphQL"
Der reason ist Pflicht, Schlüssel sind exakt (keine Wildcards), ein Eintrag gilt nur für ein Release (OCTO-CK204 bei einem
veralteten Eintrag), und eine Bestätigung senkt nie den erforderlichen Versionssprung. Details: Versionierungsregeln.
securitySensitive
securitySensitive: true an einer Attributdefinition markiert Passwort-Hashes, Security Stamps, Token und 2FA-Geheimnisse
(ckLanguage: 2, andernfalls Meldung 90; weggelassen = false). Es ändert weder Daten noch API; es entscheidet nur, wie eine spätere
Verschärfung des Zugriffs klassifiziert wird (siehe oben). Das Setzen oder Entfernen ist MINOR. Markieren Sie das Attribut in einem früheren
Release als die Zugriffsverschärfung oder im selben Release – die Ausnahme gilt nur, wenn das Attribut in beiden Versionen
sicherheitsrelevant ist.
attributes:
- id: PasswordHash
valueType: String
securitySensitive: true