Skip to main content

Securing MarkLogic Server

XQuery - Matching by Paths or Attributes

This next example shows how protected paths can be used with fn:contains() and fn:matches(). The example uses the same roles from the previous example, adding a new role (els-role-3).

First unprotect the protected paths from the previous example:

(: unprotect the protected paths -> run against the Security database :)

xquery version "1.0-ml"; 
import module namespace sec = "http://marklogic.com/xdmp/security" 
  at "/MarkLogic/security.xqy";
      
sec:unprotect-path("secret", ()),
sec:unprotect-path("top-secret", ())

Note

Adding or unprotecting protected paths will trigger reindexing. After unprotecting elements, you must wait for reindexing to finish.

Create a new role els-role-3 and add els-user-3 to the role. See Create Roles and Create Users and Assign Roles for details.

Add a new document with permissions to the Documents database:

(: run this against the Documents database :)

xquery version "1.0-ml";

xdmp:document-insert(
"attributes.xml", <root>
 <title>Document Title</title>
 <summary>Summary of document contents</summary>
 <executive-summary>Executive summary of contents
  <info attr="EU">Only role with "EU" attribute can read this summary </info>
  <info attr="UK">Only role with "UK" attribute can read this summary </info>
  <info attr="US">Only role with "US" attribute can read this summary </info>
</executive-summary>
 <content>Contents of document 
  Unclassified content
  <notes> 
    <info attr="EU">Only role with "EU" attribute can read this content</info>
    <info attr="UK">Only role with "UK" attribute can read this content</info>
    <info attr="US">Only role with "US" attribute can read this content</info>
  </notes>
 </content>
</root>,
(xdmp:permission("els-role-1", "read"), xdmp:permission("els-role-2", "read"), xdmp:permission("els-role-3", "read"),
xdmp:permission("els-role-1", "update"), xdmp:permission("els-role-2", "update"), xdmp:permission("els-role-3", "update")))

Add the new protected paths with permissions for roles to the Security database:

(: add new protected paths -> run against the Security database :)

xquery version "1.0-ml";
import module namespace sec="http://marklogic.com/xdmp/security" 
  at "/MarkLogic/security.xqy";

sec:protect-path("//info[fn:matches(@attr, 'US')]", (), (xdmp:permission("els-role-1", "read"))),
sec:protect-path("//info[fn:matches(@attr, 'UK')]", (), (xdmp:permission("els-role-2", "read"), 
  xdmp:permission("els-role-3", "read"))),
sec:protect-path("//info[fn:matches(@attr, 'EU')]", (), (xdmp:permission("els-role-3", "read")))
=>
Returns three numbers representing the protected paths

Note

Adding, unprotecting, or changing permissions on protected paths will trigger reindexing.

Notice that the protected paths include attributes in the document elements. Also note that els-role-3 has permissions for two protected paths (@attr, ‘UK’ and @attr, ‘EU’).

Run this next query, similar to the previous queries, this time looking for the attributes.xml document. First query in the context of els-user-1 who has a role that can see the “US” attribute:

(: run this against the Documents database :)

xdmp:eval('fn:doc("attributes.xml")',(),
  <options xmlns="xdmp:eval">
    <user-id>{xdmp:user("els-user-1")}</user-id>
  </options>
)

=>
<?xml  version="1.0" encoding="UTF-8"?>
<root>
 <title>Document Title</title>
 <summary>Summary of document contents</summary>
 <executive-summary>Executive summary of contents 
  <info attr="US">Only role having "US" attribute can read this summary</info>
 </executive-summary>
 <content>Contents of document 
  Unclassified content 
 <notes>
  <info attr="US">Only role having "US" attribute can read this content
  </info>
 </notes>
 </content>
</root>

Next modify the query to run in the context of els-user-2, who has a role that can see the “UK” attribute:

(: run this against the Documents database :)

xdmp:eval('fn:doc("attributes.xml")',(),
  <options xmlns="xdmp:eval">
    <user-id>{xdmp:user("els-user-2")}</user-id>
  </options>
)

=>
<?xml  version="1.0" encoding="UTF-8"?>
<root>
 <title>Document Title</title>
 <summary>Summary of document contents</summary>
 <executive-summary>Executive summary of contents 
  <info attr="UK">Only role having "UK" attribute can read this summary
  </info>
 </executive-summary>
 <content>Contents of document 
  Unclassified content 
 <notes>
  <info attr="UK">Only role having "UK" attribute can read this content</info>
 </notes>
 </content>
</root>

And finally modify the query to run in the context of els-user-3:

(: run this against the Documents database :)

xdmp:eval('fn:doc("attributes.xml")',(),
  <options xmlns="xdmp:eval">
    <user-id>{xdmp:user("els-user-3")}</user-id>
  </options>
)

=>
<?xml  version="1.0" encoding="UTF-8"?>
<root>
 <title>Document Title</title>
 <summary>Summary of document contents</summary>
 <executive-summary>Executive summary of contents 
  <info attr="EU">Only role having "EU" attribute can read this summary

  </info>
  <info attr="UK">Only role having "UK" attribute can read this summary
   
  </info>
 </executive-summary>
 <content>Contents of document 
  Unclassified content
  
 <notes>
  <info attr="EU">Only role having "EU" attribute can read this content
     
  </info>
  <info attr="UK">Only role having "UK" attribute can read this content
     
  </info>
 </notes>
 </content>
</root>

The els-user-3 has protected path permissions on both elements with the “EU” info attribute and the elements with the “UK” info attribute, so the els-user-3 can see both elements. If you are getting different results, check to be sure that you created an els-role-3 and added the els-user-3 to that role.

Note

If you run the query in the context of the admin user, you will be able to see the entire document because the query is using fn:doc.