Skip to main content

Securing MarkLogic Server

JavaScript Examples of Element Security

You can also query the documents using Server-Side JavaScript. Run these JavaScript queries, using the previous users and documents, on the Documents database in Query Console.

First run the queries in the context of els-user-1:

// run this against the Documents database

var prog1 = `cts.search(cts.wordQuery("def"), "unfiltered")`;
var prog2 = `cts.search(cts.elementAttributeWordQuery(xs.QName("bar"), xs.QName("attr"), "test1"), "unfiltered")`;
var prog3 = `cts.search(cts.jsonPropertyValueQuery("bar", "2"))`;
var prog4 = `cts.search(cts.elementAttributeWordQuery(xs.QName("reg"), xs.QName("expr"), "is"), "unfiltered")`;
var res = [];
res.push(xdmp.eval(prog1, null, {userId:xdmp.user("els-user-1")}));
res.push(xdmp.eval(prog2, null, {userId:xdmp.user("els-user-1")}));
res.push(xdmp.eval(prog3, null, {userId:xdmp.user("els-user-1")}));
res.push(xdmp.eval(prog4, null, {userId:xdmp.user("els-user-1")}));
res;
=>
[
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<root><bar baz=\"2\">def</bar>
<bar attr=\"test1\">ghi</bar>
</root>", 
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<root>
<bar baz=\"2\">def</bar>
<bar attr=\"test1\">ghi</bar>
</root>", 
  {
  "foo": 1, 
    "bar": "2", 
    "baz": {
      "bar": [
       3, 
       4
      ]
    }
  },
  null
]

Notice that all of the documents are returned, but the elements with protected paths are missing from the content:

<bar baz="1" attr="test">abc</bar>
"test": 5
<reg expr="this is a string">1</reg>

In the second query, the document does not show up at all because the query is searching on a protected path that els-user-1 is not allowed to see (protected path “test”).

Note

If you are getting different results, check to see that you have set up your user roles correctly and added the query rolesets to the Security database.

Now, modify the query to use the context of the els-user-2 and run the queries again:

// run this against the Documents database

var prog1 = `cts.search(cts.wordQuery("def"), "unfiltered")`;
var prog2 = `cts.search(cts.elementAttributeWordQuery(xs.QName("bar"), xs.QName("attr"), "test1"), "unfiltered")`;
var prog3 = `cts.search(cts.jsonPropertyValueQuery("bar", "2"))`;
var prog4 = `cts.search(cts.elementAttributeWordQuery(xs.QName("reg"), xs.QName("expr"), "is"), "unfiltered")`;
var res = [];
res.push(xdmp.eval(prog1, null, {userId:xdmp.user("els-user-2")}));
res.push(xdmp.eval(prog2, null, {userId:xdmp.user("els-user-2")}));
res.push(xdmp.eval(prog3, null, {userId:xdmp.user("els-user-2")}));
res.push(xdmp.eval(prog4, null, {userId:xdmp.user("els-user-2")}));
res;
=>
[
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<root>
<bar baz=\"1\" attr=\"test\">abc</bar>
<bar baz=\"2\">def</bar>
<bar attr=\"test1\">ghi</bar>
</root>", 
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<root><bar baz=\"1\" attr=\"test\">abc</bar>
<bar baz=\"2\">def</bar>
<bar attr=\"test1\">ghi</bar>
</root>", 
  {
  "foo": 1, 
    "bar": "2", 
    "baz": {
      "bar": [
       3, 
       4
      ]
    ,
"test": 5
    }
  },
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<root>
<reg expr=\"this is a string\">1</reg>
<reg>2</reg>
</root>"
]

This time all of the documents are returned, along with the protected elements. Notice that the one document is returned twice; two different queries will find the same document.

Run the query one more time using the xdmp:eval() pattern as els-user-3 and notice that none of the documents are returned because els-user-3 does not have the basic permissions to read the documents.

// run this against the Documents database

var prog1 = `cts.search(cts.wordQuery("def"), "unfiltered")`;
var prog2 = `cts.search(cts.elementAttributeWordQuery(xs.QName("bar"), xs.QName("attr"), "test1"), "unfiltered")`;
var prog3 = `cts.search(cts.jsonPropertyValueQuery("bar", "2"))`;
var prog4 = `cts.search(cts.elementAttributeWordQuery(xs.QName("reg"), xs.QName("expr"), "is"), "unfiltered")`;
var res = [];
res.push(xdmp.eval(prog1, null, {userId:xdmp.user("els-user-3")}));
res.push(xdmp.eval(prog2, null, {userId:xdmp.user("els-user-3")}));
res.push(xdmp.eval(prog3, null, {userId:xdmp.user("els-user-3")}));
res.push(xdmp.eval(prog4, null, {userId:xdmp.user("els-user-3")}));
res;
=>
[
null, 
null, 
null, 
null
]

Because els-user-3 does not have document level permissions, no documents are returned. You can use document level permissions along with element level security for additional security. See Combining Document and Element Level Permissions for more information.

Now unprotect the paths and run the previous query again without the protected paths to see difference in output. Unprotect the paths :

//run this against the Security database

var security = require('/MarkLogic/security.xqy');
declareUpdate();

security.unprotectPath('/root/bar[@baz=1]', []);
security.unprotectPath('test', []);
security.unprotectPath('/root/reg[fn:matches(@expr, "is")]', []);

Note

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

Unprotecting the paths does not remove them from the database. You will still see the protected paths in the Admin Interface or when you run fn:collection("http://marklogic.com/xdmp/protected-paths") against the Security database. But if you are els-role-1 or els-role-2, you will be able to see the whole document once the protected paths are unprotected, if you have document permissions for the document (like els-role-1 and els-role-2 but not els-role-3). See Unprotecting or Removing Paths for more details.

Look through the code examples and run the queries using the xdmp.eval() pattern. Run the queries in the context of the different users to better understand how the element level security logic works.