Because permissions are indexed, with the right incantation you can also query 
by permissions. First you need to create a hash of the permission using the 
role and capability, similar to the xdmp.permission constructor: 

  // JavaScript
  xdmp.add64(
    xdmp.mul64(
      xdmp.add64(
        xdmp.mul64(roleID, 5), xdmp.hash64(capability)
      ), 5
    ), xdmp.hash64('permission()')
  );

Then use that hash to create a cts.termQuery (or in XQuery cts:term-query). 
cts.termQuery is an undocumented query that matches the low-level terms that 
make up the indexes.

You can use a cts.termQuery just like, or along with, any other cts.query. For 
example,

  cts.uris(null, null, cts.termQuery(hash));

The example below allows you to specify one or more role IDs and it will 
calculate the cross-product of roles and capabilities to build a query that 
will match any document with any permission that uses those roles. (The 
minimized blob at the top is just helper functions.)

Justin



'use strict';

/** {@link 
https://gist.github.com/jmakeig/0a331823ad9a458167f6#file-apply-as-9-sjs} */
function applyAs(fct,options,thisArg){return function(){const 
f=()=>{return[fct.call(thisArg,...arguments)]};options=options||{};if('string'===typeof
 
options.database){options.database=xdmp.database(options.database)}if(options.user){options.userId=xdmp.user(options.user);delete
 
options.user}if(fct.transactionMode&&!(options.transactionMode)){options.transactionMode=fct.transactionMode}return
 fn.head(xdmp.invokeFunction(f,options)).pop();}};
/** {@link 
https://gist.github.com/jmakeig/0a331823ad9a458167f6#file-hof-mapper-sjs} */
function map(fct,mapper){const ident=item=>item;mapper=mapper||ident;return 
function*_map(){const itr=fct.apply(null,arguments);if('string'===typeof 
itr||!(itr[Symbol.iterator])){yield itr}else{for(const item of itr){yield 
mapper(item)}}}};

const sec = require('/MarkLogic/security');

/**
 * Generate hashes of permission terms for the cross product of roles and 
capabilities (read, 
 * update, insert, execute). Use `cts.termQuery(hash)` to match documents by 
their terms.
 *
 * @param   {string|Iterable.<string>} [roleIDs] - One or more role IDs. 
Defaults to all roles.
 * @returns {GeneratorFunction}                  - A generator that yields 
permission term hashes for the 
 *                                                 cross-product of roles and 
capabilities
 */
function* getPermissionTerms(roleIDs) {
  const capabilities = ['read', 'update', 'insert', 'execute'];
  /** @function */
  const getRoleIDs = map(applyAs(sec.getRoleIds, { database: 
xdmp.securityDatabase() }), fn.data);
  function permHash(roleID, capability) {
    return xdmp.add64(
      xdmp.mul64(
        xdmp.add64(
          xdmp.mul64(roleID, 5), xdmp.hash64(capability)
        ), 5
      ), xdmp.hash64('permission()')
    );
  }
  for(const roleID of roleIDs || getRoleIDs()) {
    for(const capability of capabilities) {
      yield permHash(roleID, capability);
    }
  }
}

const query = cts.orQuery(
    [...getPermissionTerms(/* Array of roleIDs here to limit to specific roles 
*/)]
      .map(term => cts.termQuery(term))
);

// URIs of the documents that have the supplied permissions
cts.uris(null, null, query);




