Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Role-Based Access Control (RBAC) #95

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

Role-Based Access Control (RBAC) #95

wants to merge 5 commits into from

Conversation

cbbayburt
Copy link
Contributor

This RFC proposes Role-Based Access Control (RBAC) model in Uyuni.

See the rendered version.

### Performance

Access control checks query the database on every request.
To mitigate performance impact, a user's access rules can be preloaded into memory and stored in the user's session data during login, enabling faster access control validation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small issue: if the rules that apply to a user change while they are logged in, this will lead to a mismatch, how will we address that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As first step I think we should not cache any information. We can change that if we notice any performance issue. The topical (as most simple) solution for this kind of situation is that the user must logout and login again.
Another option is to check which sessions are active in the database for user affected by the access change an invalidate those authentication sessions.

Copy link
Contributor Author

@cbbayburt cbbayburt Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. But when we decide to implement caching, I think safest and easiest solution would be to simply kick the user out and require a new login whenever their permissions change. This shouldn't happen often anyway.

@Etheryte
Copy link
Member

How do we plan to integrate all of this with the frontend? If a user does not have W on a resource, it doesn't make sense to show them UI components that will just throw an error when they try to save changes. So we would also need to make this information available to the UI in some manner and enforce checks there as well for usability.

@rjmateus
Copy link
Member

How do we plan to integrate all of this with the frontend?

@Etheryte In my opinion that is a good usability improvement but should be considered a second step. With this RFC we want to set the foundations in term of authentication and changing all the pipes to make it more robust and configurable. As next step I think we can improve a lot the UI, and make it more tailored to the end user.

Copy link
Contributor

@aaannz aaannz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, this RBAC implementation will not help with something like proxy admin seeing only systems below that proxy, right?

Visibility will still be based only on organization level, correct?


1. **Authentication:** Identifies the user calling this action.
2. **Authorization:** Determines if the user is allowed to call the endpoints for this action.
3. **Accounting:** Assesses whether the user is permitted to operate on a specific entity.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is my bad English, but are you sure Accounting means this? Everywhere I looked, accounting is about measuring, logging etc.

From the sentence in the RFC I would assume accounting is some action which can block user access.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a concept in user access control called Authentication, Authorization, and Accounting (AAA)
Here some links talking about it:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to rephrase this to be more clear

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, this RBAC implementation will not help with something like proxy admin seeing only systems below that proxy, right?

Visibility will still be based only on organization level, correct?

This actually refers to "Accounting". No, at this point we won't control access at this level.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well not according to those links @rjmateus provided. Accounting is according to those links:

Accounting, the final process in the framework, is all about measuring what's happening within the network. As part of the protocol, it will collect and log data on user sessions, such as length of time, type of session, and resource usage. The value here is that it offers a clear audit trail for compliance and business purposes.
Accounting

Accounting records the used service type, start time, and data traffic to collect and record the network resource usage of the user for implementing time- or traffic-based accounting and network monitoring.

So to me this sound like Accounting it measuring and logging and no way it can decide user has access to this or that. It is simple passive step of logging.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the "visibility" of resource should be part of Authorization step.

But as we do this RBAC over API calls and not over resources, I guess there is no way to limit user access to individual machines.

User has access to system/listSystems API call and thus can list all systems of given organization (assuming organization visibility will remain).
User has write access to system/deleteSystem and thus can remove all systems of given organization.

Copy link
Member

@rjmateus rjmateus Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And now I'm trying to figure out from where I read that statement :-|
And you are completely right, Accounting is the process of recording all the user interactions.
Currently access control to machines have a different process on SUMA, and relies on 2 roles (spacewalk admin and organization admin) and also if a user is assigned to a systems group.
This is something we can refactor, but in my opinion should be a different step.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aaannz thank you for pointing it out. Resource access should be one step of the Authorization process. However, This RFC is focusing only at the part ...perform operation O.... This would be already a big step, since the role/access control is spread across all the code (from API definition, to database access factories), and this is something we must clean.
Next step can be find a better way to perform resource access check. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, it seems I mixed up all the terms. I rewrote the part so it's hopefully clearer now.

