[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:
parent
73e1af5309
commit
cd5d080e49
10 changed files with 4410 additions and 0 deletions
501
wiki-lenny/share/security.__init__.py
Normal file
501
wiki-lenny/share/security.__init__.py
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue