Access Control in OpenMRS

Access Control In OpenMRS

OpenMRS uses Role Based Access Control (RBAC) to control access to resources. Anything from creating a user to viewing a certain page is restricted through privileges. There are two layers to the OpenMRS system, the web application and the Application Program Interface (API). The web application consists of the web pages and interfaces used to work with the OpenMRS application, and the API deals with the background services (i.e. reading patient files).

Web Application

The web application is found in the webapp module, and these are mostly the .jsp pages. Most of the pages require that the user must have a certain privilege (or privileges) to access them. The check is managed by two different tags; Require Tag and Privilege Tag. The controllers for these tags can be found in org.openmrs.web.taglib package, in the web module.

Require Tag

The require tag checks if a user has the privilage(s) required to view a certain page. If the user does not have the required privilege(s), they will be redirected to another page. The controller for this tag can be found at org.openmrs.web.taglib.RequireTag.java in the web module.

Example:

<openmrs:require privilege="Manage Concept Classes" otherwise="/login.htm" redirect="/admin/concepts/conceptClass.form" />
This will demand that the user have the "Manage Concept Classes" privilege. If they don't, kick the user back to the "/login.htm" page. Then, after they log in on that page, send the user to "/admin/concepts/conceptClass.form".

.

 When this tag is called upon, the following fields are set; privilege, allPrivileges, anyPrivilege, otherwise, and redirect. In the above example, the fields are set as;

privilege = "Manage Concept Classes"
allPrivileges = null
anyPrivilege = null
otherwise = "/login.htm"
redirect = "/admin/contecpts/conceptClass.form"

If the tag is used correctly, only one of privilege, allPrivileges, anyPrivilege will have a non-null value. The result of the check depends on which one of the three fields is not null. Privilege is supposed to consist of a single privilege, and the user is granted access only if they have the privilege. allPrivileges is supposed to consist of more than one privilege, and the user is granted access only if they have all the privileges. anyPrivilege is supposed to consist of more than one privilege, and the user is granted access if they have at least one of the privileges. 

Privilege Tag

The privilege tag checks if a user has the privilege(s) required to view a certain section of a page, such as tabs on a navigation bar. If the user does not have the required privilege(s), the section of the page will not be included. The controller for this tag cab be found at org.openmrs.web.taglib.PrivilegeTag.java in the web module.

Example:

<openmrs:hasPrivilege privilege="View Navigation Menu">
This will demand that the user have the "View Navigation Menu" privilege.

The code for the example is found in webapp.WEB-INF.template.banner.jsp in the webapp module. The code is a call to check if the navigation bar should be included in the page. If the user does not have the "View Navigation Menu," then the bar will not be included.
NOTE: Even an anonymous user has the "View Navigation Menu" privilege. 

The access control check works similarly to RequireTag.java, but PrivilegeTag only has one field for privileges called privilege which can contain one or more privileges. Another difference is the isInverted field, which can be set to true or left null. If isInverted is not set to true, then the user is granted access if they have at least one of the privileges. If isInverted is set to true, then the user is granted access if they don't have any of the privileges.

API

The API is found in the api module, and this is where all the background stuff happens.

"Everything is delivered from the database in the form of objects: Patient, User, Concept, etc java objects.
You can fetch/save these objects using what we call "services" (PatientService, UserService, ConceptService).
You can get the services at any point by calling Context.getPatientService(), .getUserService(), etc." How To Use the OpenMRS API

Services are handled through interfaces. The service interfaces are found at org.openmrs.api and the implementation of the services are found at org.openmrs.api.impl. The methods declared in interfaces use @Authorized annotation.

Authorized Annotation

@Authorized annotation can be found at org.openmrs.annotation.Authorized. It is used to describe service layer authorization attributes.

Examples:

To require that the user have either View or Add privileges:
@Authorized ({"View Users", "Add User"})
public void getUsersByName(String name);
To require that they have all privileges
@Authorized (value = {"Add Users", "Edit Users"}, requireAll=true)
public void getUsersByName(String name); 
To just require that they be authenticated
@Authorized ()
public void getUsersByName(String name);

The Authorized annotation has two fields value and requireAll. value takes one or more privileges, and requireAll can be set to true (but is false by default).

In the above examples, the fields are set the following way:

Example 1: @Authorized ({"View Users", "Add User"})
value = {"View Users", "Add User"}
requireAll = false
Example 2: @Authorized (value = {"Add Users", "Edit Users"}, requireAll=true)
value = {"Add Users", "Edit Users"}
requireAll = true
Example 3: @Authrozied()
value = {}
requireAll = false

Authorization Advice

Authorization Advice can be found at org.openmrs.aop.AuthorizationAdvice and it provides the authorization Aspect Oriented Programming (AOP) advice performed before every service layer method call. In applicationContext-service.xml an authorizationInterceptor bean is defined and included in the serviceInterceptors list. This makes Spring wrap all service methods with an AOP advice class. And using the @Authorized annotation on a method, the advice class will check that the currently-authenticated user has that privilege. If the user does not have the required privilege(s), an API Authentication Exception is thrown.

NOTE: Some methods do further privilege checking.