Every access point in Uyuni is mapped as an **endpoint** in the database and grouped into **namespaces**.
Access control is enforced by defining rules for these namespaces, either per **user** or **access group**.

Below is a simplified ER diagram illustrating the proposed structure for access information in the database:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we are not discussing implementation details here, but seeing the ER diagram I can't help myself.

Please NO NUMERIC IDs, I can't stress this enough.

Use BIGINT with identity generated always. Exception being already existing IDs.
And btw. VARCHAR is the same as TEXT in postgres, so use TEXT everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted. Thanks for the tip.

@cbbayburt
Copy link
Contributor Author

cbbayburt commented Nov 13, 2024

How do we plan to integrate all of this with the frontend? If a user does not have W on a resource, it doesn't make sense to show them UI components that will just throw an error when they try to save changes. So we would also need to make this information available to the UI in some manner and enforce checks there as well for usability.

@Etheryte the new mechanism will provide means to check information regarding access rules to use on any page to show/hide/disable sections. You'll be able to check if the user has access to something and render that part conditionally.

The hardest part is to do this on all the existing pages. We have to keep this out of scope to make it feasible for 5.1. Until then, when the user navigates to some disallowed part, we'll simply return a branded 403 page.

Once the RBAC is done, we'll have to go page-by-page to add conditions according to the user's access rules.

With the initial implementation though, we'll at least hide the disallowed entries from the left menu bar.

If we can find the time, we can also do this for important parts, like hiding disallowed system details tabs, etc.

Copy link
Member

@rjmateus rjmateus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks really good Can. Just some small clarification, otherwise looks great to me.


## Overview

This solution stores all available network endpoints in the database and determines which endpoints each user can access, with the exception of the **Uyuni Administrator** role, which bypasses access control.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This solution stores all available network endpoints in the database and determines which endpoints each user can access, with the exception of the **Uyuni Administrator** role, which bypasses access control.
This solution stores all available system endpoints in the database and determines which endpoints each user can access, except the **Uyuni Administrator** role, which bypasses access control.

- **`endpoint`**: The accessible URI of the endpoint.
- **`class_method`**: The Java class that handles incoming requests to the endpoint, such as a controller class for web endpoints or an API handler for API endpoints.
- **`http_method`**: The HTTP method (e.g., GET, POST, PUT, DELETE) accepted by the endpoint. If multiple methods are supported, each should be defined as a separate endpoint since they typically serve different purposes.
- **`scope`**: Indicates whether the endpoint is accessible through the web UI (including internal API calls) or the public API.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add 2 more fields to the table, one expressing if the endpoint needs authentication and another if authorization is required.
In the PoC this was implemented as a static list on Java side, but I think we could move this information to the database.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a auth_required column to the RFC to exclude the URLs outside of authentication space (like login, about, API docs, etc.).

I'm not 100% sure about the second flag. It may or may not be needed, depending on the details of the implementation. So I'll leave it out for now.


For example, `clm.project.details` **[R]** allows viewing a project's details, where `clm.project.details` **[W]** allows modifying them.

- **`description`**: A clear description of what a namespace grants access to.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can also add a flag to define if the namespace is tailored to be used from the Web application side of API (XML-RPC side), similar of what we have in the endpoint. I know we can play around with the namespace prefix, but this way would be more explicit.


- **`label`**: The label of the access group.
- **`description`**: The purpose or scope of the access group.
- **`org_id`**: The ID of the organization to which the group belongs. This field will be `null` for predefined groups.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This "vendor" groups cannot be changed by the users.


#### Existing roles

To ensure a smooth transition, existing roles will remain until all JSP/Struts pages are fully removed. The new access control mechanism includes default user access groups replicating the following roles:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this topic. We must create access groups mapping the existing roles, that is a must.
However, some of these roles need to stay around for a longer time, even if we remove struts, since they grant access to data and not just features: "Uyuni Administrator" and "Organization administrator" (not 100% sure about the remaining).
For the ones that don't grant access to data, we could try to remove them. The part that can be tricky is the struts ACL rules, which may stop working properly.

An alternative solution for the roles that don't grant data access, and avoid touching the struts ACL could be assign those roles to all users, since the access control to the features will happen in the next mechanism.

We also must develop a automatic migration mechanism from the old roles to the new user access groups.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by data access?

An alternative solution for the roles that don't grant data access, and avoid touching the struts ACL could be assign those roles to all users, since the access control to the features will happen in the next mechanism.

Yes, the problem is that the current access control with static roles is embedded everywhere (React as well) and trying to remove these is going to cost a lot of time. So instead, my plan is to leave them in, hide them from the UI (replaced by the new ones), and assign all these static roles to every existing and future user so they effectively won't do any access control.

However, I'm wondering if we should keep an unrestricted bypass mechanism in the filters for the Uyuni and Org Admin roles like:

    public boolean canAccess(User user) {
        return (user.hasRole(RoleFactory.ORG_ADMIN) && user.getOrg().equals(getOrg())) ||
                user.hasRole(RoleFactory.SAT_ADMIN);
    }

or should we rely on RBAC only? Are there some areas that won't be covered by RBAC?

Copy link
Member

@rjmateus rjmateus Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can try in the existing implementation. If a user is part of organization and has not roles, and are not assigned to any systems groups, then the user cannot see any system. If the user has the role Org Admin, then it can see all the systems that are part of that organization.
So currently, the roles are not just to control access to pages but also access to data. I can show you tomorrow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok now I get it. So those superuser logic must stay in the new implementation in any case. All the others can go once we're ready to strip their logic from the individual pages.

After the initial development and mapping of all existing endpoints to the new structure, ongoing effort is required to keep endpoint mappings and the namespace structure up-to-date. To reduce RBAC overhead when implementing new features, the following additional resources are proposed:

- An extensive Wiki guide on adding necessary endpoints with proper namespace organization
- Stored procedures and scripts to easily grant/revoke user access for development
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to avoid store procedures as much as possible. They are hard to test and are hidden inside the database.

Copy link
Contributor Author

@cbbayburt cbbayburt Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. These will be just throwaway tools to help when we're implementing RFC. There's no plan to ship or persist them. Once the API is in place, they won't be needed anyway.

accepted/00000-rbac.md Show resolved Hide resolved
# Unresolved Questions
[unresolved]: #unresolved-questions

- **Granularity of access control:** Should the RBAC model include finer-grained permissions, such as access by specific actions (create, update, delete) within a namespace, or is the proposed "View" vs. "Modify" access mode sufficient for most cases?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, whenever its possible we should have as fine grain as possible, with a separation between the create, update, delete, for example.


- **Granularity of access control:** Should the RBAC model include finer-grained permissions, such as access by specific actions (create, update, delete) within a namespace, or is the proposed "View" vs. "Modify" access mode sufficient for most cases?

- **External integration needs:** Will the RBAC system require compatibility with external authentication or access control systems, such as LDAP, and if so, what integration points are required to support this?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That I would say is part of authentication. However, we could define a mapping between LDAP user groups and suma internal access groups, and automatically assign users to a group at login time.


- **External integration needs:** Will the RBAC system require compatibility with external authentication or access control systems, such as LDAP, and if so, what integration points are required to support this?

- **Additional access groups:** Should predefined access groups be expanded to cover specific use cases beyond those mirroring existing roles?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is something we will find out, and we can react according to feedback from customers. One extra groups could be the read-only user that we have now for API only. that can be a new access group and stop relying on method names and a flag on user definition.

@Etheryte
Copy link
Member

Also a small ping for @thesp0nge, if you have the time to give this a read, any input you might have on this proactively would be much appreciated.

@thesp0nge
Copy link
Contributor

thesp0nge commented Nov 13, 2024

I'm sorry but for such kind of requests you really want to ask @szachovy , your security champion.

@cbbayburt
Copy link
Contributor Author

@Etheryte @aaannz @rjmateus, all the discussions should be addressed now. Please let me know if there's something I missed.

Copy link
Member

@Etheryte Etheryte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my side this looks good, I think this is high level enough that most of the detailed discussions will happen in the specific implementation parts later.

@cbbayburt
Copy link
Contributor Author

From my side this looks good, I think this is high level enough that most of the detailed discussions will happen in the specific implementation parts later.

