RBAC Endpoint Mapping - uyuni-project/uyuni GitHub Wiki
Registering endpoints for RBAC
With the introduction of Role-Based Access Control in 5.1 Beta 2, we now have to add mappings for any new/updated web endpoints to the relevant tables in the DB.
This guide explains the steps needed to be taken in order to make sure all the new endpoints are allowed access through the new RBAC filters.
For more information on how RBAC works under the hood, please refer to the Role-Based Access Control (RBAC) RFC document.
Important note
RBAC is effectively bypassed for "Satellite Admin" user. Because of this, please make sure you test your features with a separate "Org Admin" user instead of the default admin/admin
.
How does RBAC work?
Role-Based Access Control is configured via namespaces, which are groups of web endpoints that serve a specific purpose like an action of a feature or rendering of a complex web page.
With the initial version, all the existing endpoints in MLM are already mapped to namespaces.
To browse the existing namespace tree, you can query the access.namespace
table or the access.endpointCatalog
view.
When a user requests an endpoint either in the web UI or the API, a Java servlet filter called the authorization filter checks the DB to determine which namespaces this endpoint belongs to, and checks if the user has access to this namespace.
User access to namespaces is provided via access groups (formerly "roles"). This relation is defined in access.accessGroupNamespace
table. Depending on the desired access level, you might want to add the new namespace to any or all of the available access groups. Keep in mind that the initial data in access.accessGroup
table only includes the predefined access groups, but in a typical setup, there might be extra access groups defined by the administrators.
If you don't add an entry to access.accessGroupNamespace
, your namespace won't be accessible by any user group, but it will be available to administrators who can provide access to this namespace by creating additional access groups and assigning the namespace to them.
Why do I need to add mappings for an endpoint?
As a failsafe mechanism, an RBAC filter automatically rejects any endpoint that is not added to the database with a proper mapping. If you skip this part, your endpoints won't be accessible to MLM users.
How do I register my endpoints?
At the time of the Beta release, we have mapped a total of 1785 endpoints to a namespace tree of 987 individual namespaces. It means that if you are adding or updating an endpoint for an existing page or feature, a namespace that suits the purpose of your endpoint probably already exists. In that case you only need to add one entry for the endpoint, and one additional entry for the relation between the new endpoint and the existing namespace.
If you think the endpoints you're adding deserves granularity of access control, you should map them to a new namespace at any level of the namespace tree.
A new namespace is only accssible to a user group if a corresponding access.accessGroupNamespace
entry is also added.
Below are the most common 5 cases that require changes in RBAC data:
- I have a new page/feature
- I made changes to an existing page/feature
- I haven't changed any endpoint URLs
- I implemented a new API endpoint
- I have a special endpoint that doesn't require authentication
1. I have a new page/feature
You need to add entries to the following DB tables:
-
access.endpoint
: The entry that describes your web endpointclass_method
: Reserved for XMLRPC endpoints, must be an empty stringendpoint
: The URL of the endpoint starting after the/rhn
parthttp_method
: The expected HTTP method of the request (GET
,POST
, etc.)scope
:W
for web UIauth_required
:true
if this is an authenticated endpoint
-
access.namespace
: The entry that describes your namespacenamespace
: The namespace as a dot-separated stringaccess_mode
:R
for read/view,W
for write/modify. Regardless of the HTTP method, if the endpoint does any change in the system, this value should beW
.description
: A short paragraph to describe the purpose of the namespace. RBAC UI will display these descriptions to help users with administration. We also plan a full-text search field on this field to make browsing easier.
-
access.endpointNamespace
: The entry for endpoint to namespace relation -
access.accessGroupNamespace
: Specifies which access groups have access to the namespace
Example
-- New endpoints
INSERT INTO access.endpoint (class_method, endpoint, http_method, scope, auth_required)
VALUES ('', '/manager/systems/details/ansible/playbooks', 'GET', 'W', True);
INSERT INTO access.endpoint (class_method, endpoint, http_method, scope, auth_required)
VALUES ('', '/manager/api/systems/details/ansible/discover-playbooks/:pathId', 'GET', 'W', True);
INSERT INTO access.endpoint (class_method, endpoint, http_method, scope, auth_required)
VALUES ('', '/manager/api/systems/details/ansible/paths/save', 'POST', 'W', True);
-- 'View' namespace
INSERT INTO access.namespace (namespace, access_mode, description)
VALUES ('systems.ansible', 'R', 'Browse ansible playbooks and inventories');
-- 'Modify' namespace
INSERT INTO access.namespace (namespace, access_mode, description)
VALUES ('systems.ansible', 'W', 'Modify, delete ansible paths, schedule playbook executions');
-- Endpoints for 'View' namespace
INSERT INTO access.endpointNamespace (namespace_id, endpoint_id)
SELECT ns.id, ep.id FROM access.namespace ns, access.endpoint ep
WHERE ns.namespace = 'systems.ansible' AND ns.access_mode = 'R'
AND ep.endpoint = '/manager/systems/details/ansible/playbooks' AND ep.http_method = 'GET';
INSERT INTO access.endpointNamespace (namespace_id, endpoint_id)
SELECT ns.id, ep.id FROM access.namespace ns, access.endpoint ep
WHERE ns.namespace = 'systems.ansible' AND ns.access_mode = 'R'
AND ep.endpoint = '/manager/api/systems/details/ansible/discover-playbooks/:pathId' AND ep.http_method = 'GET';
-- Endpoint for 'Modify' namespace
INSERT INTO access.endpointNamespace (namespace_id, endpoint_id)
SELECT ns.id, ep.id FROM access.namespace ns, access.endpoint ep
WHERE ns.namespace = 'systems.ansible' AND ns.access_mode = 'W'
AND ep.endpoint = '/manager/api/systems/details/ansible/paths/save' AND ep.http_method = 'POST';
-- Rule for general access
-- (relates all access groups to the new 'systems.ansible' namespaces)
INSERT INTO access.accessGroupNamespace
SELECT ag.id, ns.id
FROM access.accessGroup ag, access.namespace ns
WHERE ns.namespace = 'systems.ansible';
-- Or for access to a specific group
INSERT INTO access.accessGroupNamespace
SELECT ag.id, ns.id
FROM access.accessGroup ag, access.namespace ns
WHERE ns.namespace = 'systems.ansible'
AND ag.label = 'system_group_admin';
2. I made changes to an existing page/feature
If the new/modified endpoints fit an existing namespace, you don't have to insert into the namespace
table.
If you're changing an endpoint URL, you must write an update to the endpoint
table. If it's a new URL, you must write an insert
.
Finally, for each new endpoint, you must insert the relation to its namespace to the endpointNamespace
table.
3. I haven't changed any endpoint URLs
You should still have a look at the existing endpoint and namespace records and see if they're still accurate (HTTP method, namespace desciption, access mode).
4. I implemented a new API endpoint
While Web UI namespaces are logical groupings of endpoints that are intended to work together, API namespaces map individual API endpoints for top granularity. In other words, every API endpoint has its separate namespace and has a 1-to-1 mapping to its endpoint.
The process is very similar to Case 1, with slight changes in the table values:
-
access.endpoint
: One entry per methodclass_method
: The fully qualified class name of the handler +.
+ the name of the handler methodendpoint
: The JSON over HTTP URL of the API endpointhttp_method
:GET
if annotated with@ReadOnly
,POST
otherwisescope
:A
for APIauth_required
:true
if the method expects a session key
-
access.namespace
: A new entry for each methodnamespace
:api.<api_namespace>.<api_method_in_snake_case>
access_mode
: Most likelyR
if annotated with@ReadOnly
,W
otherwisedescription
: A short version of the method's apidoc description.
-
access.endpointNamespace
: The 1-to-1 relation between the endpoint and the namespace -
access.accessGroupNamespace
: An entry for every access group that's allowed access to the method
5. I have a special endpoint that doesn't require authentication
These endpoints will bypass RBAC, so there's no need to connect them to a namespace, but for RBAC to recognize these endpoints, a record in the endpoint
is still necessary:
access.endpoint
: The entry that describes your web endpointclass_method
: An empty stringendpoint
: The URL of the endpoint starting after the/rhn
parthttp_method
: The expected HTTP method of the request (GET
,POST
, etc.)scope
:W
for web UIauth_required
:false
to bypass RBAC authorization
Example
INSERT INTO access.endpoint (class_method, endpoint, http_method, scope, auth_required)
VALUES ('', '/hub/ping', 'POST', 'W', False)
Debugging
To see what actual endpoints a user has access to, query the access.userAccessTable
view. This is the master view that shows the access information.
com.redhat.rhn.frontend.servlets.AuthorizationFilter
writes debug messages to the rhn_web_ui.log
whenever an access has been granted or denied.
Consider enabling debug level logging for this class.
RBAC responds with a 403 when a request is denied access. Therefore, tomcat access logs can be useful as well.