TopLogic - the automated application engine
  • Releases
  • Dokumentation
  • Github
  • Discord
  1. Home
  2. Releases
  3. TL_8.0.0-alpha12
  4. #29856

8.0.0-alpha12
TopLogic Release

2026-10-30

enhancement

major
#29658
TL Views: Schwebende Hauptaktion (Floating Action Button) und Aktionsfächer (Speed Dial)
#29704
tl-archetype-app: Neue Anwendung standardmäßig mit React-Oberfläche erzeugen, klassische Oberfläche per Option
#29729
TL Views: Ein Flussdiagramm (flow-diagram) lässt sich nicht für Mehrfachauswahl konfigurieren
#29781
TL Views: Aktionsketten mit benannten Argumenten – Auslöser liefern mehrere Werte, Skripte verwenden sie als implizite Variablen, Aktionen mit Ergebnis führen Folgeaktionen in <with-result> aus
#29856
React UI: consolidate the access check definitions of views, commands and TL-Script functions
#29860
Konfigurationsformulare: Eingabefelder werden über fachliche Annotationen (@MultiLine, @Code, @Html, @Inline, …) gewählt, in klassischer und React-Oberfläche gleich; @ControlProvider und @ConfigControl entfallen
#29865
Configuration layering: removing a list entry whose key the underlying layers do not (or no longer) define fails the application start
#29875
Design System wave 5a: color input, icon select and slider read the design system package
#29877
Developer docs: an article on the persistency configuration of an application (single branch vs. multiple branches, type systems, what changes the schema)
#29880
XIO: Import of an evolving external format: report unknown elements, reject a second composite container with a location, match several values in one <switch> case
#29887
TL Views: Einfügevorschläge für ein Textfeld - per TL-Script berechnete Vorschläge an der Schreibmarke
#29888
view-layer forms: normalize an entered value before it is stored
#29892
view-layer forms: actions that enter and leave edit mode (<enter-edit-mode/>, <leave-edit-mode/>)
#29893
Model upgrade log does not mention a reference whose target type changed
#29896
Typed configuration: config:ref addresses an entry of a keyed list by a globally unique key without naming the path to it; a view overlay addresses every element by its id
#29904
Design System wave 5b: media controls read the design system package
#29905
Switch tl-engine to Java 21
#29906
Design System wave 5c: calendar, kanban, dashboard and value list read the design system package
#29914
Design System wave 5d: foundation pieces and foreign stylesheets read the design system package; tlReactControls.css is removed
#29917
Rollenprofil-Import: implizite Leserechte der umgebenden Sicherheits-Layouts ergänzen
#29941
TL Views: Auswahlfeld, das zusätzlich einen frei eingegebenen Wert annimmt ("auswählen oder neu eingeben")
#29942
TL Views: Erwähnungen im HTML-Editor - Verweise auf Objekte (z.B. Projektmitglieder, Tickets) per @/# einfügen, die zu Links werden
#29944
Konfigurierbare Start-Haken, die nach dem Start aller Services ausgeführt werden
#29947
Steueranwendung: Workspace-Management – leere TopLogic-Anwendung in einem Docker-Container anlegen, starten, stoppen und aufräumen
#29948
Credentials für externe Dienste ohne Geheimnisse in der Konfiguration: Secret-Quellen, Token-basierte Authentifizierung (Keycloak/OIDC, Workload Identity, Azure) und API-Gateway
#29953
Steueranwendung: Agenten-Harness im Workspace – Auftrag an Claude Code, Live-Transkript, Übernahme durch einen Menschen, LLM-Zugang über einen Proxy
#29954
KI: Modellkatalog aus der Dienstkonfiguration des LLM-Anbieter-Dienstes herausnehmen und in einer eigenen Ansicht zeigen
#29955
React-UI: Dienstkonfiguration anzeigen, bearbeiten und speichern (mit Kennzeichnung "Neustart erforderlich")
#29956
Anwendungen und Fragment-Module ohne TopLogic-Parent-POM bauen: BOM, Maven-Packaging tl-app/tl-fragment und Feature-Starter
#29957
React-Oberfläche: Browser-Tab-Titel folgt der angezeigten Ansicht
#29963
Development-Menü im React-UI nur für angemeldete Benutzer anzeigen
#29965
tl-ai-core: LLM-Anbieter ohne Anwendungskonfiguration über Umgebungsvariablen aktivieren
#29968
Filterleiste: Presets mit Zähler und eigener CSS-Klasse
#29973
XML import: <set-translation> stores an empty text and needs a bound language
#29974
XML import: <unless-known> cannot import the attributes of a known element without its structure
#29975
XML import: <resolve-existing> loads only the partition of existing objects an ID belongs to; linking checks membership without reading the whole reference
#29976
View layer: <tree-table> offers no <export> (Excel export) although the export writes trees as outlines
#29977
View layer: <tree-table> has no node display (icon, label, CSS class per cell) and no row-class
#29981
View layer: a table column cannot be declared unsortable, so the groups it forms cannot keep the order of their rows
#29989
Javadoc-Verweise und ungenutzte Imports im Maven-Build prüfen, Eclipse meldet fehlerhaftes Javadoc nur noch als Warnung
#30012
Translator: erkannte Ausgangssprache bei automatischer Spracherkennung zurückgeben
#30017
Build: Git-Merge-Treiber für die generierten Ressourcendateien, damit parallele Pull-Requests nicht bei jedem neuen Schlüssel in Konflikt geraten
#30051
Initiale Gruppen "user" und "superuser" in der Basis anlegen
minor
#29852
TL Views: Mehrwertige Binärwerte als Datei-Chips (oder Werteliste) mit Mehrfach-Upload, Pflichtfeld, Validierung und accept-Filter; <file-list> entfällt
#29872
TL Views: the min/max/step of a number input (slider) are fixed in the configuration and cannot come from a channel or expression
#29873
TL Views: a time input (ReactDatePickerControl.Kind.TIME) cannot enter or show seconds
#29874
TL Views: a select input always sorts its options by label - no way to keep the order of the option list
#29898
Development start (tl-ide-jetty) provides no Jakarta WebSocket container
#29952
React-View-Schicht: Fenster folgen Commits anderer Threads ohne eigene Konfiguration – ersetzt CommitSource und den Refresh-Teil von AgentViewRefreshService