Thanks for the review, Karl. Yeah I think this level of detail should be enough to get everyone on the same page and make the right decisions from the beginning so we won't have to scratch the whole design.

Copy link
Member

@rjmateus rjmateus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Thank you for preparing it

Comment on lines +269 to +270
# Drawbacks
[drawbacks]: #drawbacks

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spacing throughout the document is inconsistent, can you parse it through https://github.com/DavidAnson/markdownlint ?

Copy link
Contributor

@admd admd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only done have way through. I have Left some questions for my understanding.


- **`endpoint`**: The accessible URI of the endpoint.

- **`class_method`**: The Java class that handles incoming requests to the endpoint, such as a controller class for web endpoints or an API handler for API endpoints.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this be required field or optional? What will we have in case of struts actions ?


- **`http_method`**: The HTTP method (e.g., GET, POST, PUT, DELETE) accepted by the endpoint. If multiple methods are supported, each should be defined as a separate endpoint since they typically serve different purposes.

- **`auth_required`**: Indicates if the endpoint requires authorization.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If endpoint, will we have a separate namespace for such endpoints? What endpoints would usually fall in this category?


### Namespace

A **namespace** is a logical grouping of endpoints that performs a specific task and defines the smallest unit of access control.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How will we make sure that we don't miss endpoints which are not part of any namespace? my question is more going forward - will we have any tooling which identify this?

- `clm.project.list`: Allows viewing the CLM projects list.
- `clm.project.details`: Allows viewing or modifying the details of a CLM project.

- **`access_mode`**: Defines the access level for each namespace as either "View" (R) or "Modify" (W). Many namespaces will have separate entries for each mode, with different endpoints for each purpose.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe actions like export CSV will go under view(R) and delete under W, right?


A **namespace** is a logical grouping of endpoints that performs a specific task and defines the smallest unit of access control.

- **`namespace`**: A label representing the namespace, expressed as a dot-separated string of components. Each component corresponds to a level of hierarchy for a task, with related tasks sharing the same components at higher levels.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can an end point be part of more than 1 namespace?


- **`scope`**: Indicates whether the endpoint is accessible through the web UI (including internal API calls) or the public API.

#### Organization of namespaces
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have any table to represent this in our sample database schema, right? will we have some kind of inheritance here that if I have access at the top level namespace, I get access to everything else. And if yes, can as an admin, still change it to be more granular.

- System Group Administrator
- Read-only API User

Each of these roles grants access to different features in Uyuni. This static access control is enforced individually within each feature. Stripping these control checks throughout the system requires significant effort. Therefore, in the initial phase, the logic of these static roles will coexist with their RBAC counterparts, but will be bypassed by assigning them to all existing and future users. This will effectively make RBAC the exclusive access control mechanism.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not clear to me. Yes, we will keep the existing roles so we don't break the users setup when the migrate to 5.1. But wow are we planning to get rid of them? Will we allow administrators to still play with old roles ? Having new and old permissions setup could be pretty confusing for the admin to understand.

Users can be granted access either individually or through access groups.

Users assigned to an access group inherit all permissions defined within that group.
While administrators can grant individual users additional permissions beyond those provided by their assigned group, they cannot revoke permissions set by the group. Additionally, users can belong to multiple access groups and inherit all associated access rules.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, I didn't get this. Here additional permissions means which are not covered by groups or is it more in a sense that those permissions that user got through groups can be overridden by individually assigning to the user?

Users assigned to an access group inherit all permissions defined within that group.
While administrators can grant individual users additional permissions beyond those provided by their assigned group, they cannot revoke permissions set by the group. Additionally, users can belong to multiple access groups and inherit all associated access rules.

Predefined access groups are designed to meet the needs of most environments; however, administrators can create custom groups to address specific requirements as needed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that namespaces will be static and administrator cannot change them or create new ones or will we provide the same flexibility(not considering here if that's good or bad) there as well that we are providing here in case of access groups?

- Prevent access to sensitive image store information:

```
Revoke 'View' on 'cm.store.details' from 'Alice'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If Alice got this 'View' access through one accessgroup but then here Alice got that permissions revoked, I expect this would take precedence or?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants