enhancement
major
minor
defect
critical
major
minor
major
#30025
React UI: Hide commands and units for which the logged-in user does not have the required role on any object
Context
Since #29856, ModelAccessPolicy (isOffered) determines whether a command or a React UI element is hidden or merely disabled. Currently, only items that no user is ever permitted to access are hidden: no role is granted the operation on the type (or a specialization, or on the types of the access parent). The logged-in user plays no role in this decision.
As a result, a user with limited permissions sees all commands that any other user could execute—disabled, with an explanation provided as a tooltip. A user who cannot execute a function on a single object should not see it at all.
Desired Behavior
A command or unit is hidden if the logged-in user cannot perform the operation on any object of that type: The user does not hold any of the roles to which the operation is granted for that type on any object (directly, via groups, or via role rules). If the user holds such a role on at least one object, the command remains visible and is either enabled on the specific object as before or disabled with an explanation.
The check must never hide anything that the user is actually permitted to do on an object (overestimating permissions is allowed; underestimating them is not).
Solution
- AccessManager.hasRoleOnAnyObject(Person, Collection<? extends BoundRole>): whether a person holds one of the roles on any object. No person or no roles → false, administrator → true; the AccessManager itself conservatively returns true. The result is never more restrictive than hasRole on a specific object.
- StorageAccessManager answers the question with an existence query on the role table (SecurityStorage.hasRoleOnAnyObject using the person’s groups)—the same table that hasRole reads (object and security parents); an inherited role is listed there under the ancestor. During a table rebuild or in the event of a read error, the response is ` true`.
- The responses are cached per interaction, person, and set of roles and discarded with each new revision of the knowledge base; caching does not occur if there are pending changes in the current transaction. For this purpose, the mechanism of `RoleAssignmentMemo` has been extracted as a general class, `InteractionRevisionMemo`, which both use. A newly acquired role or group membership thus takes effect within the same interaction.
- ModelAccessPolicy: All decisions regarding “Operation is not assigned to any role” (isOffered, hiding instead of disabling on an object, attribute, attribute of a type, draft attribute, and upon creation) now consider only the assigned roles that the logged-in user holds on any object. Access parents as in #30024.
- React Demo (tl-demo-react): An account that is a member of P1 but is not a project administrator anywhere no longer sees the “Delete” option for P1 (hidden instead of disabled) and sees the “Shared” option for a milestone as read-only; an account that administers another project sees both as disabled. The description of the scenario in demoReactConf.config.xml has been updated accordingly.
A command that operates on its input channel (e.g. , <delete-object/>) has no type if there is no object in the channel and remains disabled rather than hidden when the channel is empty; this addresses #30038 (optional value type of a channel).