Attribute‐Based Access Control (ABAC) in NoSQL

By Adam Fowler

A useful pattern for security is to apply permissions based on data within a record rather than separately assign permissions to the record. This could be based on either metadata, individual column (Bigtable clones), or element (Aggregate NoSQL databases) values.

A good example is a customer name being mentioned within a document. You may want to restrict access to all the documents mentioning that ­customer to only those people with access to this customer’s information. You can restrict access to these documents by processing the data within the document, and applying the relevant security permissions based on the value of that data.

No NoSQL databases provide this capability right out of the box. That’s because permissions must be assigned to the record after the data is saved by the application but before it’s available for retrieval by other applications or users. So, this permission assignment must occur within the transaction boundary.

Also, very few NoSQL databases support ACID‐compliant transactions (MarkLogic, FoundationDB, and Neo4j, do for example). If a database doesn’t support out‐of‐the‐box assignment of permissions based on data within a document but does support ACID transactions and pre‐commit triggers, then an easy workaround is possible.

It’s generally easy to write a trigger that checks for the presence of a value within a record and to modify permissions based on its value. As long as a database supports doing so during the commit process, and not after the commit, then you know your data is made secure by using a simple pre‐commit trigger.

As an example, MarkLogic Server supports fully serializable ACID transactions and pre‐commit triggers. Following is a simple XML document that I want to support for attribute‐based access control:

<MeetingReport>
    <SalesPerson>jbloggs</SalesPerson>
    <Customer>ACME</Customer>
    <Notes>Lorem Ipsum Dolar Sit Amet...</Notes>
</MeetingReport>

MarkLogic Server’s triggers use the W3C XQuery language. The following XQuery example is a simple trigger that, when installed in MarkLogic, assigns read and write permissions:

xquery version "1.0-ml";
import module namespace
  trgr = 'http://marklogic.com/xdmp/triggers'
  at '/MarkLogic/triggers.xqy';
declare variable $trgr:uri as xs:string external;
declare variable $trgr:trigger as node() external;
if (“ACME” = fn:doc($trgr:uri)/MeetingReport/Customer)
then
  xdmp:document-set-permissions($trgr-uri,
    (xdmp:permission(“seniorsales”,”update”),
     xdmp:permission(“sales”,”read”)
    )
  )
else ()

Once the trigger is installed in the file setperms.xqy in a MarkLogic Server Modules Database, execute the following code in the web coding application for MarkLogic – Query Console to enable the trigger. On a default MarkLogic Server installation, you can find the Query Console at the URL: http://localhost:8000/qconsole.

Here is code showing how to install the trigger using Query Console:

xquery version "1.0-ml";
import module namespace
  trgr='http://marklogic.com/xdmp/triggers'
  at '/MarkLogic/triggers.xqy';
trgr:create-trigger("setperms",
  "Set Sales Doc Permissions",
  trgr:trigger-data-event(
    trgr:collection-scope("meetingreports"),
    trgr:document-content("modify"),
    trgr:pre-commit()
  ), trgr:trigger-module(
    xdmp:database("Modules"), "/triggers/",
    "setperms.xqy"
  ), fn:true(),
  xdmp:default-permissions(),
  fn:false()
)