Fixes #37665 - Context-based frontend permission management #10338
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Redmine issue
Community-forum post this implementation is based on
This PR implements a faster way to check user permissions in Foreman core/plugin frontend code. This speed advantage is gained by leveraging the ForemanContext to store the current user's permissions.
Changes made:
ForemanContext
Added permissions field to context. Contains the current user's permissions - Implmenented as a set of permission names.
Permitted
A component that abstracts the conditional rendering scheme of "Render if <permission> is granted"
usePermission/s
Two custom hooks that allow checking whether a is granted a single or multiple permissions
useForemanPermissions
Context hook that provides a reference to the actual permissions set
useRefreshedContext
A custom hook that refreshes the ForemanContext by requesting the up-to-date application state from the backend
ContextController
atapi/v2/context
Controller that interfaces with the application context on the backend
view_context
permissionPerformance
Five samples taken on a decently fast system using FireFox developer edition with cache disabled and no background tasks running.
Different approaches tested using this component.
API-based approach
Permitted
componentPermitted
component in conjunction withuseRefreshedContext()
Discussion
Although it may seem like the difference between
Permitted
andPermitted
+useRefreshedContext
is negligible, this is not the case, as the actual time to mount the component differs by at least the API request duration when usinguseRefreshedContext
. Unfortunately, I don't have any values there because the React profiler is ...not great.Even with a full page-reload, a speed difference of ~7% may be observed. This will be amplified on weaker / busier systems than mine. Furthermore, navigation between frontend-rendered pages will benefit a lot more, as comparatively little data needs to be requested from the backend.
Unfortunately, I nuked my dev-environment shortly after collecting the data above, so I will have to amend the results for client-rendered -> client-rendered later :)
The case for permission constants in JS
Although not strictly in the scope of this issue, I believe that this presents a good opportunity to introduce permission constants for JS, similar to API status constants. These constants mainly offer the following benefits:
To aid developers in creating these constants, I created a rake task,
export_permissions
, that automatically generates these JS constants from the permission seed file.TODO
I tried a lot of things, but I didn't manage to mock the
app_metadata
function, so the test is useless at the momentSince this hook interfaces pretty deeply with the context and the API, testing it is rather difficult. I got pretty far with
renderHook
and mocks but am running into issues with the setForemanContext function.