[wiki-lenny/share/] Hacking sauvage de MoinMoin fonctionnel

Plusieurs fichiers ont été sauvagement hacké pour que MoiMoin ait le
comportement que l'on attend de lui au Cr@ns.

 * PageGraphicalEditor.py --- Pour interdire l'éditeur graphique sur
                              certains pages
 * action/newaccount.py --- Pour interdre la création de compte depuis
                            l'extérieur
 * formatter/__init__.py --- Pour pouvoir afficher des boîtes dans les
                             différents formatters...
 * formatter/text_html.py --- ... ce qui n'est implémenté que dans le
                              formatteur html pour l'instant.
 * security/__init__.py --- Pour n'autoriser l'accès en lecture qu'aux
                            pages publiques depuis l'extérieur.

darcs-hash:20081109154331-bd074-0c8a84ce7016e8a1ebe63795d377fe91065cb0b8.gz
This commit is contained in:
Antoine Durand-Gasselin 2008-11-09 16:43:31 +01:00
parent 73e1af5309
commit cd5d080e49
10 changed files with 4410 additions and 0 deletions

View file

@ -0,0 +1,411 @@
# -*- coding: iso-8859-1 -*-
"""
MoinMoin - Call the GUI editor (FCKeditor)
Same as PageEditor, but we use the HTML based GUI editor here.
TODO:
* see PageEditor.py
@copyright: 2006 Bastian Blank, Florian Festi,
2006-2007 MoinMoin:ThomasWaldmann
@license: GNU GPL, see COPYING for details.
"""
import re
from MoinMoin import PageEditor
from MoinMoin import wikiutil
from MoinMoin.Page import Page
from MoinMoin.widget import html
from MoinMoin.widget.dialog import Status
from MoinMoin.util import web
from MoinMoin.parser.text_moin_wiki import Parser as WikiParser
def execute(pagename, request):
if not request.user.may.write(pagename):
_ = request.getText
request.theme.add_msg_('You are not allowed to edit this page.', "error")
Page(request, pagename).send_page()
return
PageGraphicalEditor(request, pagename).sendEditor()
class PageGraphicalEditor(PageEditor.PageEditor):
""" Same as PageEditor, but use the GUI editor (FCKeditor) """
def word_rule(self):
regex = re.compile(r"\(\?<![^)]*?\)")
word_rule = regex.sub("", WikiParser.word_rule_js)
return repr(word_rule)[1:]
def sendEditor(self, **kw):
""" Send the editor form page.
@keyword preview: if given, show this text in preview mode
@keyword staytop: don't go to #preview
@keyword comment: comment field (when preview is true)
"""
from MoinMoin import i18n
from MoinMoin.action import SpellCheck
request = self.request
form = request.form
_ = self._
request.disableHttpCaching(level=2)
request.emit_http_headers()
raw_body = ''
msg = None
conflict_msg = None
edit_lock_message = None
preview = kw.get('preview', None)
staytop = kw.get('staytop', 0)
# check edit permissions
if not request.user.may.write(self.page_name):
msg = _('You are not allowed to edit this page.')
elif not self.isWritable():
msg = _('Page is immutable!')
elif self.rev:
# Trying to edit an old version, this is not possible via
# the web interface, but catch it just in case...
msg = _('Cannot edit old revisions!')
else:
# try to acquire edit lock
ok, edit_lock_message = self.lock.acquire()
if not ok:
# failed to get the lock
if preview is not None:
edit_lock_message = _('The lock you held timed out. Be prepared for editing conflicts!'
) + "<br>" + edit_lock_message
else:
msg = edit_lock_message
# Did one of the prechecks fail?
if msg:
request.theme.add_msg(msg, "error")
self.send_page()
return
# check if we want to load a draft
use_draft = None
if 'button_load_draft' in form:
wanted_draft_timestamp = int(form.get('draft_ts', ['0'])[0])
if wanted_draft_timestamp:
draft = self._load_draft()
if draft is not None:
draft_timestamp, draft_rev, draft_text = draft
if draft_timestamp == wanted_draft_timestamp:
use_draft = draft_text
# Check for draft / normal / preview submit
if use_draft is not None:
title = _('Draft of "%(pagename)s"')
# Propagate original revision
rev = int(form['draft_rev'][0])
self.set_raw_body(use_draft, modified=1)
preview = use_draft
elif preview is None:
title = _('Edit "%(pagename)s"')
else:
title = _('Preview of "%(pagename)s"')
# Propagate original revision
rev = request.rev
self.set_raw_body(preview, modified=1)
# send header stuff
lock_timeout = self.lock.timeout / 60
lock_page = wikiutil.escape(self.page_name, quote=1)
lock_expire = _("Your edit lock on %(lock_page)s has expired!") % {'lock_page': lock_page}
lock_mins = _("Your edit lock on %(lock_page)s will expire in # minutes.") % {'lock_page': lock_page}
lock_secs = _("Your edit lock on %(lock_page)s will expire in # seconds.") % {'lock_page': lock_page}
# get request parameters
try:
text_rows = int(form['rows'][0])
except StandardError:
text_rows = self.cfg.edit_rows
if request.user.valid:
text_rows = int(request.user.edit_rows)
if preview is not None:
# Check for editing conflicts
if not self.exists():
# page does not exist, are we creating it?
if rev:
conflict_msg = _('Someone else deleted this page while you were editing!')
elif rev != self.current_rev():
conflict_msg = _('Someone else changed this page while you were editing!')
if self.mergeEditConflict(rev):
conflict_msg = _("""Someone else saved this page while you were editing!
Please review the page and save then. Do not save this page as it is!""")
rev = self.current_rev()
if conflict_msg:
# We don't show preview when in conflict
preview = None
elif self.exists():
# revision of existing page
rev = self.current_rev()
else:
# page creation
rev = 0
self.setConflict(bool(conflict_msg))
# Page editing is done using user language
request.setContentLanguage(request.lang)
# Get the text body for the editor field.
# TODO: what about deleted pages? show the text of the last revision or use the template?
if preview is not None:
raw_body = self.get_raw_body()
if use_draft:
request.write(_("[Content loaded from draft]"), '<br>')
elif self.exists():
# If the page exists, we get the text from the page.
# TODO: maybe warn if template argument was ignored because the page exists?
raw_body = self.get_raw_body()
elif 'template' in form:
# If the page does not exist, we try to get the content from the template parameter.
template_page = wikiutil.unquoteWikiname(form['template'][0])
if request.user.may.read(template_page):
raw_body = Page(request, template_page).get_raw_body()
if raw_body:
request.write(_("[Content of new page loaded from %s]") % (template_page, ), '<br>')
else:
request.write(_("[Template %s not found]") % (template_page, ), '<br>')
else:
request.write(_("[You may not read %s]") % (template_page, ), '<br>')
# Make backup on previews - but not for new empty pages
if not use_draft and preview and raw_body:
self._save_draft(raw_body, rev)
draft_message = None
loadable_draft = False
if preview is None:
draft = self._load_draft()
if draft is not None:
draft_timestamp, draft_rev, draft_text = draft
if draft_text != raw_body:
loadable_draft = True
page_rev = rev
draft_timestamp_str = request.user.getFormattedDateTime(draft_timestamp)
draft_message = _(u"'''<<BR>>Your draft based on revision %(draft_rev)d (saved %(draft_timestamp_str)s) can be loaded instead of the current revision %(page_rev)d by using the load draft button - in case you lost your last edit somehow without saving it.''' A draft gets saved for you when you do a preview, cancel an edit or unsuccessfully save.", wiki=True, percent=True) % locals()
# Setup status message
status = [kw.get('msg', ''), conflict_msg, edit_lock_message, draft_message]
status = [msg for msg in status if msg]
status = ' '.join(status)
status = Status(request, content=status)
request.theme.add_msg(status, "error")
request.theme.send_title(
title % {'pagename': self.split_title(), },
page=self,
html_head=self.lock.locktype and (
PageEditor._countdown_js % {
'countdown_script': request.theme.externalScript('countdown'),
'lock_timeout': lock_timeout,
'lock_expire': lock_expire,
'lock_mins': lock_mins,
'lock_secs': lock_secs,
}) or '',
editor_mode=1,
)
request.write(request.formatter.startContent("content"))
# Generate default content for new pages
if not raw_body:
raw_body = _('Describe %s here.') % (self.page_name, )
# send form
request.write('<form id="editor" method="post" action="%s/%s#preview">' % (
request.getScriptname(),
wikiutil.quoteWikinameURL(self.page_name),
))
# yet another weird workaround for broken IE6 (it expands the text
# editor area to the right after you begin to type...). IE sucks...
# http://fplanque.net/2003/Articles/iecsstextarea/
request.write('<fieldset style="border:none;padding:0;">')
request.write(unicode(html.INPUT(type="hidden", name="action", value="edit")))
# Send revision of the page our edit is based on
request.write('<input type="hidden" name="rev" value="%d">' % (rev, ))
# Add src format (e.g. 'wiki') into a hidden form field, so that
# we can load the correct converter after POSTing.
request.write('<input type="hidden" name="format" value="%s">' % self.pi['format'])
# Create and send a ticket, so we can check the POST
request.write('<input type="hidden" name="ticket" value="%s">' % wikiutil.createTicket(request))
# Save backto in a hidden input
backto = form.get('backto', [None])[0]
if backto:
request.write(unicode(html.INPUT(type="hidden", name="backto", value=backto)))
# button bar
button_spellcheck = '<input class="button" type="submit" name="button_spellcheck" value="%s">' % _('Check Spelling')
save_button_text = _('Save Changes')
cancel_button_text = _('Cancel')
if self.cfg.page_license_enabled:
request.write('<p><em>', _(
"""By hitting '''%(save_button_text)s''' you put your changes under the %(license_link)s.
If you don't want that, hit '''%(cancel_button_text)s''' to cancel your changes.""", wiki=True) % {
'save_button_text': save_button_text,
'cancel_button_text': cancel_button_text,
'license_link': wikiutil.getLocalizedPage(request, self.cfg.page_license_page).link_to(request),
}, '</em></p>')
request.write('''
<input class="button" type="submit" name="button_save" value="%s">
<input class="button" type="submit" name="button_preview" value="%s">
<input class="button" type="submit" name="button_switch" value="%s">
''' % (save_button_text, _('Preview'), _('Text mode'), ))
if loadable_draft:
request.write('''
<input class="button" type="submit" name="button_load_draft" value="%s" onClick="flgChange = false;">
<input type="hidden" name="draft_ts" value="%d">
<input type="hidden" name="draft_rev" value="%d">
''' % (_('Load Draft'), draft_timestamp, draft_rev))
request.write('''
%s
<input class="button" type="submit" name="button_cancel" value="%s">
<input type="hidden" name="editor" value="gui">
''' % (button_spellcheck, cancel_button_text, ))
if self.cfg.mail_enabled:
request.write('''
<script type="text/javascript">
function toggle_trivial(CheckedBox)
{
TrivialBoxes = document.getElementsByName("trivial");
for (var i = 0; i < TrivialBoxes.length; i++)
TrivialBoxes[i].checked = CheckedBox.checked;
}
</script>
&nbsp;
<input type="checkbox" name="trivial" id="chktrivialtop" value="1" %(checked)s onclick="toggle_trivial(this)">
<label for="chktrivialtop">%(label)s</label>
''' % {
'checked': ('', 'checked')[form.get('trivial', ['0'])[0] == '1'],
'label': _("Trivial change"),
})
from MoinMoin.security.textcha import TextCha
request.write(TextCha(request).render())
self.sendconfirmleaving() # TODO update state of flgChange to make this work, see PageEditor
# Add textarea with page text
lang = self.pi.get('language', request.cfg.language_default)
contentlangdirection = i18n.getDirection(lang) # 'ltr' or 'rtl'
uilanguage = request.lang
url_prefix_static = request.cfg.url_prefix_static
url_prefix_local = request.cfg.url_prefix_local
wikipage = wikiutil.quoteWikinameURL(self.page_name)
fckbasepath = url_prefix_local + '/applets/FCKeditor'
wikiurl = request.getScriptname()
if not wikiurl or wikiurl[-1] != '/':
wikiurl += '/'
themepath = '%s/%s' % (url_prefix_static, request.theme.name)
smileypath = themepath + '/img'
# auto-generating a list for SmileyImages does NOT work from here!
editor_size = int(request.user.edit_rows) * 22 # 22 height_pixels/line
word_rule = self.word_rule()
request.write("""
<script type="text/javascript" src="%(fckbasepath)s/fckeditor.js"></script>
<script type="text/javascript">
<!--
var oFCKeditor = new FCKeditor( 'savetext', '100%%', %(editor_size)s, 'MoinDefault' ) ;
oFCKeditor.BasePath= '%(fckbasepath)s/' ;
oFCKeditor.Config['WikiBasePath'] = '%(wikiurl)s' ;
oFCKeditor.Config['WikiPage'] = '%(wikipage)s' ;
oFCKeditor.Config['PluginsPath'] = '%(url_prefix_local)s/applets/moinFCKplugins/' ;
oFCKeditor.Config['CustomConfigurationsPath'] = '%(url_prefix_local)s/applets/moinfckconfig.js' ;
oFCKeditor.Config['WordRule'] = %(word_rule)s ;
oFCKeditor.Config['SmileyPath'] = '%(smileypath)s/' ;
oFCKeditor.Config['EditorAreaCSS'] = '%(themepath)s/css/common.css' ;
oFCKeditor.Config['SkinPath'] = '%(fckbasepath)s/editor/skins/silver/' ;
oFCKeditor.Config['AutoDetectLanguage'] = false ;
oFCKeditor.Config['DefaultLanguage'] = '%(uilanguage)s' ;
oFCKeditor.Config['ContentLangDirection'] = '%(contentlangdirection)s' ;
oFCKeditor.Value= """ % locals())
from MoinMoin.formatter.text_gedit import Formatter
self.formatter = Formatter(request)
self.formatter.page = self
output = request.redirectedOutput(self.send_page_content, request, raw_body, format=self.pi['format'], do_cache=False)
output = repr(output)
if output[0] == 'u':
output = output[1:]
request.write(output)
request.write(""" ;
oFCKeditor.Create() ;
//-->
</script>
""")
request.write("<p>")
request.write(_("Comment:"),
' <input id="editor-comment" type="text" name="comment" value="%s" size="80" maxlength="200">' % (
wikiutil.escape(kw.get('comment', ''), 1), ))
request.write("</p>")
# Category selection
filterfn = self.cfg.cache.page_category_regexact.search
cat_pages = request.rootpage.getPageList(filter=filterfn)
cat_pages.sort()
cat_pages = [wikiutil.pagelinkmarkup(p) for p in cat_pages]
cat_pages.insert(0, ('', _('<No addition>')))
request.write("<p>")
request.write(_('Add to: %(category)s') % {
'category': unicode(web.makeSelection('category', cat_pages)),
})
if self.cfg.mail_enabled:
request.write('''
&nbsp;
<input type="checkbox" name="trivial" id="chktrivial" value="1" %(checked)s onclick="toggle_trivial(this)">
<label for="chktrivial">%(label)s</label> ''' % {
'checked': ('', 'checked')[form.get('trivial', ['0'])[0] == '1'],
'label': _("Trivial change"),
})
request.write('''
&nbsp;
<input type="checkbox" name="rstrip" id="chkrstrip" value="1" %(checked)s>
<label for="chkrstrip">%(label)s</label>
</p> ''' % {
'checked': ('', 'checked')[form.get('rstrip', ['0'])[0] == '1'],
'label': _('Remove trailing whitespace from each line')
})
request.write("</p>")
badwords_re = None
if preview is not None:
if 'button_spellcheck' in form or 'button_newwords' in form:
badwords, badwords_re, msg = SpellCheck.checkSpelling(self, request, own_form=0)
request.write("<p>%s</p>" % msg)
request.write('</fieldset>')
request.write("</form>")
if preview is not None:
if staytop:
content_id = 'previewbelow'
else:
content_id = 'preview'
self.send_page(content_id=content_id, content_only=1, hilite_re=badwords_re)
request.write(request.formatter.endContent()) # end content div
request.theme.send_footer(self.page_name)
request.theme.send_closing_html()

