Oops. I just realized my example will only run in the upcoming MarkLogic 9, where we've updated the JavaScript engine to support ES2015, the latest version of the language. I'll leave it as an exercise for the reader to translate to MarkLogic 8-compatible ES5 or XQuery; the concepts are the same.
Happy coding! Justin > On Dec 10, 2016, at 11:37 AM, Justin Makeig <[email protected]> > wrote: > > 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
