Authentication and authorization framework re-work

We have another exciting endeavour in OpenMRS Core! We are planning to do a revamp of our authentication and authorization framework. It’s a complex topic and we are hoping to do it in an informed way thus asking developers and implementers for input.

Currently OpenMRS Core supports only basic authentication. In addition to that there’s the OAuth module that enables the use of external authentication providers. We are considering upgrading to Spring Security and thus opening up for different authentication methods from within core.

The more impactful and more complex change that we are planning is around authorization. Currently OpenMRS uses permissions and roles to limit access to certain operations for users. There’s also the data filter module that tries to add another layer of access control based on a location that a user chooses when logging in.

What’s currently possible?

Currently, it’s possible to enforce rules such as:

Registration clerk can manage ALL patients’ visits, but has no access to ANY patients’ encounters and observations.

Dr. Smith can only view/edit ALL patients assigned to a certain location. (with data filter module)

What are we aiming for?

We would like to provide a more granular access control. There are different approaches to this complex problem so we need to determine one that suits needs for majority of implementers thus this initial thread in hope to hear about all your needs that are not addressed by the currently available solutions.

Here are some examples of attribute and relationship based access control rules that we are aiming for to be possible:

Dr. Smith can view Patient A’s encounters and observations because Dr. Smith is a member of the Cardiology Team, and the Cardiology Team is the treating group for Patient A.

Dr. Brown is examining Patient A and can only view Patient A’s encounters and observations until examination is completed (e.g. conducted MRI, visit completed).

Mrs. Taylor is doing laboratory tests and can only view/enter encounters and observations of specific type.

Evaluation of Spring ACL

Spring Security provides a feature called ACL, which is a domain-object-level access control (e.g., “User A has READ access to Record 123”).

While that sounds exactly like what OpenMRS needs, the way Spring ACL implements this makes it dangerous for our domain. It creates dedicated tables to map permissions. If you have thousands of patients with hundreds of individual records (lab results, notes, prescriptions), and hundreds of staff members, Spring ACL will generate millions of rows just to map who can see what. Querying these massively bloated tables on every single data fetch introduces severe latency. Moreover it is static so e.g. if a nurse’s shift ends, or a doctor is temporarily assigned to a new ward, updating thousands of individual ACL rows in the database to reflect that temporary reality is a nightmare.

The Proposed Technical Path

The currently considred proposal is to continue with our RBAC permissions and add Attribute and Relationship Based Access Control via Spring Security’s method annotations like:

@PreAuthorize("@openmrsSecurity.isAuthorized(authentication, #patientId)") to handle the logic dynamically in code.

@PostAuthorize for filtering of results and @AuthorizeReturnObject to proxy returned objects and limit access on their fields.

For performance reasons, we should also provide a mechanism to adjust DB queries similar to what the data filter module does so that not all checks are done in code, but also at the DB level.

This change will not happen in one release. We aim to provide a framework to add these more complex access rules and start refactoring code to be able to apply those rules in different places.

It is also essential for an implementation to be able to adjust rules or disable them completely depending on needs.

How can you help?

First of all its important to identify different access control rules that you see relevant in your setup. A list of examples as listed above.

We are also looking for alternative ideas or frameworks that we could use to enforce rules.

Once we identify the technical solution and business rules, we would love help from developers to introduce changes in core and modules.

3 Likes

Thanks for getting this conversation started @raff . A lot of work has gone into the authentication side of things via the authentication and authenticationui modules over the past few years, so whatever is done needs to be well aware of what these modules do, what they aim to solve, and ensure that the future path provides a clear plan for how these will fit in or need to evolve.

Thanks @raff! I at least like the idea of having some additional context supplied when making authorization decisions. Hopefully, we can develop the kind of thing that will allow implementations to customize this as they need so that. One thing I’m wondering is if we can leverage the Infinispan cache to improve performance by, e.g., at least caching the results of decisions within the scope of a single request or operation.

I don’t think there’s anything wrong with data-filter style DB query re-writes, but it would be nice if we could do this in a way that requires a little less deep system knowledge to setup and avoid footguns (like having data filter queries that accidentally touch unindexed columns or unindexed column-combinations which can slow down the whole system).

I don’t think Raff’s proposal here will touch too much that concerns the authentication side of things (we still have to establish who a user is before we can evaluate rules), but we do have a potential GSoC project this year (well two) that do have implications for this (finally using authentication in O3 and re-working how password authentication works). However, the goal for both of those is to maintain the same interfaces.

@ibacher re: your note that the ‘finally using authentication in O3’ GSoC project is active this year, I’ve shared a detailed frontend architecture in thread 49131 (openmrsFetch Location-header contract, 2FA polling, requirePasswordChange routing). Would appreciate your thoughts whenever you have a moment specifically on whether the frontend should negotiate the response version or gate behind a feature flag.