scripts/wiki/wikiacl.py
chove a79fd2d23c modification pour la prise en compte du tag PagePublique
darcs-hash:20060424144554-4ec08-686c0ec07d8691c40e0396da8bd9486f5c9cf266.gz
2006-04-24 16:45:54 +02:00

407 lines
14 KiB
Python
Raw Blame History

# -*- coding: iso-8859-1 -*-
"""
MoinMoin Access Control Lists
@copyright: 2003 by Thomas Waldmann, http://linuxwiki.de/ThomasWaldmann
@copyright: 2003 by Gustavo Niemeyer, http://moin.conectiva.com.br/GustavoNiemeyer
@license: GNU GPL, see COPYING for details.
"""
import re
from MoinMoin import user, search
#### HACK SAUVAGE 1/4
import sys
sys.path.append('/usr/scripts/gestion/')
from iptools import is_crans
#### FIN DU HACK 1/4
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_enabled
If true will enable ACL support.
Default: 0
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"]
#### HACK SAUVAGE 2/4
special_users = ["All", "Known", "Trusted", "Crans", "NoCrans"]
#### FIN DU HACK 2/4
def __init__(self, request, lines=[]):
"""Initialize an ACL, starting from <nothing>.
"""
self.setLines(request.cfg, lines)
def setLines(self, cfg, lines=[]):
self.clean()
self.addBefore(cfg)
if not lines:
self.addDefault(cfg)
else:
for line in lines:
self.addLine(cfg, line)
self.addAfter(cfg)
def clean(self):
self.acl = [] # [ ('User', {"read": 0, ...}), ... ]
self.acl_lines = []
self._is_group = {}
def addBefore(self, cfg):
self.addLine(cfg, cfg.acl_rights_before, remember=0)
def addDefault(self, cfg):
self.addLine(cfg, cfg.acl_rights_default, remember=0)
def addAfter(self, cfg):
self.addLine(cfg, cfg.acl_rights_after, remember=0)
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
"""
# FIXME: should compile this once and cache (in cfg?)
group_re = re.compile(cfg.page_group_regex)
# 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.addDefault(cfg)
continue
for entry in entries:
if group_re.search(entry):
self._is_group[entry] = 1
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.
"""
if not request.cfg.acl_enabled:
# everybody may read and write:
if dowhat in ["read", "write",]:
return 1
# only known users may do some more dangerous things:
if request.user.valid:
if dowhat in ["delete", "revert",]:
return 1
# in any other case, we better disallow it:
return 0
is_group_member = request.dicts.has_member
allowed = None
for entry, rightsdict in self.acl:
if entry in self.special_users:
handler = getattr(self, "_special_"+entry, None)
allowed = handler(request, name, dowhat, rightsdict)
elif self._is_group.get(entry) and is_group_member(entry, name):
allowed = rightsdict.get(dowhat)
elif entry == name:
allowed = rightsdict.get(dowhat)
if allowed is not None:
return allowed
return 0
def getString(self, b='#acl ', e='\n'):
"""print the acl strings we were fed with"""
return ''.join(["%s%s%s" % (b,l,e) for l in self.acl_lines])
def _special_All(self, request, name, dowhat, rightsdict):
if dowhat == "read" and is_page_public(request):
return True
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
#### HACK SAUVAGE 3/4
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 3/4
def _special_Trusted(self, request, name, dowhat, rightsdict):
""" check if user <name> is known AND even has logged in using a password.
does not work for subsription emails that should be sent to <user>,
as he is not logged in in that case.
"""
if request.user.trusted and name == request.user.name:
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 aclstirng: 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:
# XXX TODO disallow : and , in usernames
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, body):
""" Parse acl lines on page and return ACL object
Use ACL object may(request, dowhat) to get acl rights.
"""
if not request.cfg.acl_enabled:
return AccessControlList(request)
acl_lines = []
while body and body[0] == '#':
# extract first line
try:
line, body = body.split('\n', 1)
except ValueError:
line = body
body = ''
# end parsing on empty (invalid) PI
if line == "#":
break
# skip comments (lines with two hash marks)
if line[1] == '#':
continue
tokens = line.split(None, 1)
if tokens[0].lower() == "#acl":
if len(tokens) == 2:
args = tokens[1].rstrip()
else:
args = ""
acl_lines.append(args)
return AccessControlList(request, acl_lines)
#### HACK SAUVAGE 4/4
def is_page_public(request):
#return True
## On recherche si la page est publique
this_page = request.page.page_name
query = search.QueryParser().parse_query(u'Cat<EFBFBD>goriePagePublique')
page = search.Page(request, this_page)
result = query.search(page)
if result:
return True
else:
return None
#### FIn DU HACK 4/4