View file

@ -0,0 +1,428 @@
# -*- coding: utf-8 -*-
"""
MoinMoin - Call the GUI editor (FCKeditor)
Same as PageEditor, but we use the HTML based GUI editor here.
TODO:
* see PageEditor.py
@copyright: 2006 Bastian Blank, Florian Festi,
2006-2007 MoinMoin:ThomasWaldmann
@license: GNU GPL, see COPYING for details.
"""
import re
from MoinMoin import PageEditor
from MoinMoin import wikiutil
from MoinMoin.Page import Page
from MoinMoin.widget import html
from MoinMoin.widget.dialog import Status
from MoinMoin.util import web
from MoinMoin.parser.text_moin_wiki import Parser as WikiParser
def execute(pagename, request):
if not request.user.may.write(pagename):
_ = request.getText
request.theme.add_msg_('You are not allowed to edit this page.', "error")
Page(request, pagename).send_page()
return
PageGraphicalEditor(request, pagename).sendEditor()
class PageGraphicalEditor(PageEditor.PageEditor):
""" Same as PageEditor, but use the GUI editor (FCKeditor) """
def word_rule(self):
regex = re.compile(r"\(\?<![^)]*?\)")
word_rule = regex.sub("", WikiParser.word_rule_js)
return repr(word_rule)[1:]
def sendEditor(self, **kw):
""" Send the editor form page.
@keyword preview: if given, show this text in preview mode
@keyword staytop: don't go to #preview
@keyword comment: comment field (when preview is true)
"""
from MoinMoin import i18n
from MoinMoin.action import SpellCheck
request = self.request
form = request.form
_ = self._
request.disableHttpCaching(level=2)
page_categories = self.getCategories(request)
raw_body = ''
msg = None
conflict_msg = None
edit_lock_message = None
preview = kw.get('preview', None)
staytop = kw.get('staytop', 0)
# check edit permissions
if not request.user.may.write(self.page_name):
# L'effet de bord dans le test empêche-t-il la ligne suivante
# d'être exécutée ?
msg = _('You are not allowed to edit this page.')
elif not self.isWritable():
# même remarque.
msg = _('Page is immutable!')
elif self.rev:
# Trying to edit an old version, this is not possible via
# the web interface, but catch it just in case...
msg = _('Cannot edit old revisions!')
elif u"CatégorieEditeurGraphiqueInterdit" in page_categories:
msg =_('Cannot edit this page with the graphical editor')
request.theme.add_msg(msg, "error")
from MoinMoin.PageEditor import PageTextEditor
# Pour éviter à l'utilisateur de rester sur le cul, on le
# redirige vers l'éditeur texte
pg = PageTextEditor(request, self.page_name, **kw)
pg.sendEditor()
else:
# try to acquire edit lock
ok, edit_lock_message = self.lock.acquire()
if not ok:
# failed to get the lock
if preview is not None:
edit_lock_message = _('The lock you held timed out. Be prepared for editing conflicts!'
) + "<br>" + edit_lock_message
else:
msg = edit_lock_message
# Did one of the prechecks fail?
if msg:
request.theme.add_msg(msg, "error")
# Il est manifestement hors de question d'avoir envoyé les
# headers http avant d'appeler cette fonction
self.send_page()
return
# par contre, il faut les envoyer dans le cas contraire
request.emit_http_headers()
# check if we want to load a draft
use_draft = None
if 'button_load_draft' in form:
wanted_draft_timestamp = int(form.get('draft_ts', ['0'])[0])
if wanted_draft_timestamp:
draft = self._load_draft()
if draft is not None:
draft_timestamp, draft_rev, draft_text = draft
if draft_timestamp == wanted_draft_timestamp:
use_draft = draft_text
# Check for draft / normal / preview submit
if use_draft is not None:
title = _('Draft of "%(pagename)s"')
# Propagate original revision
rev = int(form['draft_rev'][0])
self.set_raw_body(use_draft, modified=1)
preview = use_draft
elif preview is None:
title = _('Edit "%(pagename)s"')
else:
title = _('Preview of "%(pagename)s"')
# Propagate original revision
rev = request.rev
self.set_raw_body(preview, modified=1)
# send header stuff
lock_timeout = self.lock.timeout / 60
lock_page = wikiutil.escape(self.page_name, quote=1)
lock_expire = _("Your edit lock on %(lock_page)s has expired!") % {'lock_page': lock_page}
lock_mins = _("Your edit lock on %(lock_page)s will expire in # minutes.") % {'lock_page': lock_page}
lock_secs = _("Your edit lock on %(lock_page)s will expire in # seconds.") % {'lock_page': lock_page}
# get request parameters
try:
text_rows = int(form['rows'][0])
except StandardError:
text_rows = self.cfg.edit_rows
if request.user.valid:
text_rows = int(request.user.edit_rows)
if preview is not None:
# Check for editing conflicts
if not self.exists():
# page does not exist, are we creating it?
if rev:
conflict_msg = _('Someone else deleted this page while you were editing!')
elif rev != self.current_rev():
conflict_msg = _('Someone else changed this page while you were editing!')
if self.mergeEditConflict(rev):
conflict_msg = _("""Someone else saved this page while you were editing!
Please review the page and save then. Do not save this page as it is!""")
rev = self.current_rev()
if conflict_msg:
# We don't show preview when in conflict
preview = None
elif self.exists():
# revision of existing page
rev = self.current_rev()
else:
# page creation
rev = 0
self.setConflict(bool(conflict_msg))
# Page editing is done using user language
request.setContentLanguage(request.lang)
# Get the text body for the editor field.
# TODO: what about deleted pages? show the text of the last revision or use the template?
if preview is not None:
raw_body = self.get_raw_body()
if use_draft:
request.write(_("[Content loaded from draft]"), '<br>')
elif self.exists():
# If the page exists, we get the text from the page.
# TODO: maybe warn if template argument was ignored because the page exists?
raw_body = self.get_raw_body()
elif 'template' in form:
# If the page does not exist, we try to get the content from the template parameter.
template_page = wikiutil.unquoteWikiname(form['template'][0])
if request.user.may.read(template_page):
raw_body = Page(request, template_page).get_raw_body()
if raw_body:
request.write(_("[Content of new page loaded from %s]") % (template_page, ), '<br>')
else:
request.write(_("[Template %s not found]") % (template_page, ), '<br>')
else:
request.write(_("[You may not read %s]") % (template_page, ), '<br>')
# Make backup on previews - but not for new empty pages
if not use_draft and preview and raw_body:
self._save_draft(raw_body, rev)
draft_message = None
loadable_draft = False
if preview is None:
draft = self._load_draft()
if draft is not None:
draft_timestamp, draft_rev, draft_text = draft
if draft_text != raw_body:
loadable_draft = True
page_rev = rev
draft_timestamp_str = request.user.getFormattedDateTime(draft_timestamp)
draft_message = _(u"'''<<BR>>Your draft based on revision %(draft_rev)d (saved %(draft_timestamp_str)s) can be loaded instead of the current revision %(page_rev)d by using the load draft button - in case you lost your last edit somehow without saving it.''' A draft gets saved for you when you do a preview, cancel an edit or unsuccessfully save.", wiki=True, percent=True) % locals()
# Setup status message
status = [kw.get('msg', ''), conflict_msg, edit_lock_message, draft_message]
status = [msg for msg in status if msg]
status = ' '.join(status)
status = Status(request, content=status)
request.theme.add_msg(status, "error")
request.theme.send_title(
title % {'pagename': self.split_title(), },
page=self,
html_head=self.lock.locktype and (
PageEditor._countdown_js % {
'countdown_script': request.theme.externalScript('countdown'),
'lock_timeout': lock_timeout,
'lock_expire': lock_expire,
'lock_mins': lock_mins,
'lock_secs': lock_secs,
}) or '',
editor_mode=1,
)
request.write(request.formatter.startContent("content"))
# Generate default content for new pages
if not raw_body:
raw_body = _('Describe %s here.') % (self.page_name, )
# send form
request.write('<form id="editor" method="post" action="%s/%s#preview">' % (
request.getScriptname(),
wikiutil.quoteWikinameURL(self.page_name),
))
# yet another weird workaround for broken IE6 (it expands the text
# editor area to the right after you begin to type...). IE sucks...
# http://fplanque.net/2003/Articles/iecsstextarea/
request.write('<fieldset style="border:none;padding:0;">')
request.write(unicode(html.INPUT(type="hidden", name="action", value="edit")))
# Send revision of the page our edit is based on
request.write('<input type="hidden" name="rev" value="%d">' % (rev, ))
# Add src format (e.g. 'wiki') into a hidden form field, so that
# we can load the correct converter after POSTing.
request.write('<input type="hidden" name="format" value="%s">' % self.pi['format'])
# Create and send a ticket, so we can check the POST
request.write('<input type="hidden" name="ticket" value="%s">' % wikiutil.createTicket(request))
# Save backto in a hidden input
backto = form.get('backto', [None])[0]
if backto:
request.write(unicode(html.INPUT(type="hidden", name="backto", value=backto)))
# button bar
button_spellcheck = '<input class="button" type="submit" name="button_spellcheck" value="%s">' % _('Check Spelling')
save_button_text = _('Save Changes')
cancel_button_text = _('Cancel')
if self.cfg.page_license_enabled:
request.write('<p><em>', _(
"""By hitting '''%(save_button_text)s''' you put your changes under the %(license_link)s.
If you don't want that, hit '''%(cancel_button_text)s''' to cancel your changes.""", wiki=True) % {
'save_button_text': save_button_text,
'cancel_button_text': cancel_button_text,
'license_link': wikiutil.getLocalizedPage(request, self.cfg.page_license_page).link_to(request),
}, '</em></p>')
request.write('''
<input class="button" type="submit" name="button_save" value="%s">
<input class="button" type="submit" name="button_preview" value="%s">
<input class="button" type="submit" name="button_switch" value="%s">
''' % (save_button_text, _('Preview'), _('Text mode'), ))
if loadable_draft:
request.write('''
<input class="button" type="submit" name="button_load_draft" value="%s" onClick="flgChange = false;">
<input type="hidden" name="draft_ts" value="%d">
<input type="hidden" name="draft_rev" value="%d">
''' % (_('Load Draft'), draft_timestamp, draft_rev))
request.write('''
%s
<input class="button" type="submit" name="button_cancel" value="%s">
<input type="hidden" name="editor" value="gui">
''' % (button_spellcheck, cancel_button_text, ))
if self.cfg.mail_enabled:
request.write('''
<script type="text/javascript">
function toggle_trivial(CheckedBox)
{
TrivialBoxes = document.getElementsByName("trivial");
for (var i = 0; i < TrivialBoxes.length; i++)
TrivialBoxes[i].checked = CheckedBox.checked;
}
</script>
&nbsp;
<input type="checkbox" name="trivial" id="chktrivialtop" value="1" %(checked)s onclick="toggle_trivial(this)">
<label for="chktrivialtop">%(label)s</label>
''' % {
'checked': ('', 'checked')[form.get('trivial', ['0'])[0] == '1'],
'label': _("Trivial change"),
})
from MoinMoin.security.textcha import TextCha
request.write(TextCha(request).render())
self.sendconfirmleaving() # TODO update state of flgChange to make this work, see PageEditor
# Add textarea with page text
lang = self.pi.get('language', request.cfg.language_default)
contentlangdirection = i18n.getDirection(lang) # 'ltr' or 'rtl'
uilanguage = request.lang
url_prefix_static = request.cfg.url_prefix_static
url_prefix_local = request.cfg.url_prefix_local
wikipage = wikiutil.quoteWikinameURL(self.page_name)
fckbasepath = url_prefix_local + '/applets/FCKeditor'
wikiurl = request.getScriptname()
if not wikiurl or wikiurl[-1] != '/':
wikiurl += '/'
themepath = '%s/%s' % (url_prefix_static, request.theme.name)
smileypath = themepath + '/img'
# auto-generating a list for SmileyImages does NOT work from here!
editor_size = int(request.user.edit_rows) * 22 # 22 height_pixels/line
word_rule = self.word_rule()
request.write("""
<script type="text/javascript" src="%(fckbasepath)s/fckeditor.js"></script>
<script type="text/javascript">
<!--
var oFCKeditor = new FCKeditor( 'savetext', '100%%', %(editor_size)s, 'MoinDefault' ) ;
oFCKeditor.BasePath= '%(fckbasepath)s/' ;
oFCKeditor.Config['WikiBasePath'] = '%(wikiurl)s' ;
oFCKeditor.Config['WikiPage'] = '%(wikipage)s' ;
oFCKeditor.Config['PluginsPath'] = '%(url_prefix_local)s/applets/moinFCKplugins/' ;
oFCKeditor.Config['CustomConfigurationsPath'] = '%(url_prefix_local)s/applets/moinfckconfig.js' ;
oFCKeditor.Config['WordRule'] = %(word_rule)s ;
oFCKeditor.Config['SmileyPath'] = '%(smileypath)s/' ;
oFCKeditor.Config['EditorAreaCSS'] = '%(themepath)s/css/common.css' ;
oFCKeditor.Config['SkinPath'] = '%(fckbasepath)s/editor/skins/silver/' ;
oFCKeditor.Config['AutoDetectLanguage'] = false ;
oFCKeditor.Config['DefaultLanguage'] = '%(uilanguage)s' ;
oFCKeditor.Config['ContentLangDirection'] = '%(contentlangdirection)s' ;
oFCKeditor.Value= """ % locals())
from MoinMoin.formatter.text_gedit import Formatter
self.formatter = Formatter(request)
self.formatter.page = self
output = request.redirectedOutput(self.send_page_content, request, raw_body, format=self.pi['format'], do_cache=False)
output = repr(output)
if output[0] == 'u':
output = output[1:]
request.write(output)
request.write(""" ;
oFCKeditor.Create() ;
//-->
</script>
""")
request.write("<p>")
request.write(_("Comment:"),
' <input id="editor-comment" type="text" name="comment" value="%s" size="80" maxlength="200">' % (
wikiutil.escape(kw.get('comment', ''), 1), ))
request.write("</p>")
# Category selection
filterfn = self.cfg.cache.page_category_regexact.search
cat_pages = request.rootpage.getPageList(filter=filterfn)
cat_pages.sort()
cat_pages = [wikiutil.pagelinkmarkup(p) for p in cat_pages]
cat_pages.insert(0, ('', _('<No addition>')))
request.write("<p>")
request.write(_('Add to: %(category)s') % {
'category': unicode(web.makeSelection('category', cat_pages)),
})
if self.cfg.mail_enabled:
request.write('''
&nbsp;
<input type="checkbox" name="trivial" id="chktrivial" value="1" %(checked)s onclick="toggle_trivial(this)">
<label for="chktrivial">%(label)s</label> ''' % {
'checked': ('', 'checked')[form.get('trivial', ['0'])[0] == '1'],
'label': _("Trivial change"),
})
request.write('''
&nbsp;
<input type="checkbox" name="rstrip" id="chkrstrip" value="1" %(checked)s>
<label for="chkrstrip">%(label)s</label>
</p> ''' % {
'checked': ('', 'checked')[form.get('rstrip', ['0'])[0] == '1'],
'label': _('Remove trailing whitespace from each line')
})
request.write("</p>")
badwords_re = None
if preview is not None:
if 'button_spellcheck' in form or 'button_newwords' in form:
badwords, badwords_re, msg = SpellCheck.checkSpelling(self, request, own_form=0)
request.write("<p>%s</p>" % msg)
request.write('</fieldset>')
request.write("</form>")
if preview is not None:
if staytop:
content_id = 'previewbelow'
else:
content_id = 'preview'
self.send_page(content_id=content_id, content_only=1, hilite_re=badwords_re)
request.write(request.formatter.endContent()) # end content div
request.theme.send_footer(self.page_name)
request.theme.send_closing_html()

