element for larger font.
+
+ Call once with on=1 to start the region, and a second time
+ with on=0 to end it.
+ """
+ tag = 'big'
+ if on:
+ return self._open(tag, allowed_attrs=[], **kw)
+ return self._close(tag)
+
+
+ # Block elements ####################################################
+
+ def preformatted(self, on, **kw):
+ """Creates a preformatted text region, with a element.
+
+ Call once with on=1 to start the region, and a second time
+ with on=0 to end it.
+ """
+ FormatterBase.preformatted(self, on)
+ tag = 'pre'
+ if on:
+ return self._open(tag, newline=1, **kw)
+ return self._close(tag)
+
+ # Use by code area
+ _toggleLineNumbersScript = """
+
+"""
+
+ def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
+ """Creates a formatted code region, with line numbering.
+
+ This region is formatted as a with a
inside it. The
+ code_id argument is assigned to the 'id' of the div element, and
+ must be unique within the document. The show, start, and step are
+ used for line numbering.
+
+ Note this is not like most formatter methods, it can not take any
+ extra keyword arguments.
+
+ Call once with on=1 to start the region, and a second time
+ with on=0 to end it.
+ """
+ _ = self.request.getText
+ res = []
+ if on:
+ code_id = self.sanitize_to_id('CA-%s' % code_id)
+ ci = self.qualify_id(self.make_id_unique(code_id))
+
+ # Open a code area
+ self._in_code_area = 1
+ self._in_code_line = 0
+ # id in here no longer used
+ self._code_area_state = [None, show, start, step, start]
+
+ # Open the code div - using left to right always!
+ attr = {'class': 'codearea', 'lang': 'en', 'dir': 'ltr'}
+ res.append(self._open('div', attr=attr))
+
+ # Add the script only in the first code area on the page
+ if self._code_area_js == 0 and self._code_area_state[1] >= 0:
+ res.append(self._toggleLineNumbersScript)
+ self._code_area_js = 1
+
+ # Add line number link, but only for JavaScript enabled browsers.
+ if self._code_area_state[1] >= 0:
+ toggleLineNumbersLink = r'''
+
+''' % (ci, self._code_area_state[2], self._code_area_state[3],
+ _("Toggle line numbers"))
+ res.append(toggleLineNumbersLink)
+
+ # Open pre - using left to right always!
+ attr = {'id': ci, 'lang': 'en', 'dir': 'ltr'}
+ res.append(self._open('pre', newline=True, attr=attr, is_unique=True))
+ else:
+ # Close code area
+ res = []
+ if self._in_code_line:
+ res.append(self.code_line(0))
+ res.append(self._close('pre'))
+ res.append(self._close('div'))
+
+ # Update state
+ self._in_code_area = 0
+
+ return ''.join(res)
+
+ def code_line(self, on):
+ res = ''
+ if not on or (on and self._in_code_line):
+ res += '\n'
+ if on:
+ res += ''
+ if self._code_area_state[1] > 0:
+ res += '%4d ' % (self._code_area_state[4], )
+ self._code_area_state[4] += self._code_area_state[3]
+ self._in_code_line = on != 0
+ return res
+
+ def code_token(self, on, tok_type):
+ return ['' % tok_type, ''][not on]
+
+
+ #### HACK SAUVAGE 1/1 ####
+ def crans_box(self, title, body_html, color=None):
+ if color==None: css_color_class=u""
+ else: css_color_class = u" %s_box" % color
+ html = [
+ '' % css_color_class,
+ '
',
+ self.heading(1,3),
+ title,
+ self.heading(0,3),
+ '
',
+ '
',
+ '
',
+ body_html,
+ '
',
+ '
',
+ '
',
+ ]
+ return('\n'.join(html))
+ #### FIN HACK 1/1 ####
+
+ # Paragraphs, Lines, Rules ###########################################
+
+ def _indent_spaces(self):
+ """Returns space(s) for indenting the html source so list nesting is easy to read.
+
+ Note that this mostly works, but because of caching may not always be accurate."""
+ if prettyprint:
+ return self.indentspace * self._indent_level
+ else:
+ return ''
+
+ def _newline(self):
+ """Returns the whitespace for starting a new html source line, properly indented."""
+ if prettyprint:
+ return '\n' + self._indent_spaces()
+ else:
+ return ''
+
+ def linebreak(self, preformatted=1):
+ """Creates a line break in the HTML output.
+
+ If preformatted is true a
element is inserted, otherwise
+ the linebreak will only be visible in the HTML source.
+ """
+ if self._in_code_area:
+ preformatted = 1
+ return ['\n', '
\n'][not preformatted] + self._indent_spaces()
+
+ def paragraph(self, on, **kw):
+ """Creates a paragraph with a element.
+
+ Call once with on=1 to start the region, and a second time
+ with on=0 to end it.
+ """
+ if self._terse:
+ return ''
+ FormatterBase.paragraph(self, on)
+ tag = 'p'
+ if on:
+ tagstr = self._open(tag, **kw)
+ else:
+ tagstr = self._close(tag)
+ return tagstr
+
+ def rule(self, size=None, **kw):
+ """Creates a horizontal rule with an
element.
+
+ If size is a number in the range [1..6], the CSS class of the rule
+ is set to 'hr1' through 'hr6'. The intent is that the larger the
+ size number the thicker or bolder the rule will be.
+ """
+ if size and 1 <= size <= 6:
+ # Add hr class: hr1 - hr6
+ return self._open('hr', newline=1, attr={'class': 'hr%d' % size}, **kw)
+ return self._open('hr', newline=1, **kw)
+
+ # Images / Transclusion ##############################################
+
+ def icon(self, type):
+ return self.request.theme.make_icon(type)
+
+ smiley = icon
+
+ def image(self, src=None, **kw):
+ """Creates an inline image with an
element.
+
+ The src argument must be the URL to the image file.
+ """
+ if src:
+ kw['src'] = src
+ return self._open('img', **kw)
+
+ def transclusion(self, on, **kw):
+ """Transcludes (includes/embeds) another object."""
+ if on:
+ return self._open('object',
+ allowed_attrs=['archive', 'classid', 'codebase',
+ 'codetype', 'data', 'declare',
+ 'height', 'name', 'standby',
+ 'type', 'width', ],
+ **kw)
+ else:
+ return self._close('object')
+
+ def transclusion_param(self, **kw):
+ """Give a parameter to a transcluded object."""
+ return self._open('param',
+ allowed_attrs=['name', 'type', 'value', 'valuetype', ],
+ **kw)
+
+ # Lists ##############################################################
+
+ def number_list(self, on, type=None, start=None, **kw):
+ """Creates an HTML ordered list, element.
+
+ The 'type' if specified can be any legal numbered
+ list-style-type, such as 'decimal','lower-roman', etc.
+
+ The 'start' argument if specified gives the numeric value of
+ the first list item (default is 1).
+
+ Call once with on=1 to start the list, and a second time
+ with on=0 to end it.
+ """
+ tag = 'ol'
+ if on:
+ attr = {}
+ if type is not None:
+ attr['type'] = type
+ if start is not None:
+ attr['start'] = start
+ tagstr = self._open(tag, newline=1, attr=attr, **kw)
+ else:
+ tagstr = self._close(tag, newline=1)
+ return tagstr
+
+ def bullet_list(self, on, **kw):
+ """Creates an HTML ordered list, element.
+
+ The 'type' if specified can be any legal unnumbered
+ list-style-type, such as 'disc','square', etc.
+
+ Call once with on=1 to start the list, and a second time
+ with on=0 to end it.
+ """
+ tag = 'ul'
+ if on:
+ tagstr = self._open(tag, newline=1, **kw)
+ else:
+ tagstr = self._close(tag, newline=1)
+ return tagstr
+
+ def listitem(self, on, **kw):
+ """Adds a list item, - element, to a previously opened
+ bullet or number list.
+
+ Call once with on=1 to start the region, and a second time
+ with on=0 to end it.
+ """
+ tag = 'li'
+ if on:
+ tagstr = self._open(tag, newline=1, **kw)
+ else:
+ tagstr = self._close(tag, newline=1)
+ return tagstr
+
+ def definition_list(self, on, **kw):
+ """Creates an HTML definition list,
element.
+
+ Call once with on=1 to start the list, and a second time
+ with on=0 to end it.
+ """
+ tag = 'dl'
+ if on:
+ tagstr = self._open(tag, newline=1, **kw)
+ else:
+ tagstr = self._close(tag, newline=1)
+ return tagstr
+
+ def definition_term(self, on, **kw):
+ """Adds a new term to a definition list, HTML element - .
+
+ Call once with on=1 to start the term, and a second time
+ with on=0 to end it.
+ """
+ tag = 'dt'
+ if on:
+ tagstr = self._open(tag, newline=1, **kw)
+ else:
+ tagstr = self._close(tag, newline=0)
+ return tagstr
+
+ def definition_desc(self, on, **kw):
+ """Gives the definition to a definition item, HTML element
- .
+
+ Call once with on=1 to start the definition, and a second time
+ with on=0 to end it.
+ """
+ tag = 'dd'
+ if on:
+ tagstr = self._open(tag, newline=1, **kw)
+ else:
+ tagstr = self._close(tag, newline=0)
+ return tagstr
+
+ def heading(self, on, depth, **kw):
+ # remember depth of first heading, and adapt counting depth accordingly
+ if not self._base_depth:
+ self._base_depth = depth
+
+ count_depth = max(depth - (self._base_depth - 1), 1)
+
+ # check numbering, possibly changing the default
+ if self._show_section_numbers is None:
+ self._show_section_numbers = self.cfg.show_section_numbers
+ numbering = self.request.getPragma('section-numbers', '').lower()
+ if numbering in ['0', 'off']:
+ self._show_section_numbers = 0
+ elif numbering in ['1', 'on']:
+ self._show_section_numbers = 1
+ elif numbering in ['2', '3', '4', '5', '6']:
+ # explicit base level for section number display
+ self._show_section_numbers = int(numbering)
+
+ heading_depth = depth
+
+ # closing tag, with empty line after, to make source more readable
+ if not on:
+ return self._close('h%d' % heading_depth) + '\n'
+
+ # create section number
+ number = ''
+ if self._show_section_numbers:
+ # count headings on all levels
+ self.request._fmt_hd_counters = self.request._fmt_hd_counters[:count_depth]
+ while len(self.request._fmt_hd_counters) < count_depth:
+ self.request._fmt_hd_counters.append(0)
+ self.request._fmt_hd_counters[-1] = self.request._fmt_hd_counters[-1] + 1
+ number = '.'.join([str(x) for x in self.request._fmt_hd_counters[self._show_section_numbers-1:]])
+ if number: number += ". "
+
+ # Add space before heading, easier to check source code
+ result = '\n' + self._open('h%d' % heading_depth, **kw)
+
+ if self.request.user.show_topbottom:
+ result += "%s%s%s%s%s%s" % (
+ self.anchorlink(1, "bottom"), self.icon('bottom'), self.anchorlink(0),
+ self.anchorlink(1, "top"), self.icon('top'), self.anchorlink(0))
+
+ return "%s%s" % (result, number)
+
+
+ # Tables #############################################################
+
+ _allowed_table_attrs = {
+ 'table': ['class', 'id', 'style'],
+ 'row': ['class', 'id', 'style'],
+ '': ['colspan', 'rowspan', 'class', 'id', 'style', 'abbr'],
+ }
+
+ def _checkTableAttr(self, attrs, prefix):
+ """ Check table attributes
+
+ Convert from wikitable attributes to html 4 attributes.
+
+ @param attrs: attribute dict
+ @param prefix: used in wiki table attributes
+ @rtype: dict
+ @return: valid table attributes
+ """
+ if not attrs:
+ return {}
+
+ result = {}
+ s = [] # we collect synthesized style in s
+ for key, val in attrs.items():
+ # Ignore keys that don't start with prefix
+ if prefix and key[:len(prefix)] != prefix:
+ continue
+ key = key[len(prefix):]
+ val = val.strip('"')
+ # remove invalid attrs from dict and synthesize style
+ if key == 'width':
+ s.append("width: %s" % val)
+ elif key == 'height':
+ s.append("height: %s" % val)
+ elif key == 'bgcolor':
+ s.append("background-color: %s" % val)
+ elif key == 'align':
+ s.append("text-align: %s" % val)
+ elif key == 'valign':
+ s.append("vertical-align: %s" % val)
+ # Ignore unknown keys
+ if key not in self._allowed_table_attrs[prefix]:
+ continue
+ result[key] = val
+ st = result.get('style', '').split(';')
+ st = '; '.join(st + s)
+ st = st.strip(';')
+ st = st.strip()
+ if not st:
+ try:
+ del result['style'] # avoid empty style attr
+ except:
+ pass
+ else:
+ result['style'] = st
+ #logging.debug("_checkTableAttr returns %r" % result)
+ return result
+
+
+ def table(self, on, attrs=None, **kw):
+ """ Create table
+
+ @param on: start table
+ @param attrs: table attributes
+ @rtype: string
+ @return start or end tag of a table
+ """
+ result = []
+ if on:
+ # Open div to get correct alignment with table width smaller
+ # than 100%
+ result.append(self._open('div', newline=1))
+
+ # Open table
+ if not attrs:
+ attrs = {}
+ else:
+ attrs = self._checkTableAttr(attrs, 'table')
+ result.append(self._open('table', newline=1, attr=attrs,
+ allowed_attrs=self._allowed_table_attrs['table'],
+ **kw))
+ result.append(self._open('tbody', newline=1))
+ else:
+ # Close tbody, table, and then div
+ result.append(self._close('tbody'))
+ result.append(self._close('table'))
+ result.append(self._close('div'))
+
+ return ''.join(result)
+
+ def table_row(self, on, attrs=None, **kw):
+ tag = 'tr'
+ if on:
+ if not attrs:
+ attrs = {}
+ else:
+ attrs = self._checkTableAttr(attrs, 'row')
+ return self._open(tag, newline=1, attr=attrs,
+ allowed_attrs=self._allowed_table_attrs['row'],
+ **kw)
+ return self._close(tag) + '\n'
+
+ def table_cell(self, on, attrs=None, **kw):
+ tag = 'td'
+ if on:
+ if not attrs:
+ attrs = {}
+ else:
+ attrs = self._checkTableAttr(attrs, '')
+ return ' ' + self._open(tag, attr=attrs,
+ allowed_attrs=self._allowed_table_attrs[''],
+ **kw)
+ return self._close(tag) + '\n'
+
+ def text(self, text, **kw):
+ txt = FormatterBase.text(self, text, **kw)
+ if kw:
+ return self._open('span', **kw) + txt + self._close('span')
+ return txt
+
+ def escapedText(self, text, **kw):
+ txt = wikiutil.escape(text)
+ if kw:
+ return self._open('span', **kw) + txt + self._close('span')
+ return txt
+
+ def rawHTML(self, markup):
+ return markup
+
+ def sysmsg(self, on, **kw):
+ tag = 'div'
+ if on:
+ return self._open(tag, attr={'class': 'message'}, **kw)
+ return self._close(tag)
+
+ def div(self, on, **kw):
+ css_class = kw.get('css_class')
+ # the display of comment class divs depends on a user setting:
+ if css_class and 'comment' in css_class.split():
+ style = kw.get('style')
+ display = self.request.user.show_comments and "display:''" or "display:none"
+ if not style:
+ style = display
+ else:
+ style += "; %s" % display
+ kw['style'] = style
+ tag = 'div'
+ if on:
+ return self._open(tag, **kw)
+ return self._close(tag)
+
+ def span(self, on, **kw):
+ css_class = kw.get('css_class')
+ # the display of comment class spans depends on a user setting:
+ if css_class and 'comment' in css_class.split():
+ style = kw.get('style')
+ display = self.request.user.show_comments and "display:''" or "display:none"
+ if not style:
+ style = display
+ else:
+ style += "; %s" % display
+ kw['style'] = style
+ tag = 'span'
+ if on:
+ return self._open(tag, **kw)
+ return self._close(tag)
+
+ def sanitize_to_id(self, text):
+ return wikiutil.anchor_name_from_text(text)
+
diff --git a/wiki-lenny/share/security.__init__.orig.py b/wiki-lenny/share/security.__init__.orig.py
new file mode 100644
index 00000000..dd2e0b04
--- /dev/null
+++ b/wiki-lenny/share/security.__init__.orig.py
@@ -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 ,
+ 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 access permission for user on page
+
+ 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 .
+ """
+ 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 ? 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 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 is known AND has logged in using a trusted
+ authentication method.
+ Does not work for subsription emails that should be sent to ,
+ 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)
+
diff --git a/wiki-lenny/share/security.__init__.py b/wiki-lenny/share/security.__init__.py
new file mode 100644
index 00000000..4efb6844
--- /dev/null
+++ b/wiki-lenny/share/security.__init__.py
@@ -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 ,
+ 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 access permission for user on page
+
+ 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 .
+ """
+ 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 ? 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 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 is known AND has logged in using a trusted
+ authentication method.
+ Does not work for subsription emails that should be sent to ,
+ 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