Исходный код вики Check programming rights

Редактировал(а) Dmitriy Rybakov 05.08.2025, 13:07

Последние авторы
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}}