View file

@ -0,0 +1,187 @@
# -*- coding: iso-8859-1 -*-
"""
MoinMoin - edit a page
This either calls the text or the GUI page editor.
@copyright: 2000-2004 Juergen Hermann <jh@web.de>,
2006 MoinMoin:ThomasWaldmann
@license: GNU GPL, see COPYING for details.
"""
from MoinMoin import wikiutil
from MoinMoin.Page import Page
def execute(pagename, request):
""" edit a page """
_ = request.getText
if 'button_preview' in request.form and 'button_spellcheck' in request.form:
# multiple buttons pressed at once? must be some spammer/bot
request.makeForbidden403()
request.surge_protect(kick_him=True) # get rid of him
return
if not request.user.may.write(pagename):
request.theme.add_msg(_('You are not allowed to edit this page.'), "error")
Page(request, pagename).send_page()
return
valideditors = ['text', 'gui', ]
editor = ''
if request.user.valid:
editor = request.user.editor_default
if editor not in valideditors:
editor = request.cfg.editor_default
editorparam = request.form.get('editor', [editor])[0]
if editorparam == "guipossible":
lasteditor = editor
elif editorparam == "textonly":
editor = lasteditor = 'text'
else:
editor = lasteditor = editorparam
if request.cfg.editor_force:
editor = request.cfg.editor_default
# if it is still nothing valid, we just use the text editor
if editor not in valideditors:
editor = 'text'
rev = request.rev or 0
savetext = request.form.get('savetext', [None])[0]
comment = request.form.get('comment', [u''])[0]
category = request.form.get('category', [None])[0]
rstrip = int(request.form.get('rstrip', ['0'])[0])
trivial = int(request.form.get('trivial', ['0'])[0])
if 'button_switch' in request.form:
if editor == 'text':
editor = 'gui'
else: # 'gui'
editor = 'text'
# load right editor class
if editor == 'gui':
from MoinMoin.PageGraphicalEditor import PageGraphicalEditor
pg = PageGraphicalEditor(request, pagename)
else: # 'text'
from MoinMoin.PageEditor import PageEditor
pg = PageEditor(request, pagename)
# is invoked without savetext start editing
if savetext is None or 'button_load_draft' in request.form:
pg.sendEditor()
return
# did user hit cancel button?
cancelled = 'button_cancel' in request.form
if request.cfg.edit_ticketing:
ticket = request.form.get('ticket', [''])[0]
if not wikiutil.checkTicket(request, ticket):
request.theme.add_msg(_('Please use the interactive user interface to use action %(actionname)s!') % {'actionname': 'edit' }, "error")
pg.send_page()
return
from MoinMoin.error import ConvertError
try:
if lasteditor == 'gui':
# convert input from Graphical editor
format = request.form.get('format', ['wiki'])[0]
if format == 'wiki':
converter_name = 'text_html_text_moin_wiki'
else:
converter_name = 'undefined' # XXX we don't have other converters yet
convert = wikiutil.importPlugin(request.cfg, "converter", converter_name, 'convert')
savetext = convert(request, pagename, savetext)
# IMPORTANT: normalize text from the form. This should be done in
# one place before we manipulate the text.
savetext = pg.normalizeText(savetext, stripspaces=rstrip)
except ConvertError:
# we don't want to throw an exception if user cancelled anyway
if not cancelled:
raise
if cancelled:
pg.sendCancel(savetext or "", rev)
pagedir = pg.getPagePath(check_create=0)
import os
if not os.listdir(pagedir):
os.removedirs(pagedir)
return
comment = wikiutil.clean_input(comment)
# Add category
# TODO: this code does not work with extended links, and is doing
# things behind your back, and in general not needed. Either we have
# a full interface for categories (add, delete) or just add them by
# markup.
if category and category != _('<No addition>'): # opera 8.5 needs this
# strip trailing whitespace
savetext = savetext.rstrip()
# Add category separator if last non-empty line contains
# non-categories.
lines = [line for line in savetext.splitlines() if line]
if lines:
#TODO: this code is broken, will not work for extended links
#categories, e.g ["category hebrew"]
categories = lines[-1].split()
if categories:
confirmed = wikiutil.filterCategoryPages(request, categories)
if len(confirmed) < len(categories):
# This was not a categories line, add separator
savetext += u'\n----\n'
# Add new category
if savetext and savetext[-1] != u'\n':
savetext += ' '
savetext += category + u'\n' # Should end with newline!
# Preview, spellcheck or spellcheck add new words
if ('button_preview' in request.form or
'button_spellcheck' in request.form or
'button_newwords' in request.form):
pg.sendEditor(preview=savetext, comment=comment)
# Preview with mode switch
elif 'button_switch' in request.form:
pg.sendEditor(preview=savetext, comment=comment, staytop=1)
# Save new text
else:
try:
from MoinMoin.security.textcha import TextCha
if not TextCha(request).check_answer_from_form():
raise pg.SaveError(_('TextCha: Wrong answer! Go back and try again...'))
savemsg = pg.saveText(savetext, rev, trivial=trivial, comment=comment)
except pg.EditConflict, e:
msg = e.message
# Handle conflict and send editor
pg.set_raw_body(savetext, modified=1)
pg.mergeEditConflict(rev)
# We don't send preview when we do merge conflict
pg.sendEditor(msg=msg, comment=comment)
return
except pg.SaveError, msg:
# msg contains a unicode string
savemsg = unicode(msg)
# Send new page after save or after unsuccessful conflict merge.
request.reset()
pg = Page(request, pagename)
# sets revision number to default for further actions
request.rev = 0
request.theme.add_msg(savemsg, "info")
pg.send_page()

View file

@ -0,0 +1,187 @@
# -*- coding: iso-8859-1 -*-
"""
MoinMoin - edit a page
This either calls the text or the GUI page editor.
@copyright: 2000-2004 Juergen Hermann <jh@web.de>,
2006 MoinMoin:ThomasWaldmann
@license: GNU GPL, see COPYING for details.
"""
from MoinMoin import wikiutil
from MoinMoin.Page import Page
def execute(pagename, request):
""" edit a page """
_ = request.getText
if 'button_preview' in request.form and 'button_spellcheck' in request.form:
# multiple buttons pressed at once? must be some spammer/bot
request.makeForbidden403()
request.surge_protect(kick_him=True) # get rid of him
return
if not request.user.may.write(pagename):
request.theme.add_msg(_('You are not allowed to edit this page.'), "error")
Page(request, pagename).send_page()
return
valideditors = ['text', 'gui', ]
editor = ''
if request.user.valid:
editor = request.user.editor_default
if editor not in valideditors:
editor = request.cfg.editor_default
editorparam = request.form.get('editor', [editor])[0]
if editorparam == "guipossible":
lasteditor = editor
elif editorparam == "textonly":
editor = lasteditor = 'text'
else:
editor = lasteditor = editorparam
if request.cfg.editor_force:
editor = request.cfg.editor_default
# if it is still nothing valid, we just use the text editor
if editor not in valideditors:
editor = 'text'
rev = request.rev or 0
savetext = request.form.get('savetext', [None])[0]
comment = request.form.get('comment', [u''])[0]
category = request.form.get('category', [None])[0]
rstrip = int(request.form.get('rstrip', ['0'])[0])
trivial = int(request.form.get('trivial', ['0'])[0])
if 'button_switch' in request.form:
if editor == 'text':
editor = 'gui'
else: # 'gui'
editor = 'text'
# load right editor class
if editor == 'gui':
from MoinMoin.PageGraphicalEditor import PageGraphicalEditor
pg = PageGraphicalEditor(request, pagename)
else: # 'text'
from MoinMoin.PageEditor import PageEditor
pg = PageEditor(request, pagename)
# is invoked without savetext start editing
if savetext is None or 'button_load_draft' in request.form:
pg.sendEditor()
return
# did user hit cancel button?
cancelled = 'button_cancel' in request.form
if request.cfg.edit_ticketing:
ticket = request.form.get('ticket', [''])[0]
if not wikiutil.checkTicket(request, ticket):
request.theme.add_msg(_('Please use the interactive user interface to use action %(actionname)s!') % {'actionname': 'edit' }, "error")
pg.send_page()
return
from MoinMoin.error import ConvertError
try:
if lasteditor == 'gui':
# convert input from Graphical editor
format = request.form.get('format', ['wiki'])[0]
if format == 'wiki':
converter_name = 'text_html_text_moin_wiki'
else:
converter_name = 'undefined' # XXX we don't have other converters yet
convert = wikiutil.importPlugin(request.cfg, "converter", converter_name, 'convert')
savetext = convert(request, pagename, savetext)
# IMPORTANT: normalize text from the form. This should be done in
# one place before we manipulate the text.
savetext = pg.normalizeText(savetext, stripspaces=rstrip)
except ConvertError:
# we don't want to throw an exception if user cancelled anyway
if not cancelled:
raise
if cancelled:
pg.sendCancel(savetext or "", rev)
pagedir = pg.getPagePath(check_create=0)
import os
if not os.listdir(pagedir):
os.removedirs(pagedir)
return
comment = wikiutil.clean_input(comment)
# Add category
# TODO: this code does not work with extended links, and is doing
# things behind your back, and in general not needed. Either we have
# a full interface for categories (add, delete) or just add them by
# markup.
if category and category != _('<No addition>'): # opera 8.5 needs this
# strip trailing whitespace
savetext = savetext.rstrip()
# Add category separator if last non-empty line contains
# non-categories.
lines = [line for line in savetext.splitlines() if line]
if lines:
#TODO: this code is broken, will not work for extended links
#categories, e.g ["category hebrew"]
categories = lines[-1].split()
if categories:
confirmed = wikiutil.filterCategoryPages(request, categories)
if len(confirmed) < len(categories):
# This was not a categories line, add separator
savetext += u'\n----\n'
# Add new category
if savetext and savetext[-1] != u'\n':
savetext += ' '
savetext += category + u'\n' # Should end with newline!
# Preview, spellcheck or spellcheck add new words
if ('button_preview' in request.form or
'button_spellcheck' in request.form or
'button_newwords' in request.form):
pg.sendEditor(preview=savetext, comment=comment)
# Preview with mode switch
elif 'button_switch' in request.form:
pg.sendEditor(preview=savetext, comment=comment, staytop=1)
# Save new text
else:
try:
from MoinMoin.security.textcha import TextCha
if not TextCha(request).check_answer_from_form():
raise pg.SaveError(_('TextCha: Wrong answer! Go back and try again...'))
savemsg = pg.saveText(savetext, rev, trivial=trivial, comment=comment)
except pg.EditConflict, e:
msg = e.message
# Handle conflict and send editor
pg.set_raw_body(savetext, modified=1)
pg.mergeEditConflict(rev)
# We don't send preview when we do merge conflict
pg.sendEditor(msg=msg, comment=comment)
return
except pg.SaveError, msg:
# msg contains a unicode string
savemsg = unicode(msg)
# Send new page after save or after unsuccessful conflict merge.
request.reset()
pg = Page(request, pagename)
# sets revision number to default for further actions
request.rev = 0
request.theme.add_msg(savemsg, "info")
pg.send_page()

View file

@ -0,0 +1,195 @@
# -*- coding: iso-8859-1 -*-
"""
MoinMoin - create account action
@copyright: 2007 MoinMoin:JohannesBerg
@license: GNU GPL, see COPYING for details.
"""
from MoinMoin import user, wikiutil, util
from MoinMoin.Page import Page
from MoinMoin.widget import html
from MoinMoin.security.textcha import TextCha
from MoinMoin.auth import MoinAuth
_debug = False
def _create_user(request):
_ = request.getText
form = request.form
if request.request_method != 'POST':
return
if not TextCha(request).check_answer_from_form():
return _('TextCha: Wrong answer! Go back and try again...')
# Create user profile
theuser = user.User(request, auth_method="new-user")
# Require non-empty name
try:
theuser.name = form['name'][0]
except KeyError:
return _("Empty user name. Please enter a user name.")
# Don't allow creating users with invalid names
if not user.isValidName(request, theuser.name):
return _("""Invalid user name {{{'%s'}}}.
Name may contain any Unicode alpha numeric character, with optional one
space between words. Group page name is not allowed.""", wiki=True) % wikiutil.escape(theuser.name)
# Name required to be unique. Check if name belong to another user.
if user.getUserId(request, theuser.name):
return _("This user name already belongs to somebody else.")
# try to get the password and pw repeat
password = form.get('password1', [''])[0]
password2 = form.get('password2', [''])[0]
# Check if password is given and matches with password repeat
if password != password2:
return _("Passwords don't match!")
if not password:
return _("Please specify a password!")
pw_checker = request.cfg.password_checker
if pw_checker:
pw_error = pw_checker(theuser.name, password)
if pw_error:
return _("Password not acceptable: %s") % pw_error
# Encode password
if password and not password.startswith('{SHA}'):
try:
theuser.enc_password = user.encodePassword(password)
except UnicodeError, err:
# Should never happen
return "Can't encode password: %s" % str(err)
# try to get the email, for new users it is required
email = wikiutil.clean_input(form.get('email', [''])[0])
theuser.email = email.strip()
if not theuser.email and 'email' not in request.cfg.user_form_remove:
return _("Please provide your email address. If you lose your"
" login information, you can get it by email.")
# Email should be unique - see also MoinMoin/script/accounts/moin_usercheck.py
if theuser.email and request.cfg.user_email_unique:
if user.get_by_email_address(request, theuser.email):
return _("This email already belongs to somebody else.")
# save data
theuser.save()
if form.has_key('create_and_mail'):
theuser.mailAccountData()
result = _("User account created! You can use this account to login now...")
if _debug:
result = result + util.dumpFormData(form)
return result
def _create_form(request):
_ = request.getText
url = request.page.url(request)
ret = html.FORM(action=url)
ret.append(html.INPUT(type='hidden', name='action', value='newaccount'))
lang_attr = request.theme.ui_lang_attr()
ret.append(html.Raw('<div class="userpref"%s>' % lang_attr))
tbl = html.TABLE(border="0")
ret.append(tbl)
ret.append(html.Raw('</div>'))
row = html.TR()
tbl.append(row)
row.append(html.TD().append(html.STRONG().append(
html.Text(_("Name")))))
cell = html.TD()
row.append(cell)
cell.append(html.INPUT(type="text", size="36", name="name"))
cell.append(html.Text(' ' + _("(Use FirstnameLastname)")))
row = html.TR()
tbl.append(row)
row.append(html.TD().append(html.STRONG().append(
html.Text(_("Password")))))
row.append(html.TD().append(html.INPUT(type="password", size="36",
name="password1")))
row = html.TR()
tbl.append(row)
row.append(html.TD().append(html.STRONG().append(
html.Text(_("Password repeat")))))
row.append(html.TD().append(html.INPUT(type="password", size="36",
name="password2")))
row = html.TR()
tbl.append(row)
row.append(html.TD().append(html.STRONG().append(html.Text(_("Email")))))
row.append(html.TD().append(html.INPUT(type="text", size="36",
name="email")))
textcha = TextCha(request)
if textcha.is_enabled():
row = html.TR()
tbl.append(row)
row.append(html.TD().append(html.STRONG().append(
html.Text(_('TextCha (required)')))))
td = html.TD()
if textcha:
td.append(textcha.render())
row.append(td)
row = html.TR()
tbl.append(row)
row.append(html.TD())
td = html.TD()
row.append(td)
td.append(html.INPUT(type="submit", name="create_only",
value=_('Create Profile')))
if request.cfg.mail_enabled:
td.append(html.Text(' '))
td.append(html.INPUT(type="submit", name="create_and_mail",
value="%s + %s" % (_('Create Profile'),
_('Email'))))
return unicode(ret)
def execute(pagename, request):
found = False
for auth in request.cfg.auth:
if isinstance(auth, MoinAuth):
found = True
break
if not found:
# we will not have linked, so forbid access
request.makeForbidden403()
return
page = Page(request, pagename)
_ = request.getText
form = request.form
submitted = form.has_key('create_only') or form.has_key('create_and_mail')
if submitted: # user pressed create button
request.theme.add_msg(_create_user(request), "dialog")
return page.send_page()
else: # show create form
request.emit_http_headers()
request.theme.send_title(_("Create Account"), pagename=pagename)
request.write(request.formatter.startContent("content"))
# THIS IS A BIG HACK. IT NEEDS TO BE CLEANED UP
request.write(_create_form(request))
request.write(request.formatter.endContent())
request.theme.send_footer(pagename)
request.theme.send_closing_html()

View file

@ -0,0 +1,207 @@
# -*- coding: iso-8859-1 -*-
"""
MoinMoin - create account action
@copyright: 2007 MoinMoin:JohannesBerg
@license: GNU GPL, see COPYING for details.
"""
from MoinMoin import user, wikiutil, util
from MoinMoin.Page import Page
from MoinMoin.widget import html
from MoinMoin.security.textcha import TextCha
from MoinMoin.auth import MoinAuth
_debug = False
def _create_user(request):
_ = request.getText
form = request.form
if request.request_method != 'POST':
return
if not TextCha(request).check_answer_from_form():
return _('TextCha: Wrong answer! Go back and try again...')
# Create user profile
theuser = user.User(request, auth_method="new-user")
# Require non-empty name
try:
theuser.name = form['name'][0]
except KeyError:
return _("Empty user name. Please enter a user name.")
# Don't allow creating users with invalid names
if not user.isValidName(request, theuser.name):
return _("""Invalid user name {{{'%s'}}}.
Name may contain any Unicode alpha numeric character, with optional one
space between words. Group page name is not allowed.""", wiki=True) % wikiutil.escape(theuser.name)
# Name required to be unique. Check if name belong to another user.
if user.getUserId(request, theuser.name):
return _("This user name already belongs to somebody else.")
# HACK SAUVAGE 1/1
# On interdit la création de compte aux gens qui ne sont pas en zone Cr@ns
import sys
sys.path.append('/usr/scripts/gestion/')
from iptools import is_crans
if not is_crans(self.request.remote_addr):
return _(u"""Création de compte impossible.
Pour des raisons de sécurité, la fonction de création d'un compte n'est
possible que depuis la zone CRANS. Si vous possédez un compte sur zamok,
vous pouvez y exécuter creer_compte_wiki.""")
# FIN HACK
# try to get the password and pw repeat
password = form.get('password1', [''])[0]
password2 = form.get('password2', [''])[0]
# Check if password is given and matches with password repeat
if password != password2:
return _("Passwords don't match!")
if not password:
return _("Please specify a password!")
pw_checker = request.cfg.password_checker
if pw_checker:
pw_error = pw_checker(theuser.name, password)
if pw_error:
return _("Password not acceptable: %s") % pw_error
# Encode password
if password and not password.startswith('{SHA}'):
try:
theuser.enc_password = user.encodePassword(password)
except UnicodeError, err:
# Should never happen
return "Can't encode password: %s" % str(err)
# try to get the email, for new users it is required
email = wikiutil.clean_input(form.get('email', [''])[0])
theuser.email = email.strip()
if not theuser.email and 'email' not in request.cfg.user_form_remove:
return _("Please provide your email address. If you lose your"
" login information, you can get it by email.")
# Email should be unique - see also MoinMoin/script/accounts/moin_usercheck.py
if theuser.email and request.cfg.user_email_unique:
if user.get_by_email_address(request, theuser.email):
return _("This email already belongs to somebody else.")
# save data
theuser.save()
if form.has_key('create_and_mail'):
theuser.mailAccountData()
result = _("User account created! You can use this account to login now...")
if _debug:
result = result + util.dumpFormData(form)
return result
def _create_form(request):
_ = request.getText
url = request.page.url(request)
ret = html.FORM(action=url)
ret.append(html.INPUT(type='hidden', name='action', value='newaccount'))
lang_attr = request.theme.ui_lang_attr()
ret.append(html.Raw('<div class="userpref"%s>' % lang_attr))
tbl = html.TABLE(border="0")
ret.append(tbl)
ret.append(html.Raw('</div>'))
row = html.TR()
tbl.append(row)
row.append(html.TD().append(html.STRONG().append(
html.Text(_("Name")))))
cell = html.TD()
row.append(cell)
cell.append(html.INPUT(type="text", size="36", name="name"))
cell.append(html.Text(' ' + _("(Use FirstnameLastname)")))
row = html.TR()
tbl.append(row)
row.append(html.TD().append(html.STRONG().append(
html.Text(_("Password")))))
row.append(html.TD().append(html.INPUT(type="password", size="36",
name="password1")))
row = html.TR()
tbl.append(row)
row.append(html.TD().append(html.STRONG().append(
html.Text(_("Password repeat")))))
row.append(html.TD().append(html.INPUT(type="password", size="36",
name="password2")))
row = html.TR()
tbl.append(row)
row.append(html.TD().append(html.STRONG().append(html.Text(_("Email")))))
row.append(html.TD().append(html.INPUT(type="text", size="36",
name="email")))
textcha = TextCha(request)
if textcha.is_enabled():
row = html.TR()
tbl.append(row)
row.append(html.TD().append(html.STRONG().append(
html.Text(_('TextCha (required)')))))
td = html.TD()
if textcha:
td.append(textcha.render())
row.append(td)
row = html.TR()
tbl.append(row)
row.append(html.TD())
td = html.TD()
row.append(td)
td.append(html.INPUT(type="submit", name="create_only",
value=_('Create Profile')))
if request.cfg.mail_enabled:
td.append(html.Text(' '))
td.append(html.INPUT(type="submit", name="create_and_mail",
value="%s + %s" % (_('Create Profile'),
_('Email'))))
return unicode(ret)
def execute(pagename, request):
found = False
for auth in request.cfg.auth:
if isinstance(auth, MoinAuth):
found = True
break
if not found:
# we will not have linked, so forbid access
request.makeForbidden403()
return
page = Page(request, pagename)
_ = request.getText
form = request.form
submitted = form.has_key('create_only') or form.has_key('create_and_mail')
if submitted: # user pressed create button
request.theme.add_msg(_create_user(request), "dialog")
return page.send_page()
else: # show create form
request.emit_http_headers()
request.theme.send_title(_("Create Account"), pagename=pagename)
request.write(request.formatter.startContent("content"))
# THIS IS A BIG HACK. IT NEEDS TO BE CLEANED UP
request.write(_create_form(request))
request.write(request.formatter.endContent())
request.theme.send_footer(pagename)
request.theme.send_closing_html()

View file

@ -0,0 +1,435 @@
# -*- coding: iso-8859-1 -*-
"""
MoinMoin - Formatter Package and FormatterBase
See "base.py" for the formatter interface.
@copyright: 2000-2004 by Juergen Hermann <jh@web.de>
@license: GNU GPL, see COPYING for details.
"""
import re
from MoinMoin import log
logging = log.getLogger(__name__)
from MoinMoin.util import pysupport
from MoinMoin import wikiutil
from MoinMoin.support.python_compatibility import rsplit
modules = pysupport.getPackageModules(__file__)
class FormatterBase:
""" This defines the output interface used all over the rest of the code.
Note that no other means should be used to generate _content_ output,
while navigational elements (HTML page header/footer) and the like
can be printed directly without violating output abstraction.
"""
hardspace = ' '
def __init__(self, request, **kw):
self.request = request
self._ = request.getText
self._store_pagelinks = kw.get('store_pagelinks', 0)
self._terse = kw.get('terse', 0)
self.pagelinks = []
self.in_p = 0
self.in_pre = 0
self._highlight_re = None
self._base_depth = 0
def set_highlight_re(self, hi_re=None):
""" set the highlighting regular expression (e.g. for search terms)
@param hi_re: either a valid re as str/unicode (you may want to use
re.escape before passing generic strings!) or a compiled
re object. raises re.error for invalid re.
"""
if isinstance(hi_re, (str, unicode)):
hi_re = re.compile(hi_re, re.U + re.IGNORECASE)
self._highlight_re = hi_re
def lang(self, on, lang_name):
return ""
def setPage(self, page):
self.page = page
def sysmsg(self, on, **kw):
""" Emit a system message (embed it into the page).
Normally used to indicate disabled options, or invalid markup.
"""
return ""
# Document Level #####################################################
def startDocument(self, pagename):
return ""
def endDocument(self):
return ""
def startContent(self, content_id="content", **kw):
if self.page:
self.request.begin_include(self.page.page_name)
return ""
def endContent(self):
if self.page:
self.request.end_include()
return ""
# Links ##############################################################
def pagelink(self, on, pagename='', page=None, **kw):
""" make a link to page <pagename>. Instead of supplying a pagename,
it is also possible to give a live Page object, then page.page_name
will be used.
"""
if not self._store_pagelinks or not on or kw.get('generated'):
return ''
if not pagename and page:
pagename = page.page_name
pagename = self.request.normalizePagename(pagename)
if pagename and pagename not in self.pagelinks:
self.pagelinks.append(pagename)
def interwikilink(self, on, interwiki='', pagename='', **kw):
""" calls pagelink() for internal interwikilinks
to make sure they get counted for self.pagelinks.
IMPORTANT: on and off must be called with same parameters, see
also the text_html formatter.
"""
wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_interwiki(self.request, interwiki, pagename)
if wikitag == 'Self' or wikitag == self.request.cfg.interwikiname:
if '#' in wikitail:
wikitail, kw['anchor'] = rsplit(wikitail, '#', 1)
wikitail = wikiutil.url_unquote(wikitail)
return self.pagelink(on, wikitail, **kw)
return ''
def url(self, on, url=None, css=None, **kw):
raise NotImplementedError
# Attachments ######################################################
def attachment_link(self, on, url=None, **kw):
raise NotImplementedError
def attachment_image(self, url, **kw):
raise NotImplementedError
def attachment_drawing(self, url, text, **kw):
raise NotImplementedError
def attachment_inlined(self, url, text, **kw):
from MoinMoin.action import AttachFile
import os
_ = self.request.getText
pagename, filename = AttachFile.absoluteName(url, self.page.page_name)
fname = wikiutil.taintfilename(filename)
fpath = AttachFile.getFilename(self.request, pagename, fname)
ext = os.path.splitext(filename)[1]
Parser = wikiutil.getParserForExtension(self.request.cfg, ext)
if Parser is not None:
try:
content = file(fpath, 'r').read()
# Try to decode text. It might return junk, but we don't
# have enough information with attachments.
content = wikiutil.decodeUnknownInput(content)
colorizer = Parser(content, self.request, filename=filename)
colorizer.format(self)
except IOError:
pass
return (self.attachment_link(1, url) +
self.text(text) +
self.attachment_link(0))
def anchordef(self, name):
return ""
def line_anchordef(self, lineno):
return ""
def anchorlink(self, on, name='', **kw):
return ""
def line_anchorlink(self, on, lineno=0):
return ""
def image(self, src=None, **kw):
"""An inline image.
Extra keyword arguments are according to the HTML <img> tag attributes.
In particular an 'alt' or 'title' argument should give a description
of the image.
"""
title = src
for titleattr in ('title', 'html__title', 'alt', 'html__alt'):
if titleattr in kw:
title = kw[titleattr]
break
if title:
return '[Image:%s]' % title
return '[Image]'
# generic transclude/include:
def transclusion(self, on, **kw):
raise NotImplementedError
def transclusion_param(self, **kw):
raise NotImplementedError
def smiley(self, text):
return text
def nowikiword(self, text):
return self.text(text)
# Text and Text Attributes ###########################################
def text(self, text, **kw):
if not self._highlight_re:
return self._text(text)
result = []
lastpos = 0
match = self._highlight_re.search(text)
while match and lastpos < len(text):
# add the match we found
result.append(self._text(text[lastpos:match.start()]))
result.append(self.highlight(1))
result.append(self._text(match.group(0)))
result.append(self.highlight(0))
# search for the next one
lastpos = match.end() + (match.end() == lastpos)
match = self._highlight_re.search(text, lastpos)
result.append(self._text(text[lastpos:]))
return ''.join(result)
def _text(self, text):
raise NotImplementedError
def strong(self, on, **kw):
raise NotImplementedError
def emphasis(self, on, **kw):
raise NotImplementedError
def underline(self, on, **kw):
raise NotImplementedError
def highlight(self, on, **kw):
raise NotImplementedError
def sup(self, on, **kw):
raise NotImplementedError
def sub(self, on, **kw):
raise NotImplementedError
def strike(self, on, **kw):
raise NotImplementedError
def code(self, on, **kw):
raise NotImplementedError
def preformatted(self, on, **kw):
self.in_pre = on != 0
def small(self, on, **kw):
raise NotImplementedError
def big(self, on, **kw):
raise NotImplementedError
# special markup for syntax highlighting #############################
def code_area(self, on, code_id, **kw):
raise NotImplementedError
def code_line(self, on):
raise NotImplementedError
def code_token(self, tok_text, tok_type):
raise NotImplementedError
def crans_box(self, title, content, color=None):
raise NotImplementedError
# Paragraphs, Lines, Rules ###########################################
def linebreak(self, preformatted=1):
raise NotImplementedError
def paragraph(self, on, **kw):
self.in_p = on != 0
def rule(self, size=0, **kw):
raise NotImplementedError
def icon(self, type):
return type
# Lists ##############################################################
def number_list(self, on, type=None, start=None, **kw):
raise NotImplementedError
def bullet_list(self, on, **kw):
raise NotImplementedError
def listitem(self, on, **kw):
raise NotImplementedError
def definition_list(self, on, **kw):
raise NotImplementedError
def definition_term(self, on, compact=0, **kw):
raise NotImplementedError
def definition_desc(self, on, **kw):
raise NotImplementedError
def heading(self, on, depth, **kw):
raise NotImplementedError
# Tables #############################################################
def table(self, on, attrs={}, **kw):
raise NotImplementedError
def table_row(self, on, attrs={}, **kw):
raise NotImplementedError
def table_cell(self, on, attrs={}, **kw):
raise NotImplementedError
# Dynamic stuff / Plugins ############################################
def macro(self, macro_obj, name, args, markup=None):
# call the macro
try:
return macro_obj.execute(name, args)
except ImportError, err:
errmsg = unicode(err)
if not name in errmsg:
raise
if markup:
return (self.span(1, title=errmsg) +
self.text(markup) +
self.span(0))
else:
return self.text(errmsg)
def _get_bang_args(self, line):
if line.startswith('#!'):
try:
name, args = line[2:].split(None, 1)
except ValueError:
return ''
else:
return args
return None
def parser(self, parser_name, lines):
""" parser_name MUST be valid!
writes out the result instead of returning it!
"""
# attention: this is copied into text_python!
parser = wikiutil.searchAndImportPlugin(self.request.cfg, "parser", parser_name)
args = None
if lines:
args = self._get_bang_args(lines[0])
logging.debug("formatter.parser: parser args %r" % args)
if args is not None:
lines = lines[1:]
if lines and not lines[0]:
lines = lines[1:]
if lines and not lines[-1].strip():
lines = lines[:-1]
p = parser('\n'.join(lines), self.request, format_args=args)
p.format(self)
del p
return ''
# Other ##############################################################
def div(self, on, **kw):
""" open/close a blocklevel division """
return ""
def span(self, on, **kw):
""" open/close a inline span """
return ""
def rawHTML(self, markup):
""" This allows emitting pre-formatted HTML markup, and should be
used wisely (i.e. very seldom).
Using this event while generating content results in unwanted
effects, like loss of markup or insertion of CDATA sections
when output goes to XML formats.
"""
import formatter, htmllib
from MoinMoin.util import simpleIO
# Regenerate plain text
f = simpleIO()
h = htmllib.HTMLParser(formatter.AbstractFormatter(formatter.DumbWriter(f)))
h.feed(markup)
h.close()
return self.text(f.getvalue())
def escapedText(self, on, **kw):
""" This allows emitting text as-is, anything special will
be escaped (at least in HTML, some text output format
would possibly do nothing here)
"""
return ""
def comment(self, text, **kw):
return ""
# ID handling #################################################
def sanitize_to_id(self, text):
'''
Take 'text' and return something that is a valid ID
for this formatter.
The default returns the first non-space character of the string.
Because of the way this is used, it must be idempotent,
i.e. calling it on an already sanitized id must yield the
original id.
'''
return text.strip()[:1]
def make_id_unique(self, id):
'''
Take an ID and make it unique in the current namespace.
'''
ns = self.request.include_id
if not ns is None:
ns = self.sanitize_to_id(ns)
id = self.sanitize_to_id(id)
id = self.request.make_unique_id(id, ns)
return id
def qualify_id(self, id):
'''
Take an ID and return a string that is qualified by
the current namespace; this default implementation
is suitable if the dot ('.') is valid in IDs for your
formatter.
'''
ns = self.request.include_id
if not ns is None:
ns = self.sanitize_to_id(ns)
return '%s.%s' % (ns, id)
return id

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,454 @@
# -*- coding: iso-8859-1 -*-
"""
MoinMoin - Wiki Security Interface and Access Control Lists
This implements the basic interface for user permissions and
system policy. If you want to define your own policy, inherit
from the base class 'Permissions', so that when new permissions
are defined, you get the defaults.
Then assign your new class to "SecurityPolicy" in wikiconfig;
and I mean the class, not an instance of it!
@copyright: 2000-2004 Juergen Hermann <jh@web.de>,
2003-2008 MoinMoin:ThomasWaldmann,
2003 Gustavo Niemeyer,
2005 Oliver Graf,
2007 Alexander Schremmer
@license: GNU GPL, see COPYING for details.
"""
import re
from MoinMoin import wikiutil, user
from MoinMoin.Page import Page
#############################################################################
### Basic Permissions Interface -- most features enabled by default
#############################################################################
def _check(request, pagename, username, right):
""" Check <right> access permission for user <username> on page <pagename>
For cfg.acl_hierarchic=False we just check the page in question.
For cfg.acl_hierarchic=True we, we check each page in the hierarchy. We
start with the deepest page and recurse to the top of the tree.
If one of those permits, True is returned.
For both configurations, we check acl_rights_before before the page/default
acl and acl_rights_after after the page/default acl, of course.
This method should not be called by users, use __getattr__ instead.
@param request: the current request object
@param pagename: pagename to get permissions from
@param username: the user name
@param right: the right to check
@rtype: bool
@return: True if you have permission or False
"""
cache = request.cfg.cache
allowed = cache.acl_rights_before.may(request, username, right)
if allowed is not None:
return allowed
if request.cfg.acl_hierarchic:
pages = pagename.split('/') # create page hierarchy list
some_acl = False
for i in range(len(pages), 0, -1):
# Create the next pagename in the hierarchy
# starting at the leaf, going to the root
name = '/'.join(pages[:i])
# Get page acl and ask for permission
acl = Page(request, name).getACL(request)
if acl.acl:
some_acl = True
allowed = acl.may(request, username, right)
if allowed is not None:
return allowed
if not some_acl:
allowed = cache.acl_rights_default.may(request, username, right)
if allowed is not None:
return allowed
else:
if request.page is not None and pagename == request.page.page_name:
p = request.page # reuse is good
else:
p = Page(request, pagename)
acl = p.getACL(request) # this will be fast in a reused page obj
allowed = acl.may(request, username, right)
if allowed is not None:
return allowed
allowed = cache.acl_rights_after.may(request, username, right)
if allowed is not None:
return allowed
return False
class Permissions:
""" Basic interface for user permissions and system policy.
Note that you still need to allow some of the related actions, this
just controls their behavior, not their activation.
When sub classing this class, you must extend the class methods, not
replace them, or you might break the acl in the wiki. Correct sub
classing looks like this:
def read(self, pagename):
# Your special security rule
if something:
return false
# Do not return True or you break acl!
# This call will use the default acl rules
return Permissions.read(pagename)
"""
def __init__(self, user):
self.name = user.name
self.request = user._request
def save(self, editor, newtext, rev, **kw):
""" Check whether user may save a page.
`editor` is the PageEditor instance, the other arguments are
those of the `PageEditor.saveText` method.
@param editor: PageEditor instance.
@param newtext: new page text, you can enable of disable saving according
to the content of the text, e.g. prevent link spam.
@param rev: new revision number? XXX
@param kw: XXX
@rtype: bool
@return: True if you can save or False
"""
return self.write(editor.page_name)
def __getattr__(self, attr):
""" Shortcut to export getPermission function for all known ACL rights
if attr is one of the rights in acl_rights_valid, then return a
checking function for it. Else raise an AttributeError.
@param attr: one of ACL rights as defined in acl_rights_valid
@rtype: function
@return: checking function for that right, accepting a pagename
"""
request = self.request
if attr not in request.cfg.acl_rights_valid:
raise AttributeError, attr
return lambda pagename: _check(self.request, pagename, self.name, attr)
# make an alias for the default policy
Default = Permissions
class AccessControlList:
''' Access Control List
Control who may do what on or with a wiki page.
Syntax of an ACL string:
[+|-]User[,User,...]:[right[,right,...]] [[+|-]SomeGroup:...] ...
... [[+|-]Known:...] [[+|-]All:...]
"User" is a user name and triggers only if the user matches. Up
to version 1.2 only WikiNames were supported, as of version 1.3,
any name can be used in acl lines, including name with spaces
using esoteric languages.
"SomeGroup" is a page name matching cfg.page_group_regex with
some lines in the form " * Member", defining the group members.
"Known" is a group containing all valid / known users.
"All" is a group containing all users (Known and Anonymous users).
"right" may be an arbitrary word like read, write, delete, admin.
Only words in cfg.acl_validrights are accepted, others are
ignored. It is allowed to specify no rights, which means that no
rights are given.
How ACL is processed
When some user is trying to access some ACL-protected resource,
the ACLs will be processed in the order they are found. The first
matching ACL will tell if the user has access to that resource
or not.
For example, the following ACL tells that SomeUser is able to
read and write the resources protected by that ACL, while any
member of SomeGroup (besides SomeUser, if part of that group)
may also admin that, and every other user is able to read it.
SomeUser:read,write SomeGroup:read,write,admin All:read
In this example, SomeUser can read and write but can not admin,
revert or delete pages. Rights that are NOT specified on the
right list are automatically set to NO.
Using Prefixes
To make the system more flexible, there are also two modifiers:
the prefixes "+" and "-".
+SomeUser:read -OtherUser:write
The acl line above will grant SomeUser read right, and OtherUser
write right, but will NOT block automatically all other rights
for these users. For example, if SomeUser ask to write, the
above acl line does not define if he can or can not write. He
will be able to write if acl_rights_before or acl_rights_after
allow this (see configuration options).
Using prefixes, this acl line:
SomeUser:read,write SomeGroup:read,write,admin All:read
Can be written as:
-SomeUser:admin SomeGroup:read,write,admin All:read
Or even:
+All:read -SomeUser:admin SomeGroup:read,write,admin
Notice that you probably will not want to use the second and
third examples in ACL entries of some page. They are very
useful on the moin configuration entries though.
Configuration options
cfg.acl_rights_default
It is is ONLY used when no other ACLs are given.
Default: "Known:read,write,delete All:read,write",
cfg.acl_rights_before
When the page has ACL entries, this will be inserted BEFORE
any page entries.
Default: ""
cfg.acl_rights_after
When the page has ACL entries, this will be inserted AFTER
any page entries.
Default: ""
cfg.acl_rights_valid
These are the acceptable (known) rights (and the place to
extend, if necessary).
Default: ["read", "write", "delete", "admin"]
'''
special_users = ["All", "Known", "Trusted"] # order is important
def __init__(self, cfg, lines=[]):
"""Initialize an ACL, starting from <nothing>.
"""
if lines:
self.acl = [] # [ ('User', {"read": 0, ...}), ... ]
self.acl_lines = []
for line in lines:
self._addLine(cfg, line)
else:
self.acl = None
self.acl_lines = None
def _addLine(self, cfg, aclstring, remember=1):
""" Add another ACL line
This can be used in multiple subsequent calls to process longer lists.
@param cfg: current config
@param aclstring: acl string from page or cfg
@param remember: should add the line to self.acl_lines
"""
# Remember lines
if remember:
self.acl_lines.append(aclstring)
# Iterate over entries and rights, parsed by acl string iterator
acliter = ACLStringIterator(cfg.acl_rights_valid, aclstring)
for modifier, entries, rights in acliter:
if entries == ['Default']:
self._addLine(cfg, cfg.acl_rights_default, remember=0)
else:
for entry in entries:
rightsdict = {}
if modifier:
# Only user rights are added to the right dict.
# + add rights with value of 1
# - add right with value of 0
for right in rights:
rightsdict[right] = (modifier == '+')
else:
# All rights from acl_rights_valid are added to the
# dict, user rights with value of 1, and other with
# value of 0
for right in cfg.acl_rights_valid:
rightsdict[right] = (right in rights)
self.acl.append((entry, rightsdict))
def may(self, request, name, dowhat):
""" May <name> <dowhat>? Returns boolean answer.
Note: this check does NOT include the acl_rights_before / _after ACL,
but it WILL use acl_rights_default if there is no (page) ACL.
"""
if self.acl is None: # no #acl used on Page
acl = request.cfg.cache.acl_rights_default.acl
else: # we have a #acl on the page (self.acl can be [] if #acl is empty!)
acl = self.acl
is_group_member = request.dicts.has_member
group_re = request.cfg.cache.page_group_regexact
allowed = None
for entry, rightsdict in acl:
if entry in self.special_users:
handler = getattr(self, "_special_"+entry, None)
allowed = handler(request, name, dowhat, rightsdict)
elif group_re.search(entry):
if is_group_member(entry, name):
allowed = rightsdict.get(dowhat)
else:
for special in self.special_users:
if is_group_member(entry, special):
handler = getattr(self, "_special_" + special, None)
allowed = handler(request, name, dowhat, rightsdict)
break # order of self.special_users is important
elif entry == name:
allowed = rightsdict.get(dowhat)
if allowed is not None:
return allowed
return allowed # should be None
def getString(self, b='#acl ', e='\n'):
"""print the acl strings we were fed with"""
if self.acl_lines:
acl_lines = ''.join(["%s%s%s" % (b, l, e) for l in self.acl_lines])
else:
acl_lines = ''
return acl_lines
def _special_All(self, request, name, dowhat, rightsdict):
return rightsdict.get(dowhat)
def _special_Known(self, request, name, dowhat, rightsdict):
""" check if user <name> is known to us,
that means that there is a valid user account present.
works for subscription emails.
"""
if user.getUserId(request, name): # is a user with this name known?
return rightsdict.get(dowhat)
return None
def _special_Trusted(self, request, name, dowhat, rightsdict):
""" check if user <name> is known AND has logged in using a trusted
authentication method.
Does not work for subsription emails that should be sent to <user>,
as he is not logged in in that case.
"""
if (request.user.name == name and
request.user.auth_method in request.cfg.auth_methods_trusted):
return rightsdict.get(dowhat)
return None
def __eq__(self, other):
return self.acl_lines == other.acl_lines
def __ne__(self, other):
return self.acl_lines != other.acl_lines
class ACLStringIterator:
""" Iterator for acl string
Parse acl string and return the next entry on each call to
next. Implement the Iterator protocol.
Usage:
iter = ACLStringIterator(cfg.acl_rights_valid, 'user name:right')
for modifier, entries, rights in iter:
# process data
"""
def __init__(self, rights, aclstring):
""" Initialize acl iterator
@param rights: the acl rights to consider when parsing
@param aclstring: string to parse
"""
self.rights = rights
self.rest = aclstring.strip()
self.finished = 0
def __iter__(self):
""" Required by the Iterator protocol """
return self
def next(self):
""" Return the next values from the acl string
When the iterator is finished and you try to call next, it
raises a StopIteration. The iterator finish as soon as the
string is fully parsed or can not be parsed any more.
@rtype: 3 tuple - (modifier, [entry, ...], [right, ...])
@return: values for one item in an acl string
"""
# Handle finished state, required by iterator protocol
if self.rest == '':
self.finished = 1
if self.finished:
raise StopIteration
# Get optional modifier [+|-]entries:rights
modifier = ''
if self.rest[0] in ('+', '-'):
modifier, self.rest = self.rest[0], self.rest[1:]
# Handle the Default meta acl
if self.rest.startswith('Default ') or self.rest == 'Default':
self.rest = self.rest[8:]
entries, rights = ['Default'], []
# Handle entries:rights pairs
else:
# Get entries
try:
entries, self.rest = self.rest.split(':', 1)
except ValueError:
self.finished = 1
raise StopIteration("Can't parse rest of string")
if entries == '':
entries = []
else:
# TODO strip each entry from blanks?
entries = entries.split(',')
# Get rights
try:
rights, self.rest = self.rest.split(' ', 1)
# Remove extra white space after rights fragment,
# allowing using multiple spaces between items.
self.rest = self.rest.lstrip()
except ValueError:
rights, self.rest = self.rest, ''
rights = [r for r in rights.split(',') if r in self.rights]
return modifier, entries, rights
def parseACL(request, text):
""" Parse acl lines from text and return ACL object """
pi, dummy = wikiutil.get_processing_instructions(text)
acl_lines = [args for verb, args in pi if verb == 'acl']
return AccessControlList(request.cfg, acl_lines)

