Custom Role for creating/updating users without access to clinical data.

Hello everyone,

Context: We have a requirement to create an users admin role in openmrs, to sole purpose of this role is to create/update users without having access to any clinical data.

The Challenge with this is that fact that in openmrs-core there are some validations within UserService#save method which checks

  • That the logged in user has all the privileges that it is trying to assign to the user being created/updated.
private void checkPrivileges(User user) {
		Collection<Role> roles = user.getAllRoles();
		List<String> requiredPrivs = new ArrayList<>();
		
		for (Role r : roles) {
			if (r.getRole().equals(RoleConstants.SUPERUSER)
			        && !Context.hasPrivilege(PrivilegeConstants.ASSIGN_SYSTEM_DEVELOPER_ROLE)) {
				throw new APIException("User.you.must.have.role", new Object[] { RoleConstants.SUPERUSER });
			}
			if (r.getPrivileges() != null) {
				for (Privilege p : r.getPrivileges()) {
					if (!Context.hasPrivilege(p.getPrivilege())) {
						requiredPrivs.add(p.getPrivilege());
					}
				}
			}
		}
		
		if (requiredPrivs.size() == 1) {
			throw new APIException("User.you.must.have.privilege", new Object[] { requiredPrivs.get(0) });
		} else if (requiredPrivs.size() > 1) {
			StringBuilder txt = new StringBuilder("You must have the following privileges in order to assign them: ");
			for (String s : requiredPrivs) {
				txt.append(s).append(", ");
			}
			throw new APIException(txt.substring(0, txt.length() - 2));
		}
	}

Ref.: UserServiceImpl#checkPrivileges(User)

The Approach that we are thinking of in order to solve this

  • create a special privilege (something like assign roles )
  • bypass the check based on assign roles privilege , which will need a code change in the method mentioned above.

Please let us know your thoughts on the approach and also if there is any other suggested way of achieving this.

Thanks

@mogoodrich @mksd @dkayiwa

cc: @rrameshbtech @vasanth2019 @mddubey

2 Likes

Looks like the assumption was that if logged in user jdoe wants to create user wsmith then this is strictly required:

privileges(wsmith) ⊆ privileges(jdoe)

As @sukreet suggests it feels to me indeed that we could definitely add something like

if (Context.hasPrivilege("_special_privilege_name_here_")) {
  return;
}

Thoughts? @burke, @ibacher, @mseaton, @wyclif ?

2 Likes

We also need to think through some edge cases like.

  • What if the logged in user with the “special_privilege” tries to assign. more roles to itself ?
1 Like

That edge case is fairly easy to deal with. Just a simple:

if (Context.getAuthenticatedUser().equals(user)) {
    throw new APIException("...");
}

The bigger issue is what happens when you have two users with the “user admin” role that coordinate with each other so that user B adds a role to user A. There’s probably no way to make a general rule about this, however (someone might be legitimately both a clinician and a system administrator, though in my view, the usual way to handle this would be to have two separate accounts).

We’d also probably want to prevent this role from having the ability to assign the system developer role.

I’m definitely ok with the proposed design, but I wonder if it isn’t worth thinking about how we might extract this logic in such a way that it’s more easily configurable by implementers. But anything that gets us away from having to assign the system developer to anyone in a production system is a win in my book.