> On Dec 8, 2016, at 6:15 PM, Will Thompson <[email protected]> wrote:
> 
> I got a script working. I suspect there are faster/better ways to do this, 
> but for anyone who may need it in the future, this will resolve any orphaned 
> permissions. For a very large data set, you would probably need to batch this:
> 
> xquery version "1.0-ml";
> 
> let $uris := cts:uris((), 'limit=30000')
> let $permissions-map :=  map:new((
>  $uris ! map:entry(., xdmp:document-get-permissions(.))
>  ))
> let $orphaned-map :=
>  xdmp:eval('
>  xquery version "1.0-ml";
>  import module namespace sec="http://marklogic.com/xdmp/security"; at 
>    "/MarkLogic/security.xqy";
>   declare variable $PERMISSIONS external;
>   map:new(
>     for $uri in map:keys($PERMISSIONS)
>     let $orphaned := 
>       for $p in map:get($PERMISSIONS, $uri)
>       return try { 
>         let $name := sec:get-role-names($p/sec:role-id) 
>         return ()
>       }
>       catch ($e) { 
>         if ($e/error:code = "SEC-ROLEDNE")
>         then $p
>         else xdmp:rethrow()
>       }
>     where (exists($orphaned))
>     return map:entry($uri, $orphaned)
>  )
>  ', 
>  (xs:QName('PERMISSIONS'), $permissions-map),
>  <options xmlns="xdmp:eval">
>    <database>{xdmp:security-database()}</database>
>  </options>)
> for $o in map:keys($orphaned-map)
> let $permissions := map:get($orphaned-map, $o)
> return xdmp:document-remove-permissions($o, $permissions) 
> 
> -W
> 
>> On Dec 8, 2016, at 6:10 PM, Will Thompson <[email protected]> wrote:
>> 
>> Hi Chris,
>> 
>> That's entirely plausible and even likely. Do you know of an easy way to 
>> remove orphaned permissions? If there's not a shortcut, I could probably 
>> build a script to do it, but it seems like it might be messy: walk through 
>> every document in the db, try to lookup each doc's permissions, catch failed 
>> ones and delete them?
>> 
>> -Will
>> 
>>> On Dec 8, 2016, at 6:01 PM, Christopher Hamlin <[email protected]> wrote:
>>> 
>>> Hi,
>>> 
>>> This is more of a guess than a suggestion.  Let's call it a suggestive 
>>> guess.
>>> 
>>> It looks like export-to-archive copies permissions.  I'm not sure what 
>>> would happen if you had a permission (which has a role-id) and the role 
>>> with that id got removed.  Maybe the above?
>>> 
>>> - Chris
>>> 
>>> On Thu, Dec 8, 2016 at 6:39 PM, Will Thompson <[email protected]> 
>>> wrote:
>>> I am trying to export data from a Windows machine into an archive to be 
>>> imported into a Mac, but MLCP crashes. The error MLCP returns is nearly 250 
>>> lines, but this part seemed possibly relevant:
>>> 
>>> "...Caused by: com.marklogic.xcc.exceptions.XQueryException: SEC-ROLEDNE: 
>>> (err:FOER0000) Role does not exist: sec:role-id = 6626745612256060316
>>> [Session: user=wthompson, cb=#17033682864837852147 [ContentSource: 
>>> user=wthompson, cb={none} [provider: address=localhost/127.0.0.1:10003, 
>>> pool=4/64]]] [Client: XCC/8.0-1, Server: XDBC/8.0-6]
>>> in /MarkLogic/security.xqy, on line 960..."
>>> 
>>> The MLCP command is:
>>> 
>>> mlcp.bat export
>>> -host localhost -port 10003 -username wthompson -password *****
>>> -output_type archive -output_file_path /some/output/path -directory_filter 
>>> /some/dir/
>>> 
>>> This is on ML 8.0-6 with the latest MLCP binaries. Any suggestions?
>>> 
>>> -Will
>>> _______________________________________________
>>> General mailing list
>>> [email protected]
>>> Manage your subscription at:
>>> http://developer.marklogic.com/mailman/listinfo/general
>>> 
>>> _______________________________________________
>>> General mailing list
>>> [email protected]
>>> Manage your subscription at: 
>>> http://developer.marklogic.com/mailman/listinfo/general
>> 
>> _______________________________________________
>> General mailing list
>> [email protected]
>> Manage your subscription at: 
>> http://developer.marklogic.com/mailman/listinfo/general
> 
> _______________________________________________
> General mailing list
> [email protected]
> Manage your subscription at: 
> http://developer.marklogic.com/mailman/listinfo/general

_______________________________________________
General mailing list
[email protected]
Manage your subscription at: 
http://developer.marklogic.com/mailman/listinfo/general

Reply via email to