defect

critical
#29926
Derived attributes computed by a <query> storage evaluate to null for a user without Discover right on the object (regression of #29782 in 8.0.0-alpha11)
#29994
Build: master verletzt den JavaDoc- und Import-Check von #29989 (tl-chat, tl-ai-core, tl-factory)
major
#29742
Konfigurierte Defaults (<defaults>) wirken nicht innerhalb der Anwendungskonfiguration
#29798
Derived reference reads as empty in the commit check (no user): result filter of AttributeByExpression stays on
#29869
A React view window that lost its SSE connection keeps every later KB revision in memory until its session ends
#29876
KnowledgeBase on H2: deleting objects scans the association tables (the reference indexes start with BRANCH, and H2 seeks an IN list only on the first index column)
#29886
view-layer <confirm>: line breaks of the message are collapsed
#29889
Adding one element to a set reference reads the whole set (quadratic set building)
#29895
deploy/local applies only to the module in the working directory: a module of a multi-module build cannot be started from the reactor root
#29908
React-Tabelle zeigt nach dem Neuladen der Seite in den oberen Zeilen keinen Inhalt
#29916
Rollenprofile: Leserecht eines Dialogs wird auf die öffnende Komponente übertragen
#29918
Design System: a panel's toolbar sticks to the title instead of standing at the end of the title bar
#29920
TL Views: Die Zeilenauswahl in einer React-Tabelle braucht bis zu einer Sekunde, weil jede Auswahl alle Zellen neu überträgt und bei einer Tabelle mit Eingabekanal alle Zellen neu erzeugt
#29922
Composition-Tabelle in einem Formular bekommt keine Höhe, ein Dialog kann nicht mehr bis zur Tabelle scrollen
#29923
React-Oberfläche: Inhalt eines Navigationseintrags mit route="none" erzeugt URLs, die sich nicht öffnen lassen
#29925
TestGenerateJavaIntegration fails with the split local Maven repository of the CI builds
#29931
invert-link-table makes both ends of a link the destination on MySQL
#29932
Gruppen und Adressen sind für Superuser nicht lesbar: tl.accounts:Group und tl.accounts:Address haben keine Rollenquelle
#29933
React-UI: Der Griff eines Split-Panels "läuft weg" beim Ziehen mit der Maus; tiefe Baumknoten brechen um statt waagerecht zu rollen
#29934
View-Designer: Zur Laufzeit geöffnete Sichten (Rahmen eines tile-stack, navigate-push, open-dialog) fehlen im Entwurfsbaum und lassen sich nicht per "Sicht auswählen" anwählen
#29964
React UI: password login sometimes logs out immediately - a command of the replaced window triggers the stale-UI reload of the old window URL
#29967
React-UI: Spaltenauswahl der Tabelle bietet das Gruppieren nach einer Spalte nicht mehr an
#29970
React-Bridge: Ein Sprung, der seine Zielseite erst erzeugt, malt sie ohne die gesetzte Auswahl
#29971
React-Views: Die Ausführbarkeit eines Befehls übersieht einen Commit eines fremden Threads während des Seitenaufbaus
#29978
View layer: <tree-table> does not reveal a selected object under a collapsed node and misses changes while hidden
#29979
Migration: migrations of an application module without a stored version never run, the auto model upgrade runs instead
#29980
Migration: update-class with a new-name in another module does not move the class's associations
#29983
View layer: a tab strip wider than its container neither scrolls nor wraps nor collapses into a menu
#29990
React-UI: Panels und bedingte Inhalte nehmen in Split-Flächen, Tabs und Stacks die falsche Höhe
#29991
View layer: split-panel ignores unit="px" of a pane and treats its size as a weight
#29992
React-Modelleditor: Annotation "Eingabekontrolle" bietet keine Umsetzung zur Auswahl an
#29999
React-UI: Tabelle zeigt nicht mehr an, welche Spalten gefiltert sind
#30001
Entwicklungsmodus: neu geschriebene WEB-INF-Dateien sind über den FileManager erst verzögert sichtbar (Theme-Import zeigt keine Variablen, ThemeEditor_Ticket-24158_Import-export scheitert sporadisch)
#30002
Service-Editor: Konfiguration aller Services fehlerfrei anzeigen
#30005
React-Oberfläche: Ein Einstiegspunkt verliert beim Laden seine View-Datei aus der URL, F5 zeigt die Default-View (Regression #29957)
#30013
TL Views: Auswahlfelder bieten später angelegte Objekte ihres Typs erst nach neuer Anmeldung an
#30014
React-Formulare: Sichtbarkeit beim Anlegen (create-visibility) eines Attributs wird nicht berücksichtigt
#30015
React-Oberfläche: Anlegen in einem Container wird nicht geprüft, solange der Container-Kanal leer ist
#30016
Aufzählungswerte (Klassifikatoren) sind für Benutzer ohne Administratorrechte nicht entdeckbar: Ein Aufzählungs-Attribut kann nicht gesetzt werden
#30019
TABLE.debounce() verwendet globalen Singleton-Timer — Slice-Nachladen schlägt bei mehreren Tabellen fehl
#30024
React-Oberfläche: NullPointerException in ModelAccessPolicy.isGrantable, wenn ein Zugriffs-Parent seine Parent-Typen nicht statisch kennt
#30026
Instabiler Test: TestQueueReporter#testGetReport schlägt fehl, wenn zwei Builds auf demselben Knoten gleichzeitig laufen oder der Rechner ausgelastet ist
#30030
Mail-Server-Daemon: neue Mails anhand der IMAP-UID statt des Gelesen-Flags erkennen, unbekannte Mails standardmäßig im Postfach belassen
#30055
TL Views: a table with active-preset and search-term channels renders every filtered row instead of a window; 15k rows load 88 MB and crash the browser tab
minor
#29515
Inkonsistente Drop-Typ Konfiguration für Drop-Operationen möglich
#29868
KBChangeAnalzyer logs "There is a part id ... but no item for update" for every update of a part of a monomorphic inline-set storage
#29883
A boolean attribute without a value reads as false on a persistent object, as null on a transient one
#29894
Commit einer geänderten Annotation loggt ERROR mit Stacktrace, wenn der alte Wert eines abgeleiteten Attributs (z.B. options) nicht mehr instanziierbar ist
#29935
Popup-Fenster (Skript-Rekorder, Dokumentation) lässt sich nach dem Schließen nicht sofort wieder öffnen (Edge/Chromium): Fehler "Cannot read properties of null (reading 'services')"
#29959
Persönliche Einstellungen werden nicht gespeichert, wenn der Benutzer eine verletzte Modell-Bedingung hat (z. B. root ohne Kontaktdaten)
#29960
React-Oberfläche: Browser-Tab zeigt immer "TopLogic View" statt des Anwendungsnamens
#29969
SecurityConfiguration: Crash durch DeletedObjectAccess
#29972
composition-table im Bearbeitungsmodus: die Lösch-Spalte bekommt den freien Platz statt der letzten Datenspalte
#30056
TL Views: NullPointerException in ReactControl.commitUpdate from the session-timeout notice countdown ("Failed to announce the activity of session")

task

major
#29767
Clean up loose design documents (docs/, specs/, module doc folders): valid content into the developer documentation, open work into tickets
#29924
Integrate the tl-ai repository into tl-engine: its modules become engine modules
#30037
Name the base type of polymorphic configuration properties as wildcard bound (PolymorphicConfiguration<? extends X>) and check the rule in the build
minor
#29921
Renovate hinterlässt in npm-Lock-Dateien verwaiste Einträge, die der nächste Maven-Build wieder entfernt

update

critical
#29949
Update dependency dompurify to ^3.4.17 [SECURITY]
#29950
Update dependency vite to ^6.4.4 [SECURITY]
#29984
Update dependency serialize-javascript to ^7.1.3 [SECURITY]
#30008
Update dependency com.google.guava:guava to v33 [SECURITY]
enhancement (User-visible)

major

#29856

React UI: consolidate the access check definitions of views, commands and TL-Script functions

MigrationReactUISecurity

Context

The React UI (.view.xml layer, com.top_logic.layout.view) has several independent ways to state who may see a part of the application or run a command. They grew feature by feature. They rely on different authorization models (role grants on a security scope, model-based access rights, free predicates), are declared in different places, and decide differently between hiding and disabling.

Two developments make a consolidation necessary:

  • #29781 changes the action chains to named arguments: a trigger supplies several named values, and scripts use them as implicit variables. Executability rules currently decide over a single command input, or over channels named in the rule. Access checks have to be expressed over the same named arguments that the actions see.
  • An access check specification for (custom) TL-Script functions and commands is upcoming (#29858). Such a function is a "button an agent can press without a UI": an agent (tl-ai, #26020, tool definitions as in #29855) calls it directly, without a view and without the executability rules of a view command. The same concept, or a closely related one, must define the access rights of a function and of a UI command. Ideally one declaration serves all of these purposes:
    • it decides the state of a button (hidden / disabled with reason / executable);
    • it is enforced when the command or function is executed;
    • it determines which tools are offered to an agent acting for a given user.

The status quo below was the basis for the design; the agreed design is in section Solution.

Status quo: access check specifications

== 1. Visibility of UI units: <access-control scope="…"/> ==

  • Applies to <nav-item> and <group> of the sidebar (SidebarElement, GroupItemElement), tabs of a <tab-bar> and sections of an <accordion> (ContentSection / ContentSectionConfig), and tiles (TileElement). These units share the mixin WithAccessControl / AccessControl, and the check is AccessChecks.isAccessible.
  • Scopes are defined centrally in the SecurityScopeService as a tree of id / label entries. The engine defines administration in tl-layout-view.conf.config.xml. Each scope is materialized as a PersBoundComp. Roles are assigned to it like to a classic component: through the security administration, or through security.xml (AccessConfigurationSetupService runs after the scope service).
  • Model: role grants on the security root (structure level; SecurityScope extends AbstractBoundChecker; per-object roles are not supported).
  • Effect: an inaccessible unit is not built at all. The check runs when the view is built. An undefined scope fails closed. Without roles, only root has access.
  • A securing unit (tab, section, tile, nav item) sets its scope as the SecurityScope of the ViewContext for its content. Command rules inside that content inherit this scope (see 2.2).

2. Command executability: `<executability>` rules (`ViewExecutabilityRule`)

Each view command (ViewCommand.Config#getExecutability) has a list of rules, combined by CombinedViewExecutabilityRule. ViewCommandModel evaluates them for the button state and checks them again when the command is dispatched to the server. A request for a refused command is therefore rejected.

=== 2.1 Model-based rights: <model-access operation="…"/> (ModelAccessRule) ===

  • Model: ModelAccessRights with the grants of the SecurityConfigurationService (#29088, access parent #29760), see the developer article access-configuration.
  • operation is a command group: Read, Write, Create, Delete or a custom business operation (e.g. Approve).
  • The checked object is object="ch" (a channel) or the command input. attribute restricts the check to one attribute, type checks against the type (security root), and container + reference check a Create in a container, including Write on the reference.
  • Hidden vs. disabled: denied="hide|disable", otherwise derived by ModelAccessPolicy. The command is hidden when the refusal does not depend on a concrete object; it is disabled, with the reason as tooltip, when the check on a concrete object fails.
  • For a concrete object, the check also asks the global CommandApprovalService (without component and command ID). Only approval checks that are independent of these take effect.

=== 2.2 Role grants on a scope: <security group="…" [scope="…"] [security-object="ch"]/> (SecurityRule) ===

  • Model: like 1. The command group is checked on the configured scope or the scope inherited from the enclosing unit; without a scope, the check fails closed. The security object is the value of the channel if it is a BoundObject, otherwise the security root.
  • Always hides. Currently not used in any .view.xml of the engine. The JavaDoc of FormElement's edit-executability names it as the way to guard editing.
2.3 Rules that the actions bring along (`ViewAction#getIntrinsicRule()`)
  • <create-transient> / <persist-transient>: Create rule (as 2.1 with type / container / reference). <delete-object>: Delete on the command input plus <delete-veto-disabled/> (TLObject#tDeleteVeto()).
  • GenericViewCommand collects the rules of its actions (also nested in <with-transaction>). Rules are always evaluated on the command input, not on the value the chain passes to the action.
2.4 Session state
  • <authenticated-only/> and <anonymous-only/> (AuthenticatedOnly, AnonymousOnly): login state, hide.
  • <inspect-enabled/> (InspectEnabled): a deployment switch (UI inspection), explicitly not a permission.
2.5 Free predicates
  • <visible-if expr="…"/> and <disabled-if expr="…"/>: a TL-Script predicate over the command input. disabled-if returns the reason. Applications use them for authorization too (e.g. currentUser() comparisons), with no connection to any rights model. The script is evaluated with the user's rights.
  • <null-input-disabled/> / <null-input-hidden/>, <form-valid/>, <config-form-valid/>, wizard rules: no access checks, but they share the same mechanism and the same list.

3. Further places with their own rule lists or checks

  • Form editing: <form edit-executability> (FormElement) guards entering edit mode with ViewExecutabilityRule`s. Without a rule, every user who sees the form may edit it. There is no default Write check on the displayed object. Write rights are enforced only when saving (TL-Script `Update). Creating rows/parts checks ModelAccessRights.isAllowedCreate directly in Java (BufferSave, RowSetEditSession). Field editability follows only the model annotations (DisplayAnnotations.isEditable), not the rights of the user.
  • Drag and drop: <drop> has a target-executability list (rules over the target item) and refuse-if (predicate, see #29781). Drag sources (TableDragConfig, TreeDragConfig, KanbanDragConfig, DragSourceBinding) have their own rules.
  • Displayed data: no check in the view layer. Reading is protected by the TL-Script security of the scripts that compute channels (all() returns only readable objects, #29453; objectResolve resolves only readable objects, #29639; result filter filterSecurity).
  • User context: the user belongs to the window (sub-session), not to the HTTP session. Window-independent content has no defined user.

4. TL-Script functions

  • Security flag: an expression evaluates with or without the user's rights (WithSecurityCheck, UpdateSecurityVisitor, QueryExecutor.disableSecurity()). Derived attributes, access rules and BPE run without security.
  • @UsesSecurity (#29639): a TLScriptFunctions method receives the security flag as a parameter and checks the rights itself (e.g. objectResolve).
  • @AdminOnly (#29603) on a method or class of TLScriptFunctions: EvalContext.checkAdmin. In an interactive context (script console, search) the call fails for a non-administrator. A script from the application configuration may call the function for every user. GetAppConfig (appConfig) uses the same check. This is a yes/no flag, with no command group, no object and no roles.
  • Configured TL-Script functions (ConfiguredTLScriptFunctions): with or without the security of the caller. Since #29858 a privileged-script declares a command group and a security object computed from its arguments; the grant is a model grant on the type of the security object. A security object that is a collection denies the call.
  • Side effects: @SideEffectFree / GenericMethod.isSideEffectFree(), HasSideEffects. A read-only execution scope for agent scripts is proposed in #29855. This is not an authorization, but is related.

5. Classic UI (for comparison and reuse)

  • CommandHandler with a BoundCommandGroup (group) and ExecutabilityRule`s, checked on the component's security object via `BoundChecker / PersBoundComp (role profiles per component).
  • CommandApprovalService: global approval checks per type, component and command.
  • The React UI reuses BoundCommandGroup, BoundChecker, PersBoundComp and CommandApprovalService (1., 2.1, 2.2). It has no command-specific security objects; the scope takes their place.

Observations

  • Three authorization models exist side by side:
    • role grants on a scope (1., 2.2);
    • model-based access rights (2.1, 2.3, 3., 4.);
    • free predicates (2.5). An application author must know which one applies where. For the same operation (e.g. editing an object) there are several possible declarations, and none of them is the default.
  • Hiding vs. disabling is decided per mechanism: omitted at build time (1.), always hidden (2.2), derived or configurable (2.1).
  • Rules decide over the single command input or over channels named in the rule. With #29781 they must refer to named arguments, like the scripts of the chain.
  • The scope-based check (1., 2.2) works only on the security root. Per-object role grants on scopes are not supported.

Solution

Principle: one grant table, based on the model

The grants of the SecurityConfigurationService are the only grants: (type | attribute | singleton, role, operation). Views and commands get no grants of their own, exactly like privileged scripts (#29858). Every user of an operation declares only which operation it performs on which object:

User Declares Checked on
Model access (TL-Script, forms) implicit: Read / Write of an attribute, Create, Delete, Discover the accessed object
privileged-script (#29858) command-group + security-object the computed object(s)
View unit (display) operation + object over the inputs of the unit the object(s) of the unit
Command (button) <access> checks: <operation> (operation + optional object over the named arguments) and <create> (type + optional context and composition) the computed object(s), by default the object of the enclosing unit

The user must hold a role granted the operation on the type of the checked object (role rules, role parents, direct assignments; AccessManager.hasRole). Access parents apply uniformly: a custom operation on a type delegating to an access parent is checked as the same operation on the parent (Create and Delete as Write, as before). The decision is ModelAccessRights.isAllowed(person, object, operation) in every case.

Checked objects. The same rule applies to the object of a view unit or command and to the security-object of a privileged script; it depends only on the value the expression yields, not on where it comes from:

  • a single object: the operation is checked on it;
  • a collection: the operation must be allowed on every element;
  • null or an empty collection: no object (a command is disabled, a unit is shown empty, a privileged script is denied).

A command acting on the selection of a table therefore names it explicitly in its <operation> check, e.g. object="$selection" or object="$selection.map(m -> $m.container())". Without object, the check is made on the object of the enclosing unit, independent of any selection.

Operations: global names, declared per type

  • An operation is a command group (BoundCommandGroup) with a global name. Show on projects and Show on devices is the same operation; the grants on the types Project and Device decide separately.
  • A type declares which operations apply to its objects: <operation name="…"/> in the <operations> list of a <class> or <module> entry of <security-config> (a list of its own next to the grants), inherited by the specializations. The built-in operations Read, Write, Create, Delete, Discover and Export always apply and cannot be declared. The singleton entries declare operations the same way.
  • The operation is a command group registered in the CommandGroupRegistry; an unknown command group, an implied-by naming an operation that does not apply to the type, and a cycle of implied-by are configuration errors.
  • implied-by="<operation>": whoever holds the given operation on an object also holds the declared one. This generalizes "every right implies Discover". A simple CRUD application grants nothing beyond the model rights (Show implied by Read, Edit by Write); dropping implied-by later and granting the operation explicitly changes no view.
  • A grant of an operation that does not apply to the type is a configuration error.
  • The operation needs labels commandGroup.<Name> / commandGroup.<Name>.tooltip in a resource bundle, as for privileged scripts.

{{{#!xml <security-config>

<class name="tl.demo.projectManagement:ProjectScope">
<operations>
<operation name="Show" implied-by="Read"/>
<operation name="ShowPlanning"/>
<operation name="Approve"/>
</operations>
<grant operation="Read" roles="demo.react.ProjectMember, demo.react.ProjectAdmin"/>
<grant operation="ShowPlanning" roles="demo.react.ProjectAdmin"/>
<grant operation="Approve" roles="demo.react.ProjectMember"/>
</class>
<class name="my.inventory:Device">
<operations>
<operation name="Show" implied-by="Read"/>
</operations>
</class>
<-- Units and commands without an object are checked on the security root. -->
<singleton name="SecurityStructure#ROOT">
<operations>
<operation name="Administration"/>
</operations>
<grant operation="Administration" roles="Administrator"/>
</singleton>

</security-config> }}}

Views

  • The units carrying <access-control> today (nav item, sidebar group, tab, accordion section, tile) declare operation, inputs, object and type instead of scope:
    • operation: the operation checked for displaying the unit.
    • inputs / object: the input channels of the unit (named as the inputs of a command, #29781) and a TL-Script expression over them, yielding the checked object(s) as described above. The expression is evaluated without the rights of the user, so that an object the user may not see leads to a denied unit, not to an empty one. Without object, the unit is checked on the security root.
    • type: the static type of the object, mandatory with object. When the view is loaded, the operation of the unit and the operations of the commands inside it (checked on the same object) must apply to this type; otherwise it is a configuration error. At runtime an object that is no instance of type is denied (and logged).
  • The unit publishes its checked object and type to its content (SecuredObject in the ViewContext), the default for the checks of the commands inside it. The check of a sidebar <group> applies to the content of each of its items.
  • The display decision is reactive: it is evaluated again when an input of the unit changes. denied="hide|placeholder" decides how a unit denied on a concrete object is shown: hide (default) does not draw the unit at all (a tile leaves no gap, a tab, accordion section or sidebar entry is skipped; nothing is rebuilt), placeholder shows a "no access" placeholder instead of its content. A hidden selected tab passes the selection to the nearest visible tab; an open sidebar entry that becomes hidden keeps its content open and shows the placeholder. The commands inside a denied unit are refused on dispatch.
  • Hide / disable policy (as ModelAccessPolicy, for units and commands):
    • no role can hold the operation on the type at all: hidden;
    • no object (null, empty collection, e.g. an empty selection): the unit is shown empty, its commands are disabled;
    • the check fails on a concrete object: a unit is hidden or shows the placeholder (denied), a command is disabled with the reason as tooltip.

Commands

  • A command declares its access checks in an <access> list; all of them must pass. This replaces the executability rules <model-access> (ModelAccessRule) and <security> (SecurityRule):
    • <operation name="…" object="…" type="…"/>: the operation on the checked object(s); object is an expression over the named arguments of the command (evaluated without the rights of the user), type is mandatory with object. Without object, the object of the enclosing unit is checked, with its type, or the security root when the command is in no unit or in one checked on the root.
    • <create type="…" in="…" reference="…"/>: creating an object of type. Creation is no operation on an existing object: it is decided by the Create grant of type with the roles the user holds on the context (ModelAccessRights.isAllowedCreate). in is an expression over the named arguments yielding the context (a collection: every element must allow it), without in the object of the enclosing unit is the context (or the security root). With reference (a composition of the context), Write on that reference is checked as well, and type is optional: it defaults to the type of the reference. An abstract type passes if at least one of its concrete specializations may be created in the context (the user chooses the concrete type, e.g. from options filtered with canCreate(context, reference, type); the created object is checked by the model rights). The same check is the intrinsic rule of <create-transient> / <persist-transient>.
  • Display: if one check can never pass (no role can hold the operation or create the type), the command is hidden; otherwise the first failing check disables it with its reason as tooltip. The checks are evaluated for the button and again when the command is dispatched; the global CommandApprovalService is asked for every checked object.
  • Actions bring their own checks (intrinsic rules), so a command using them needs no <access>: <create-transient>, <persist-transient>: creation in the container; <delete-object>: Delete plus delete veto on every element of the input, disabled without an input; <enter-edit-mode/>: the edit permission of the form. An <object-list> declares what linking and removing an element requires in <link-access> / <remove-access> (the same checks as <access>, over the list's inputs and the element); <link-element/> and <remove-element/> apply them, and the new-element template is only shown where linking is allowed.
  • Form editing: entering the edit mode of a <form> checks Write on the form object by default (edit-operation, configurable). A draft stays editable.
  • An operation of a view or command never authorizes a data change. A change is checked by the model rights when it is made, or authorized as a whole by a privileged script. A button and the privileged script it calls may name the same operation (e.g. Approve): one grant controls both, and the agent mandate (#29857) checks against the same table.

{{{#!xml <-- Sidebar entry: visible for users holding ShowPlanning on the selected project. --> <nav-item ...>

<access-control operation="ShowPlanning" inputs="project" object="$project"
type="tl.demo.projectManagement:ProjectScope"/>
...

</nav-item>

<-- Administration, checked on the security root. --> <nav-item ...>

<access-control operation="Administration"/>

</nav-item>

<-- Command checked on the projects of all selected milestones; calls the privileged script with the same operation. --> <generic-command inputs="selection as milestones">

<access>
<operation name="Approve" object="$milestones.map(m -> $m.container())"
type="tl.demo.projectManagement:ProjectScope"/>
</access>
<execute-script function="approveMilestones($milestones)"/>

</generic-command>

<-- Reply to a ticket: creates a comment in the ticket's comments (the created type is the type of the reference). --> <generic-command inputs="ticket">

<access>
<create in="$ticket" reference="demo.tickets:Ticket#comments"/>
</access>
...

</generic-command> }}}

{{{#!xml <privileged-script name="approveMilestone" command-group="Approve" security-object="$milestone.container()">

<parameters><parameter name="milestone"/></parameters>
<implementation>...</implementation>

</privileged-script> }}}

Privileged scripts (change to #29858)

Privileged scripts follow the same rule for the checked objects as views and commands:

  • security-object may yield a collection. The call is allowed if the user holds the command group on every element. Each element is treated as a single security object is today (P3, step 2 of access-privileged-scripts.md): an edit overlay of a form is checked as the object it edits; a create overlay, a transient object, an object created in the current transaction, or a value that is no TLObject denies the whole call.
  • null and an empty collection deny the call.
  • canCall reports the same decision.
  • The developer article access-privileged-scripts.md (P3, P5, Testing) and the tests (TestPrivilegedScript) are updated.

{{{#!xml <privileged-script name="approveMilestones" command-group="Approve"

security-object="$milestones.map(m -> $m.container())">
<parameters><parameter name="milestones"/></parameters>
<implementation>...</implementation>

</privileged-script> }}}

Administration of grants

  • The grants are edited per type in the administration (Access control → Grants): select a type, edit a matrix:
    • rows: the roles a user can hold on objects of the type (role rules, role parents, direct assignments; the analysis of the SecurityCoverageAnalysis, made public API), then the same roles per attribute for Read / Write of single attributes; roles granted an operation but never held on the type are shown as ineffective rows, so that the grant can be removed;
    • columns: the operations applying to the type; the column tooltip lists their users (views, commands, privileged scripts) from an index of the *.view.xml files and the configured TL-Script functions; a cell held through an implying operation (or, for Discover, through attribute grants) is shown as derived and read-only.
  • Only options with an effect are offered: roles that can be held on the type, operations that apply to it. A type delegating to an access parent has no grants of its own; its access parent is shown instead.
  • A click is written to the autoconf file of the SecurityConfigurationService at once (no form) and takes effect after "Apply configuration"; a cell differing from the definition in effect is marked as pending. Setting a cell back to what the underlying configuration grants removes the stored rule.
  • The editor is a plain <table> with dynamic columns; two general extensions of the view layer make it possible: <table edit-in-place="true"> (columns with an update script are edited directly in the table, each edit in its own transaction) and <switch rebuild="true"> (rebuilds its content for every new input value).
  • The security coverage check additionally reports operations declared on a type without any grant (and not implied), grants of application-defined operations that no view, command, form or privileged script checks on the type (directly or through an operation it implies), operations used by a view, command or privileged script that do not apply to the checked type, and unknown operations. Findings of places that do not name a checked type are listed in a group of their own.

Removed

  • SecurityScopeService, SecurityScope, the scope attribute of <access-control>, the role profiles of scopes (PersBoundComp, security.xml entries for scopes).
  • The executability rules <security> (SecurityRule) and <model-access> (ModelAccessRule, DeniedDisplay); ModelAccessPolicy is reused for the hide / disable decision.
  • The scope permission matrix of the administration (SecurityMatrixElement, permissions.view.xml); the grants editor per type takes its place.
  • The engine scope administration becomes the operation Administration on the security root.

Verification

  • JUnit tests for the operation declarations (applies to type, implied-by, access parent delegation, configuration errors), the view / command checks (single object, collection, empty collection), and privileged scripts with a collection as security object (all allowed, one denied, empty, one transient element).
  • tl-demo-react: the "Planning" (denied="placeholder") and "Approvals" (hidden) tiles of the project cockpit (operation ShowPlanning, project admins only), the polymorphic "Deliverables" of a project (abstract Deliverable with three kinds, members may create only specifications; the kind chooser offers only creatable kinds), the comment lists with <link-access> / <remove-access>, the "Approve" command of the milestone list and the tree demo (also on a multi-selection of milestones of different projects) as project member, project admin and ticket watcher; the administration entry as root and as a normal user; the grant editor showing only effective roles and operations.

Migration

  • <access-control scope="administration"/> becomes <access-control operation="Administration"/>. An application scope becomes an operation on the type of the object the unit shows (<access-control operation="X" inputs="…" object="…" type="…"/>), or on the security root; it is declared in <security-config>, and the role profiles assigned to the scope become <grant> entries of that operation. The <scopes> configuration of the SecurityScopeService and the scope entries in security.xml are removed.
  • In the executability of a command, <model-access operation="X" object="ch"/> becomes <access><operation name="X" object="$ch" type="…"/></access> on the command (the channel added to the command's inputs; without object when the checked object is the one of the enclosing unit). <model-access operation="Create" type="T" [container="ch" reference="r"]/> becomes <access><create type="T" [in="$ch" reference="r"]/></access>. <security group="X" .../> becomes an <operation> check.
  • Custom operations used in grants, views, commands or privileged scripts must be declared on the type: <operations><operation name="X" [implied-by="Y"]/></operations> in its <class>, <module> or <singleton> entry; a grant of an undeclared custom operation is a configuration error at startup.
  • Entering the edit mode of a form requires Write on the form object unless edit-operation says otherwise; an <edit-executability> with <model-access operation="Write"/> can be dropped.
  • <delete-object> on an empty input is disabled; a command running <enter-edit-mode/> is hidden or disabled by the form's edit permission.
  • Access checks of commands running <link-element/> / <remove-element/> are declared once on the <object-list> (<link-access> / <remove-access>).
  • Grants of command groups that only the classic UI checks (against the role profiles of its components) do not belong into <security-config>; the coverage check reports them as unchecked grants.
  • A privileged-script whose security-object yields a collection was always denied; it is now allowed when the user holds the command group on every element.

Related: #29781, #29088, #29453, #29603, #29639, #29760, #29855, #29857, #29858, #26020.

  • Get Started
  • Github
  • Discord
  • Das Unternehmen hinter TopLogic
  • Softwareentwicklung heute
  • Kontakt

© Copyright – Business Operation Systems GmbH

  • top-logic.com
  • Nutzungsbedingungen
  • Impressum
  • Rechtlicher Hinweis
  • Datenschutz
  • DE
  • Login