Monday, October 21, 2013

Supporting permissions in your persistence API implementation

Up to version 4.2, implementations of the persistence API didn't need to worry about permissions; permissions were entirely checked by the part of Form Runner that was calling the persistence API. This changed with version 4.3, which introduced owner-group/based permissions. Let's see what changed:
  1. Without this feature, i.e. up to version 4.2, you could determine whether a user can perform a certain operation just based on the user's roles, the operation (create, read, update, or delete), and the app/form names. For instance, we could know if Alice was authorized to read data for the License application form even before calling the persistence API.
  2. This changes with the owner/group-based permissions: it could be that Alice can read all the data for the License application form just based on her roles (maybe she's an admin, or in charge of processing applications); but if not, she might still be authorized to access some of the data for the License application form, for instance the data she created, or data created by other users in the same group, depending on how permissions were setup.
This second point means that some filtering needs to happen, based on who the user is. Say Alice accesses the License application form summary page, and the system was setup so she can only see and access her own applications. In that case, the summary page will maybe show the 5 applications she filled, amongst the maybe thousands stored in the databases. It would be unreasonable for the implementation of the persistence API to return all the applications, and count on the caller to do the filtering: as more data is entered into the system, this could become increasingly more inefficient. Hence, the job of checking permissions has been in part shifted to the implementation of the persistence API. Now:
  • The search API only returns documents the current user has access to, and for each document specifies which operations the user can perform, this through the operations attribute on the document element in the query response.
  • This same information is also returned when reading a document. In that case, since the the document is returned in the body, the list of operations is returned "out-of-band" through the Orbeon-Operations header.
To make it easier for implementations of the persistence API to deal with permissions, Orbeon Forms offers a few helper functions, provided as static methods that can be called from Java, implemented in FormRunnerPermissionsOps.scala. All the static methods mentioned below are in the org.orbeon.oxf.fr.FormRunner class. 
  • If you're implementing search, you could use those operations as follows:
    1. First, you might want to ask: is Alice authorized to access all license applications just based on her roles? The static method javaAuthorizedOperationsBasedOnRoles() answers this question.
    2. If the answer is positive, then you can return all the data, just as you used to be done in 4.2 and earlier.
    3. However, if the answer is negative (there are no operations Alice can do on license application just based on her roles), then you'll want to filter out and return only the applications Alice directly created, or that were created by another in her group, depending on the configuration.
    4. Finally, when returning applications Alice has access to, you can find what operations she can perform on each document by calling javaAllAuthorizedOperations().
  • If you're implementing the read operation, as mentioned earlier, you're expected to return the operations the user has access to through the Orbeon-Operations header. If you wish, you can delegate this task to the setAllAuthorizedOperationsHeader() method, which will figure out the list of operations and set the header for you.

No comments:

Post a Comment