Tuesday, January 9, 2018

Dynamic document based security

Domino provides document based security through the use of readers and authors fields. This is a great capability that is fairly unique in the NoSQL industry heavily used by Domino developer. One caveat though: you have to explicitly manage the list of users/group/roles in each document, generally based on the document state or content. How many times did you write an agent (background task) to update these fields nightly?

Since the beginning, Darwino supports readers and authors (as well as excluded readers and authors to remove specific users from the authorized list). It is implemented by a hook in the SQL generator that systematically adds a condition to the queries. Here is how it looks like in pseudo SQL:
SELECT D.JSON FROM DOC D WHERE (....) AND (D.READERS IN (:userid, :userroles, :usergroups...)

userid, userroles and usergroups are values related to the user making the request.
All of this is implemented (and by the way optimized) at the core Darwino level. So it applies regardless of API you use: Java, JavaScript, REST services, GraphQL...

But in 2.1, it goes beyond this simple capability: it allows an application to insert its own SQL filter:
SELECT D.JSON FROM DOC D WHERE (....) AND (<custom condition>)

For example, the custom condition can be based on fields within the document. Let's suppose that the current user object has an attribute 'country' set in LDAP. Let's also suppose that the documents in a JSON store also have a field name 'country'. From a security standpoint, we only want to allow the users to see the documents tagged with their country. With classic readers fields, you'll need an agent that periodically updates the document readers, or use groups and assign users to groups. In both cases, there is some maintenance to do.

With Darwino, it becomes as easy as implementing a runtime extension that returns a condition for the current user (again, simplified pseudo code):

String getDynamicSecurity() {
    User u = getCurrentUser();
    return "{country: '"+u.getCountryAttribute()+"'}";


There is no need to maintain a list of readers here. If a user is moved to a different country, then the query will use the latest user country (or any other needed value), immediately!

The dynamic security condition can be expressed using the built-in, MongoDB like, query language as above. This platform independent query is transpiled to SQL by the core Darwino code.
But it can also be expressed as raw SQL, allowing the filter to be as complex as desired. For example, it can lookup in another SQL table, view or stored procedure for a list of authorized ids for the current user:
This is limitless. Let's suppose that you have a users database where each user document has the name of its manager. Now suppose that user documents can only be accessed by the user and his managers. Well, just get the list of authorized IDs from a recursive SELECT statement
If the subquery becomes too complex and leads to performance issues, you can create materialized views that will be managed by the database server.

Come on MongoDB, you cannot compete against the power of relational databases :-)


Post a Comment