Исходный код вики Check programming rights
Редактировал(а) Dmitriy Rybakov 05.08.2025, 13:07
Скрыть последних авторов
| author | version | line-number | content |
|---|---|---|---|
| |
1.1 | 1 | {{groovy}} |
| 2 | import org.xwiki.model.reference.EntityReference | ||
| 3 | import org.xwiki.security.authorization.Right | ||
| 4 | |||
| 5 | xwiki.ssx.use(doc.getFullName()) | ||
| 6 | xwiki.jsx.use(doc.getFullName()) | ||
| 7 | |||
| 8 | if (!"post".equalsIgnoreCase(request.getMethod())) { | ||
| 9 | printForm(pageListPerWiki()) | ||
| 10 | } else if (!services.csrf.isTokenValid(request.getParameter('form_token'))) { | ||
| 11 | response.sendRedirect(services.csrf.getResubmissionURL()) | ||
| 12 | } else { | ||
| 13 | changePagesAuthor(request.getParameterValues('pagelist')) | ||
| 14 | } | ||
| 15 | |||
| 16 | def getXClassPageInstances(xClass) { | ||
| 17 | def templateName = xClass.replace('Class', '') + 'Template' | ||
| 18 | def list = services.query.hql("select distinct doc.fullName from XWikiDocument doc, BaseObject as obj where obj.name = doc.fullName and obj.className = '$xClass' and doc.fullName != '$templateName'").execute() | ||
| 19 | return list | ||
| 20 | } | ||
| 21 | |||
| 22 | def getTranslationPages(String scope) { | ||
| 23 | def query = services.query.hql("select distinct doc.fullName from XWikiDocument doc, BaseObject as obj, StringProperty as prop where obj.name = doc.fullName and obj.className = :class and prop.id.id = obj.id and prop.id.name = 'scope' and prop.value = :scope ") | ||
| 24 | query.bindValue('class', 'XWiki.TranslationDocumentClass') | ||
| 25 | query.bindValue('scope', scope) | ||
| 26 | return query.execute() | ||
| 27 | } | ||
| 28 | |||
| 29 | def getJsxOrSsxPages(String scope) { | ||
| 30 | def query = services.query.hql("select distinct doc.fullName from XWikiDocument doc, BaseObject as obj, StringProperty as prop where obj.name = doc.fullName and (obj.className = :class1 or obj.className = :class2) and prop.id.id = obj.id and prop.id.name = 'use' and prop.value = :scope ") | ||
| 31 | query.bindValue('class1', 'XWiki.JavaScriptExtension') | ||
| 32 | query.bindValue('class2', 'XWiki.StyleSheetExtension') | ||
| 33 | query.bindValue('scope', scope) | ||
| 34 | return query.execute() | ||
| 35 | } | ||
| 36 | |||
| 37 | def getScriptedJsxOrSsxPages() { | ||
| 38 | def query = services.query.hql("select distinct doc.fullName from XWikiDocument doc, BaseObject as obj, IntegerProperty as prop where obj.name = doc.fullName and (obj.className = :class1 or obj.className = :class2) and prop.id.id = obj.id and prop.id.name = 'parse' and prop.value = 1 ") | ||
| 39 | query.bindValue('class1', 'XWiki.JavaScriptExtension') | ||
| 40 | query.bindValue('class2', 'XWiki.StyleSheetExtension') | ||
| 41 | return query.execute() | ||
| 42 | } | ||
| 43 | |||
| 44 | def getPagesWithScriptedContent(scriptType) { | ||
| 45 | def query = services.query.hql("select distinct doc.fullName from XWikiDocument doc where doc.content like :like") | ||
| 46 | // Trick to not declare a script which would get interpreted within this page content | ||
| 47 | def like = '%{{:tag}}%{{/:tag}}%' | ||
| 48 | like = like.replaceAll(':tag', scriptType) | ||
| 49 | query.bindValue('like', like) | ||
| 50 | return query.execute() | ||
| 51 | } | ||
| 52 | |||
| 53 | def hasRight(EntityReference reference, Right right) { | ||
| 54 | def page = xwiki.getDocument(reference) | ||
| 55 | if (page != null) { | ||
| 56 | def authorReference = page.getAuthorReference() | ||
| 57 | def hasAccess = services.security.authorization.hasAccess(right, authorReference, reference) | ||
| 58 | return hasAccess | ||
| 59 | } else { | ||
| 60 | // This seems to happen sometimes when an error occurred on target wiki initialization | ||
| 61 | out.println("{{warning}}An error occurred when retrieving page $reference.{{/warning}}") | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | def pageListPerWiki() | ||
| 66 | { | ||
| 67 | |||
| 68 | def wikilist = [ "xwiki" ] | ||
| 69 | if (!xwiki.metaClass.respondsTo(xwiki, 'isVirtualMode') || xwiki.isVirtualMode()) { | ||
| 70 | def wikiManager = xwiki.getPlugin("wikimanager") | ||
| 71 | if (wikiManager != null) { | ||
| 72 | wikilist = wikiManager.getAllWikis() | ||
| 73 | } else { | ||
| 74 | wikilist = services.wiki?.getAll().collect({ wiki -> wiki.id }) | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | def knownpageslist = [ | ||
| 79 | "AnnotationCode.Script", | ||
| 80 | "AnnotationCode.Settings", | ||
| 81 | "AnnotationCode.Style", | ||
| 82 | "Main.Activity", | ||
| 83 | "Main.SpaceIndex", | ||
| 84 | "Main.Spaces", | ||
| 85 | "XWiki.AllAttachments", | ||
| 86 | ]; | ||
| 87 | |||
| 88 | def context2 = xcontext.getContext() | ||
| 89 | def db = context2.getDatabase() | ||
| 90 | |||
| 91 | def wikiPagelists = [:] | ||
| 92 | |||
| 93 | try { | ||
| 94 | for (wiki in wikilist) { | ||
| 95 | def wikiname = wiki.toString() | ||
| 96 | context2.setDatabase(wikiname) | ||
| 97 | // get all the pages which have a required rights object | ||
| 98 | // attached to them, that specifies either edit or | ||
| 99 | // programming | ||
| 100 | def pagelist = services.query.xwql("select distinct doc.fullName from Document doc, doc.object(XWiki.RequiredRightClass) as reqRight where reqRight.level = 'programming' or reqRight.level = 'edit'").execute() | ||
| 101 | // add the known pages that need to be saved with programming | ||
| 102 | // || edit rights to the list of pages, if they don't have the ' | ||
| 103 | // required rights object | ||
| 104 | for (knownpage in knownpageslist) { | ||
| 105 | if (!pagelist.contains(knownpage) && xwiki.exists(knownpage)) { | ||
| 106 | pagelist.add(knownpage) | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | // Create map linking rights to page lists | ||
| 111 | wikiPagelists[wikiname] = [:] | ||
| 112 | |||
| 113 | /** Get pages requiring programming rights */ | ||
| 114 | |||
| 115 | // Add all XClass instances which are likely to need programming rights: | ||
| 116 | // - WikiComponents generally use Groovy scripts | ||
| 117 | // - SchedulerJobs use either a Groovy script or a Java class | ||
| 118 | ['XWiki.ComponentClass', 'XWiki.SchedulerJobClass'].each{ | ||
| 119 | it -> pagelist.addAll(getXClassPageInstances(it)) | ||
| 120 | } | ||
| 121 | |||
| 122 | // Also add all pages containing Groovy code in their content (they may need to be unchecked in case | ||
| 123 | // it's actually code documentation, not real code) | ||
| 124 | pagelist.addAll(getPagesWithScriptedContent('groovy')) | ||
| 125 | |||
| 126 | // Add translation bundles with global scope since they require programming right | ||
| 127 | // ref: DocumentTranslationBundleFactory#checkRegistrationAuthorization | ||
| 128 | pagelist.addAll(getTranslationPages('GLOBAL')) | ||
| 129 | // JSX or SSX pages which are always loaded require programming right | ||
| 130 | // ref: AbstractDocumentSkinExtensionPlugin#getAlwaysUsedExtensions | ||
| 131 | pagelist.addAll(getJsxOrSsxPages('always')) | ||
| 132 | |||
| 133 | def rs = xwiki.getXWiki().getRightService() | ||
| 134 | |||
| 135 | wikiPagelists[wikiname]['programming'] = pagelist.findAll{ | ||
| 136 | name -> !rs.hasProgrammingRights(xwiki.getDocument(name).getDocument(), context2) || | ||
| 137 | needsRequiredRightsObj(xwiki.getDocument(name)) | ||
| 138 | } | ||
| 139 | |||
| 140 | /** Get pages requiring script right */ | ||
| 141 | |||
| 142 | // Using a Set to avoid duplicates | ||
| 143 | wikiPagelists[wikiname]['script'] = new HashSet() | ||
| 144 | |||
| 145 | // Macros, UIExtensions and Skins typically contain Velocity code | ||
| 146 | ['XWiki.WikiMacroClass', 'XWiki.UIExtensionClass', 'XWiki.XWikiSkins'].each{ | ||
| 147 | it -> wikiPagelists[wikiname]['script'].addAll(getXClassPageInstances(it)) | ||
| 148 | } | ||
| 149 | |||
| 150 | wikiPagelists[wikiname]['script'].addAll(getScriptedJsxOrSsxPages()) | ||
| 151 | |||
| 152 | // Add all pages containing a Velocity script saved by a user lacking script right | ||
| 153 | wikiPagelists[wikiname]['script'].addAll(getPagesWithScriptedContent('velocity')) | ||
| 154 | wikiPagelists[wikiname]['script'] = wikiPagelists[wikiname]['script'].findAll{ | ||
| 155 | pageId -> | ||
| 156 | def reference = services.model.resolveDocument(wikiname + ':' + pageId) | ||
| 157 | return !hasRight(reference, Right.SCRIPT) | ||
| 158 | } | ||
| 159 | |||
| 160 | /** Get pages likely to require admin right */ | ||
| 161 | |||
| 162 | wikiPagelists[wikiname]['admin'] = new HashSet() | ||
| 163 | |||
| 164 | // Add translation documents with wiki scope | ||
| 165 | wikiPagelists[wikiname]['admin'].addAll(getTranslationPages('WIKI')) | ||
| 166 | wikiPagelists[wikiname]['admin'] = wikiPagelists[wikiname]['admin'].findAll{ | ||
| 167 | pageId -> | ||
| 168 | def reference = services.model.resolveDocument(wikiname + ':' + pageId) | ||
| 169 | return !hasRight(reference, Right.ADMIN) | ||
| 170 | } | ||
| 171 | |||
| 172 | } | ||
| 173 | } finally { | ||
| 174 | context2.setDatabase(db) | ||
| 175 | } | ||
| 176 | |||
| 177 | return wikiPagelists | ||
| 178 | } | ||
| 179 | |||
| 180 | def printForm(pagelistPerWiki) | ||
| 181 | { | ||
| 182 | |||
| 183 | println "{{info}} You need to run this with a user that has programming rights, otherwise it will have no effect. {{/info}}\n\n" | ||
| 184 | |||
| 185 | println("""\ | ||
| 186 | {{html}} | ||
| 187 | <span class=\"buttonwrapper\"> | ||
| 188 | <a id=\"select_all_all_wikis\" class=\"select_all\">select all in all wikis</a> | ||
| 189 | </span> | ||
| 190 | <span class=\"buttonwrapper\"> | ||
| 191 | <a id=\"unselect_all_all_wikis\" class=\"unselect_all\">select none</a> | ||
| 192 | </span> | ||
| 193 | <form action=\"${xwiki.getURL(doc.getFullName())}\" method=\"post\" > | ||
| 194 | <input type=\"hidden\" name=\"form_token\" value=\"${services.csrf.token}\" /> | ||
| 195 | """) | ||
| 196 | |||
| 197 | for (wikiname in pagelistPerWiki.keySet()) { | ||
| 198 | if (pagelistPerWiki.get(wikiname).size() == 0) { | ||
| 199 | continue | ||
| 200 | } | ||
| 201 | println("<fieldset id=\"pagelist:${wikiname}\" class=\"pagelist\" >") | ||
| 202 | println("<h2><legend class=\"wikiname\">Wiki: ${wikiname}</legend></h2>") | ||
| 203 | println("""\ | ||
| 204 | <div class=\"select_checkbox_control\"> | ||
| 205 | <span class=\"buttonwrapper\"> | ||
| 206 | <a id=\"select_all:${wikiname}\" class=\"select_all\">select all</a> | ||
| 207 | </span> | ||
| 208 | <span class=\"buttonwrapper\"> | ||
| 209 | <a id=\"unselect_all:${wikiname}\" class=\"unselect_all\">select none</a> | ||
| 210 | </span> | ||
| 211 | </div> | ||
| 212 | """) | ||
| 213 | println("<div>") | ||
| 214 | def rights = ['programming', 'admin', 'script'] | ||
| 215 | rights.each{ right -> | ||
| 216 | def str = right.capitalize() | ||
| 217 | println("<h2> $str right </h2>") | ||
| 218 | for (page in pagelistPerWiki.get(wikiname).get(right)) { | ||
| 219 | println("""\ | ||
| 220 | <div class=\"page_checkbox_item\"> | ||
| 221 | <input type=\"checkbox\" name=\"pagelist\" id=\"page_${wikiname}_${page}\" | ||
| 222 | class=\"${wikiname}_page\" value=\"${wikiname}:${page}\"/> | ||
| 223 | <a href=\"${xwiki.getURL(wikiname + ':' + page)}?viewer=code\">${page}</a> | ||
| 224 | </div> | ||
| 225 | """) | ||
| 226 | } | ||
| 227 | } | ||
| 228 | println("</div>") | ||
| 229 | println("</fieldset>") | ||
| 230 | } | ||
| 231 | |||
| 232 | println("""\ | ||
| 233 | <hr /> | ||
| 234 | <span class=\"buttonwrapper\"> | ||
| 235 | <input class=\"button\" type=\"submit\" value=\"save with programming rights\" /> | ||
| 236 | </span> | ||
| 237 | </form> | ||
| 238 | {{/html}} | ||
| 239 | """) | ||
| 240 | |||
| 241 | } | ||
| 242 | |||
| 243 | def needsRequiredRightsObj(prDoc) | ||
| 244 | { | ||
| 245 | def objList = prDoc.getObjects('XWiki.RequiredRightClass'); | ||
| 246 | return objList == null || objList.findAll{ | ||
| 247 | obj -> obj.getProperty('level').getValue() == 'programming' || | ||
| 248 | obj.getProperty('level').getValue() == 'edit' | ||
| 249 | }.size() == 0 | ||
| 250 | } | ||
| 251 | |||
| 252 | def changePagesAuthor(pagelist) { | ||
| 253 | for (page in pagelist) { | ||
| 254 | def prDoc = xwiki.getDocument(page) | ||
| 255 | try { | ||
| 256 | def protectedDoc = prDoc.document | ||
| 257 | // Update the page content author in order to fix programming rights. | ||
| 258 | protectedDoc.setContentAuthorReference(xcontext.getContext().getUserReference()) | ||
| 259 | // We need to change the page author in order to have a history about the user who executed this script otherwise the last author will be kept. | ||
| 260 | protectedDoc.setAuthorReference(xcontext.getContext().getUserReference()) | ||
| 261 | xwiki.getXWiki().saveDocument(protectedDoc, "Changed the author of the page to fix programming rights", xcontext.context) | ||
| 262 | println " * Successfully changed the author of the page '${page}'" | ||
| 263 | } catch (Exception e) { | ||
| 264 | println " * Failed to change the author of the page '${page}': " + e | ||
| 265 | } | ||
| 266 | } | ||
| 267 | } | ||
| 268 | {{/groovy}} | ||
| 269 |