View file

@ -0,0 +1,501 @@
# -*- coding: iso-8859-1 -*-
"""
MoinMoin - Wiki Security Interface and Access Control Lists
This implements the basic interface for user permissions and
system policy. If you want to define your own policy, inherit
from the base class 'Permissions', so that when new permissions
are defined, you get the defaults.
Then assign your new class to "SecurityPolicy" in wikiconfig;
and I mean the class, not an instance of it!
@copyright: 2000-2004 Juergen Hermann <jh@web.de>,
2003-2008 MoinMoin:ThomasWaldmann,
2003 Gustavo Niemeyer,
2005 Oliver Graf,
2007 Alexander Schremmer
@license: GNU GPL, see COPYING for details.
"""
import re
from MoinMoin import wikiutil, user
from MoinMoin.Page import Page
#### HACK SAUVAGE 1/5
import sys
sys.path.append('/usr/scripts/gestion/')
from iptools import is_crans
#### FIN DU HACK 1/5
#############################################################################
### Basic Permissions Interface -- most features enabled by default
#############################################################################
def _check(request, pagename, username, right):
""" Check <right> access permission for user <username> on page <pagename>
For cfg.acl_hierarchic=False we just check the page in question.
For cfg.acl_hierarchic=True we, we check each page in the hierarchy. We
start with the deepest page and recurse to the top of the tree.
If one of those permits, True is returned.
For both configurations, we check acl_rights_before before the page/default
acl and acl_rights_after after the page/default acl, of course.
This method should not be called by users, use __getattr__ instead.
@param request: the current request object
@param pagename: pagename to get permissions from
@param username: the user name
@param right: the right to check
@rtype: bool
@return: True if you have permission or False
"""
cache = request.cfg.cache
allowed = cache.acl_rights_before.may(request, username, right)
if allowed is not None:
return allowed
if request.cfg.acl_hierarchic:
pages = pagename.split('/') # create page hierarchy list
some_acl = False
for i in range(len(pages), 0, -1):
# Create the next pagename in the hierarchy
# starting at the leaf, going to the root
name = '/'.join(pages[:i])
# Get page acl and ask for permission
acl = Page(request, name).getACL(request)
if acl.acl:
some_acl = True
allowed = acl.may(request, username, right)
if allowed is not None:
return allowed
if not some_acl:
allowed = cache.acl_rights_default.may(request, username, right)
if allowed is not None:
return allowed
else:
if request.page is not None and pagename == request.page.page_name:
p = request.page # reuse is good
else:
p = Page(request, pagename)
acl = p.getACL(request) # this will be fast in a reused page obj
allowed = acl.may(request, username, right)
if allowed is not None:
return allowed
allowed = cache.acl_rights_after.may(request, username, right)
if allowed is not None:
return allowed
return False
class Permissions:
""" Basic interface for user permissions and system policy.
Note that you still need to allow some of the related actions, this
just controls their behavior, not their activation.
When sub classing this class, you must extend the class methods, not
replace them, or you might break the acl in the wiki. Correct sub
classing looks like this:
def read(self, pagename):
# Your special security rule
if something:
return false
# Do not return True or you break acl!
# This call will use the default acl rules
return Permissions.read(pagename)
"""
def __init__(self, user):
self.name = user.name
self.request = user._request
def save(self, editor, newtext, rev, **kw):
""" Check whether user may save a page.
`editor` is the PageEditor instance, the other arguments are
those of the `PageEditor.saveText` method.
@param editor: PageEditor instance.
@param newtext: new page text, you can enable of disable saving according
to the content of the text, e.g. prevent link spam.
@param rev: new revision number? XXX
@param kw: XXX
@rtype: bool
@return: True if you can save or False
"""
return self.write(editor.page_name)
def __getattr__(self, attr):
""" Shortcut to export getPermission function for all known ACL rights
if attr is one of the rights in acl_rights_valid, then return a
checking function for it. Else raise an AttributeError.
@param attr: one of ACL rights as defined in acl_rights_valid
@rtype: function
@return: checking function for that right, accepting a pagename
"""
request = self.request
if attr not in request.cfg.acl_rights_valid:
raise AttributeError, attr
return lambda pagename: _check(self.request, pagename, self.name, attr)
# make an alias for the default policy
Default = Permissions
class AccessControlList:
''' Access Control List
Control who may do what on or with a wiki page.
Syntax of an ACL string:
[+|-]User[,User,...]:[right[,right,...]] [[+|-]SomeGroup:...] ...
... [[+|-]Known:...] [[+|-]All:...]
"User" is a user name and triggers only if the user matches. Up
to version 1.2 only WikiNames were supported, as of version 1.3,
any name can be used in acl lines, including name with spaces
using esoteric languages.
"SomeGroup" is a page name matching cfg.page_group_regex with
some lines in the form " * Member", defining the group members.
"Known" is a group containing all valid / known users.
"All" is a group containing all users (Known and Anonymous users).
"right" may be an arbitrary word like read, write, delete, admin.
Only words in cfg.acl_validrights are accepted, others are
ignored. It is allowed to specify no rights, which means that no
rights are given.
How ACL is processed
When some user is trying to access some ACL-protected resource,
the ACLs will be processed in the order they are found. The first
matching ACL will tell if the user has access to that resource
or not.
For example, the following ACL tells that SomeUser is able to
read and write the resources protected by that ACL, while any
member of SomeGroup (besides SomeUser, if part of that group)
may also admin that, and every other user is able to read it.
SomeUser:read,write SomeGroup:read,write,admin All:read
In this example, SomeUser can read and write but can not admin,
revert or delete pages. Rights that are NOT specified on the
right list are automatically set to NO.
Using Prefixes
To make the system more flexible, there are also two modifiers:
the prefixes "+" and "-".
+SomeUser:read -OtherUser:write
The acl line above will grant SomeUser read right, and OtherUser
write right, but will NOT block automatically all other rights
for these users. For example, if SomeUser ask to write, the
above acl line does not define if he can or can not write. He
will be able to write if acl_rights_before or acl_rights_after
allow this (see configuration options).
Using prefixes, this acl line:
SomeUser:read,write SomeGroup:read,write,admin All:read
Can be written as:
-SomeUser:admin SomeGroup:read,write,admin All:read
Or even:
+All:read -SomeUser:admin SomeGroup:read,write,admin
Notice that you probably will not want to use the second and
third examples in ACL entries of some page. They are very
useful on the moin configuration entries though.
Configuration options
cfg.acl_rights_default
It is is ONLY used when no other ACLs are given.
Default: "Known:read,write,delete All:read,write",
cfg.acl_rights_before
When the page has ACL entries, this will be inserted BEFORE
any page entries.
Default: ""
cfg.acl_rights_after
When the page has ACL entries, this will be inserted AFTER
any page entries.
Default: ""
cfg.acl_rights_valid
These are the acceptable (known) rights (and the place to
extend, if necessary).
Default: ["read", "write", "delete", "admin"]
'''
#special_users = ["All", "Known", "Trusted"] # order is important
## HACK SAUVAGE 2/5
special_users = ["All", "Known", "Trusted", "Crans", "NoCrans"] # order is important
## FIN HACK 2/5
def __init__(self, cfg, lines=[]):
"""Initialize an ACL, starting from <nothing>.
"""
if lines:
self.acl = [] # [ ('User', {"read": 0, ...}), ... ]
self.acl_lines = []
for line in lines:
self._addLine(cfg, line)
else:
self.acl = None
self.acl_lines = None
def _addLine(self, cfg, aclstring, remember=1):
""" Add another ACL line
This can be used in multiple subsequent calls to process longer lists.
@param cfg: current config
@param aclstring: acl string from page or cfg
@param remember: should add the line to self.acl_lines
"""
# Remember lines
if remember:
self.acl_lines.append(aclstring)
# Iterate over entries and rights, parsed by acl string iterator
acliter = ACLStringIterator(cfg.acl_rights_valid, aclstring)
for modifier, entries, rights in acliter:
if entries == ['Default']:
self._addLine(cfg, cfg.acl_rights_default, remember=0)
else:
for entry in entries:
rightsdict = {}
if modifier:
# Only user rights are added to the right dict.
# + add rights with value of 1
# - add right with value of 0
for right in rights:
rightsdict[right] = (modifier == '+')
else:
# All rights from acl_rights_valid are added to the
# dict, user rights with value of 1, and other with
# value of 0
for right in cfg.acl_rights_valid:
rightsdict[right] = (right in rights)
self.acl.append((entry, rightsdict))
def may(self, request, name, dowhat):
""" May <name> <dowhat>? Returns boolean answer.
Note: this check does NOT include the acl_rights_before / _after ACL,
but it WILL use acl_rights_default if there is no (page) ACL.
"""
if self.acl is None: # no #acl used on Page
acl = request.cfg.cache.acl_rights_default.acl
else: # we have a #acl on the page (self.acl can be [] if #acl is empty!)
acl = self.acl
is_group_member = request.dicts.has_member
group_re = request.cfg.cache.page_group_regexact
allowed = None
for entry, rightsdict in acl:
if entry in self.special_users:
handler = getattr(self, "_special_"+entry, None)
allowed = handler(request, name, dowhat, rightsdict)
elif group_re.search(entry):
if is_group_member(entry, name):
allowed = rightsdict.get(dowhat)
else:
for special in self.special_users:
if is_group_member(entry, special):
handler = getattr(self, "_special_" + special, None)
allowed = handler(request, name, dowhat, rightsdict)
break # order of self.special_users is important
elif entry == name:
allowed = rightsdict.get(dowhat)
if allowed is not None:
return allowed
return allowed # should be None
def getString(self, b='#acl ', e='\n'):
"""print the acl strings we were fed with"""
if self.acl_lines:
acl_lines = ''.join(["%s%s%s" % (b, l, e) for l in self.acl_lines])
else:
acl_lines = ''
return acl_lines
def _special_All(self, request, name, dowhat, rightsdict):
## HACK SAUVAGE 3/5
if dowhat == "read" and is_page_public(request):
return True
## FIN HACK 3/5
return rightsdict.get(dowhat)
def _special_Known(self, request, name, dowhat, rightsdict):
""" check if user <name> is known to us,
that means that there is a valid user account present.
works for subscription emails.
"""
if user.getUserId(request, name): # is a user with this name known?
return rightsdict.get(dowhat)
return None
def _special_Trusted(self, request, name, dowhat, rightsdict):
""" check if user <name> is known AND has logged in using a trusted
authentication method.
Does not work for subsription emails that should be sent to <user>,
as he is not logged in in that case.
"""
if (request.user.name == name and
request.user.auth_method in request.cfg.auth_methods_trusted):
return rightsdict.get(dowhat)
return None
#### HACK SAUVAGE 4/5
def _requete_interne(self, request):
try:
if is_crans(request.remote_addr) and (request.remote_addr != u'138.231.136.3' or is_crans(request.mpyreq.headers_in['X-Forwarded-For'].split(",")[-1].strip())):
return True
except:
pass
return False
def _special_Crans(self, request, name, dowhat, rightsdict):
if self._requete_interne(request):
return rightsdict.get(dowhat)
return None
def _special_NoCrans(self, request, name, dowhat, rightsdict):
if dowhat == "read" and is_page_public(request):
return True
if not self._requete_interne(request):
return rightsdict.get(dowhat)
return None
#### FIN Du HACK 4/5
def __eq__(self, other):
return self.acl_lines == other.acl_lines
def __ne__(self, other):
return self.acl_lines != other.acl_lines
class ACLStringIterator:
""" Iterator for acl string
Parse acl string and return the next entry on each call to
next. Implement the Iterator protocol.
Usage:
iter = ACLStringIterator(cfg.acl_rights_valid, 'user name:right')
for modifier, entries, rights in iter:
# process data
"""
def __init__(self, rights, aclstring):
""" Initialize acl iterator
@param rights: the acl rights to consider when parsing
@param aclstring: string to parse
"""
self.rights = rights
self.rest = aclstring.strip()
self.finished = 0
def __iter__(self):
""" Required by the Iterator protocol """
return self
def next(self):
""" Return the next values from the acl string
When the iterator is finished and you try to call next, it
raises a StopIteration. The iterator finish as soon as the
string is fully parsed or can not be parsed any more.
@rtype: 3 tuple - (modifier, [entry, ...], [right, ...])
@return: values for one item in an acl string
"""
# Handle finished state, required by iterator protocol
if self.rest == '':
self.finished = 1
if self.finished:
raise StopIteration
# Get optional modifier [+|-]entries:rights
modifier = ''
if self.rest[0] in ('+', '-'):
modifier, self.rest = self.rest[0], self.rest[1:]
# Handle the Default meta acl
if self.rest.startswith('Default ') or self.rest == 'Default':
self.rest = self.rest[8:]
entries, rights = ['Default'], []
# Handle entries:rights pairs
else:
# Get entries
try:
entries, self.rest = self.rest.split(':', 1)
except ValueError:
self.finished = 1
raise StopIteration("Can't parse rest of string")
if entries == '':
entries = []
else:
# TODO strip each entry from blanks?
entries = entries.split(',')
# Get rights
try:
rights, self.rest = self.rest.split(' ', 1)
# Remove extra white space after rights fragment,
# allowing using multiple spaces between items.
self.rest = self.rest.lstrip()
except ValueError:
rights, self.rest = self.rest, ''
rights = [r for r in rights.split(',') if r in self.rights]
return modifier, entries, rights
def parseACL(request, text):
""" Parse acl lines from text and return ACL object """
pi, dummy = wikiutil.get_processing_instructions(text)
acl_lines = [args for verb, args in pi if verb == 'acl']
return AccessControlList(request.cfg, acl_lines)
#### HACK SAUVAGE 5/5
def is_page_public(request):
#return True
## On recherche si la page est publique
this_page = request.page.page_name
categories = request.page.getCategories(request)
if u'CatégoriePagePublique' in categories:
return True
else:
return None
#### FIn DU HACK 5/5