Short description

This patch add an "attach" acl entry, that control whether or not an user can attach files to a specific page.

Why?

MoinMoin offers two features that help prevent abuse of an open wiki:

Unfortunately, it's not that easy when it comes to attachments.

It is therefore much more difficult to prevent abuse of attachements than texts.

This added ACL entry would enable a site administrator to restrict adding attachments to trusted users (as attachments are much more difficult to monitor), while keeping the wiki open to add or change pages.

Restricting access to Know users is not enough to prevent abuse. Bots are able to register to a wiki and the add attachments.

This feature will be useful to all user hosting an open wiki on the internet.

Questions

Shouldn't the patch rather check "write" AND "attach" rights instead of only "attach" rights?

Or use some "readattach", "writeattach", "deleteattach" rights?

(!) BTW, when UnifyPagesAndAttachments gets implemented, files will just have normal ACLs, like pages do.

Patch to moin-1.6

# HG changeset patch
# User Jean-Philippe Guérard <jean-philippe.guerard@tigreraye.org>
# Date 1178928781 -7200
# Node ID 4352478d65d7c8ad72391bbee2a0c739c1b81c8e
# Parent  dc9a3809af61aa74bdb4861f1ab7d02f8b730c0e
Implementation of an attach ACL entry.

diff -r dc9a3809af61 -r 4352478d65d7 MoinMoin/_tests/test_security.py
--- a/MoinMoin/_tests/test_security.py  Mon May 07 22:50:51 2007 +0200
+++ b/MoinMoin/_tests/test_security.py  Sat May 12 02:13:01 2007 +0200
@@ -200,6 +200,7 @@ class AclTestCase(unittest.TestCase):
         acl_rights = [
             "Admin1,Admin2:read,write,delete,revert,admin  "
             "Admin3:read,write,admin  "
+            "Admin4:read,write,attach,admin  "
             "JoeDoe:read,write  "
             "name with spaces,another one:read,write  "
             "CamelCase,extended name:read,write  "
@@ -215,6 +216,7 @@ class AclTestCase(unittest.TestCase):
             ('Admin1',              ('read', 'write', 'admin', 'revert', 'delete')),
             ('Admin2',              ('read', 'write', 'admin', 'revert', 'delete')),
             ('Admin3',              ('read', 'write', 'admin')),
+            ('Admin4',              ('read', 'write', 'attach', 'admin')),
             ('JoeDoe',              ('read', 'write')),
             ('SomeGuy',             ('read',)),
             # Extended names or mix of extended and CamelCase
diff -r dc9a3809af61 -r 4352478d65d7 MoinMoin/action/AttachFile.py
--- a/MoinMoin/action/AttachFile.py     Mon May 07 22:50:51 2007 +0200
+++ b/MoinMoin/action/AttachFile.py     Sat May 12 02:13:01 2007 +0200
@@ -347,7 +347,7 @@ def _build_filelist(request, pagename, s
                 viewlink += ' | <a href="%(baseurl)s/%(urlpagename)s?action=%(action)s&amp;do=install&amp;target=%(urlfile)s">%(label_install)s</a>' % parmdict
             elif (zipfile.is_zipfile(os.path.join(attach_dir, file).encode(config.charset)) and
                 mt.minor == 'zip' and request.user.may.read(pagename) and request.user.may.delete(pagename)
-                and request.user.may.write(pagename)):
+                and request.user.may.attach(pagename)):
                 viewlink += ' | <a href="%(baseurl)s/%(urlpagename)s?action=%(action)s&amp;do=unzip&amp;target=%(urlfile)s">%(label_unzip)s</a>' % parmdict
 
 
@@ -477,7 +477,7 @@ def send_uploadform(pagename, request):
         request.write('<p>%s</p>' % _('You are not allowed to view this page.'))
         return
 
-    writeable = request.user.may.write(pagename)
+    writeable = request.user.may.attach(pagename)
 
     # First send out the upload new attachment form on top of everything else.
     # This avoids usability issues if you have to scroll down a lot to upload
@@ -544,14 +544,14 @@ def execute(pagename, request):
     elif 'do' not in request.form:
         upload_form(pagename, request)
     elif request.form['do'][0] == 'savedrawing':
-        if request.user.may.write(pagename):
+        if request.user.may.attach(pagename):
             save_drawing(pagename, request)
             request.emit_http_headers()
             request.write("OK")
         else:
             msg = _('You are not allowed to save a drawing on this page.')
     elif request.form['do'][0] == 'upload':
-        if request.user.may.write(pagename):
+        if request.user.may.attach(pagename):
             if 'file' in request.form:
                 do_upload(pagename, request)
             else:
@@ -589,7 +589,7 @@ def execute(pagename, request):
         else:
             msg = _('You are not allowed to get attachments from this page.')
     elif request.form['do'][0] == 'unzip':
-        if request.user.may.delete(pagename) and request.user.may.read(pagename) and request.user.may.write(pagename):
+        if request.user.may.delete(pagename) and request.user.may.read(pagename) and request.user.may.attach(pagename):
             unzip_file(pagename, request)
         else:
             msg = _('You are not allowed to unzip attachments of this page.')
@@ -732,7 +732,7 @@ def move_file(request, pagename, new_pag
     _ = request.getText
 
     newpage = Page(request, new_pagename)
-    if newpage.exists(includeDeleted=1) and request.user.may.write(new_pagename) and request.user.may.delete(pagename):
+    if newpage.exists(includeDeleted=1) and request.user.may.attach(new_pagename) and request.user.may.delete(pagename):
         new_attachment_path = os.path.join(getAttachDir(request, new_pagename,
                               create=1), new_attachment).encode(config.charset)
         attachment_path = os.path.join(getAttachDir(request, pagename),
diff -r dc9a3809af61 -r 4352478d65d7 MoinMoin/config/multiconfig.py
--- a/MoinMoin/config/multiconfig.py    Mon May 07 22:50:51 2007 +0200
+++ b/MoinMoin/config/multiconfig.py    Sat May 12 02:13:01 2007 +0200
@@ -188,10 +188,10 @@ class DefaultConfig:
     DesktopEdition = False
 
     # All acl_rights_* lines must use unicode!
-    acl_rights_default = u"Trusted:read,write,delete,revert Known:read,write,delete,revert All:read,write"
+    acl_rights_default = u"Trusted:read,write,attach,delete,revert Known:read,write,delete,revert All:read,write"
     acl_rights_before = u""
     acl_rights_after = u""
-    acl_rights_valid = ['read', 'write', 'delete', 'revert', 'admin']
+    acl_rights_valid = ['read', 'write', 'attach', 'delete', 'revert', 'admin']
     acl_hierarchic = False
 
     actions_excluded = [] # ['DeletePage', 'AttachFile', 'RenamePage', 'test', ]
diff -r dc9a3809af61 -r 4352478d65d7 MoinMoin/security/__init__.py
--- a/MoinMoin/security/__init__.py     Mon May 07 22:50:51 2007 +0200
+++ b/MoinMoin/security/__init__.py     Sat May 12 02:13:01 2007 +0200
@@ -228,7 +228,7 @@ class AccessControlList:
    
        cfg.acl_rights_default
            It is is ONLY used when no other ACLs are given.
-           Default: "Known:read,write,delete All:read,write",
+           Default: "Trusted:read,write,attach,delete,revert Known:read,write,delete,revert All:read,write",
 
        cfg.acl_rights_before
            When the page has ACL entries, this will be inserted BEFORE
@@ -243,7 +243,7 @@ class AccessControlList:
        cfg.acl_rights_valid
            These are the acceptable (known) rights (and the place to
            extend, if necessary).
-           Default: ["read", "write", "delete", "admin"]
+           Default: ["read", "write", "attach", "delete", "admin"]
     '''
 
     special_users = ["All", "Known", "Trusted"] # order is important
diff -r dc9a3809af61 -r 4352478d65d7 wikiconfig.py
--- a/wikiconfig.py     Mon May 07 22:50:51 2007 +0200
+++ b/wikiconfig.py     Sat May 12 02:13:01 2007 +0200
@@ -15,7 +15,7 @@ class Config(DefaultConfig):
     data_underlay_dir = os.path.join(moinmoin_dir, 'wiki', 'underlay')
 
     DesktopEdition = True # give all local users full powers
-    acl_rights_default = u"All:read,write,delete,revert,admin"
+    acl_rights_default = u"All:read,write,attach,delete,revert,admin"
     surge_action_limits = None # no surge protection
     sitename = u'MoinMoin DesktopEdition'
     logo_string = u'<img src="/moin_static160/common/moinmoin.png" alt="MoinMoin Logo">'

Patch to moin-1.7

# HG changeset patch
# User Jean-Philippe Guérard <jean-philippe.guerard@tigreraye.org>
# Date 1178929623 -7200
# Node ID 0c2f60b3c01fb32b8079718b51358023f39fe378
# Parent  a63c473a100db0f52c8edea3ddfa9dfcb0749e64
Add an attach ACL entry.

diff -r a63c473a100d -r 0c2f60b3c01f MoinMoin/_tests/test_security.py
--- a/MoinMoin/_tests/test_security.py  Sat May 05 15:26:15 2007 +0200
+++ b/MoinMoin/_tests/test_security.py  Sat May 12 02:27:03 2007 +0200
@@ -199,6 +199,7 @@ class TestAcl(unittest.TestCase):
         acl_rights = [
             "Admin1,Admin2:read,write,delete,revert,admin  "
             "Admin3:read,write,admin  "
+            "Admin4:read,write,attach,admin  "
             "JoeDoe:read,write  "
             "name with spaces,another one:read,write  "
             "CamelCase,extended name:read,write  "
@@ -214,6 +215,7 @@ class TestAcl(unittest.TestCase):
             ('Admin1',              ('read', 'write', 'admin', 'revert', 'delete')),
             ('Admin2',              ('read', 'write', 'admin', 'revert', 'delete')),
             ('Admin3',              ('read', 'write', 'admin')),
+            ('Admin4',              ('read', 'write', 'attach', 'admin')),
             ('JoeDoe',              ('read', 'write')),
             ('SomeGuy',             ('read',)),
             # Extended names or mix of extended and CamelCase
diff -r a63c473a100d -r 0c2f60b3c01f MoinMoin/action/AttachFile.py
--- a/MoinMoin/action/AttachFile.py     Sat May 05 15:26:15 2007 +0200
+++ b/MoinMoin/action/AttachFile.py     Sat May 12 02:27:03 2007 +0200
@@ -347,7 +347,7 @@ def _build_filelist(request, pagename, s
                 viewlink += ' | <a href="%(baseurl)s/%(urlpagename)s?action=%(action)s&amp;do=install&amp;target=%(urlfile)s">%(label_install)s</a>' % parmdict
             elif (zipfile.is_zipfile(os.path.join(attach_dir, file).encode(config.charset)) and
                 mt.minor == 'zip' and request.user.may.read(pagename) and request.user.may.delete(pagename)
-                and request.user.may.write(pagename)):
+                and request.user.may.attach(pagename)):
                 viewlink += ' | <a href="%(baseurl)s/%(urlpagename)s?action=%(action)s&amp;do=unzip&amp;target=%(urlfile)s">%(label_unzip)s</a>' % parmdict
 
 
@@ -477,7 +477,7 @@ def send_uploadform(pagename, request):
         request.write('<p>%s</p>' % _('You are not allowed to view this page.'))
         return
 
-    writeable = request.user.may.write(pagename)
+    writeable = request.user.may.attach(pagename)
 
     # First send out the upload new attachment form on top of everything else.
     # This avoids usability issues if you have to scroll down a lot to upload
@@ -544,14 +544,14 @@ def execute(pagename, request):
     elif 'do' not in request.form:
         upload_form(pagename, request)
     elif request.form['do'][0] == 'savedrawing':
-        if request.user.may.write(pagename):
+        if request.user.may.attach(pagename):
             save_drawing(pagename, request)
             request.emit_http_headers()
             request.write("OK")
         else:
             msg = _('You are not allowed to save a drawing on this page.')
     elif request.form['do'][0] == 'upload':
-        if request.user.may.write(pagename):
+        if request.user.may.attach(pagename):
             if 'file' in request.form:
                 do_upload(pagename, request)
             else:
@@ -589,7 +589,7 @@ def execute(pagename, request):
         else:
             msg = _('You are not allowed to get attachments from this page.')
     elif request.form['do'][0] == 'unzip':
-        if request.user.may.delete(pagename) and request.user.may.read(pagename) and request.user.may.write(pagename):
+        if request.user.may.delete(pagename) and request.user.may.read(pagename) and request.user.may.attach(pagename):
             unzip_file(pagename, request)
         else:
             msg = _('You are not allowed to unzip attachments of this page.')
@@ -732,7 +732,7 @@ def move_file(request, pagename, new_pag
     _ = request.getText
 
     newpage = Page(request, new_pagename)
-    if newpage.exists(includeDeleted=1) and request.user.may.write(new_pagename) and request.user.may.delete(pagename):
+    if newpage.exists(includeDeleted=1) and request.user.may.attach(new_pagename) and request.user.may.delete(pagename):
         new_attachment_path = os.path.join(getAttachDir(request, new_pagename,
                               create=1), new_attachment).encode(config.charset)
         attachment_path = os.path.join(getAttachDir(request, pagename),
diff -r a63c473a100d -r 0c2f60b3c01f MoinMoin/config/multiconfig.py
--- a/MoinMoin/config/multiconfig.py    Sat May 05 15:26:15 2007 +0200
+++ b/MoinMoin/config/multiconfig.py    Sat May 12 02:27:03 2007 +0200
@@ -208,10 +208,10 @@ class DefaultConfig:
     DesktopEdition = False
 
     # All acl_rights_* lines must use unicode!
-    acl_rights_default = u"Trusted:read,write,delete,revert Known:read,write,delete,revert All:read,write"
+    acl_rights_default = u"Trusted:read,write,attach,delete,revert Known:read,write,delete,revert All:read,write"
     acl_rights_before = u""
     acl_rights_after = u""
-    acl_rights_valid = ['read', 'write', 'delete', 'revert', 'admin']
+    acl_rights_valid = ['read', 'write', 'attach', 'delete', 'revert', 'admin']
 
     actions_excluded = [] # ['DeletePage', 'AttachFile', 'RenamePage', 'test', ]
     allow_xslt = False
diff -r a63c473a100d -r 0c2f60b3c01f MoinMoin/security/__init__.py
--- a/MoinMoin/security/__init__.py     Sat May 05 15:26:15 2007 +0200
+++ b/MoinMoin/security/__init__.py     Sat May 12 02:27:03 2007 +0200
@@ -156,7 +156,7 @@ class AccessControlList:
    
        cfg.acl_rights_default
            It is is ONLY used when no other ACLs are given.
-           Default: "Known:read,write,delete All:read,write",
+           Default: "Trusted:read,write,attach,delete,revert Known:read,write,delete,revert All:read,write",
 
        cfg.acl_rights_before
            When the page has ACL entries, this will be inserted BEFORE
@@ -171,7 +171,7 @@ class AccessControlList:
        cfg.acl_rights_valid
            These are the acceptable (known) rights (and the place to
            extend, if necessary).
-           Default: ["read", "write", "delete", "admin"]
+           Default: ["read", "write", "attach", "delete", "admin"]
     '''
 
     special_users = ["All", "Known", "Trusted"] # order is important
diff -r a63c473a100d -r 0c2f60b3c01f wiki/config/wikiconfig.py
--- a/wiki/config/wikiconfig.py Sat May 05 15:26:15 2007 +0200
+++ b/wiki/config/wikiconfig.py Sat May 12 02:27:03 2007 +0200
@@ -93,7 +93,7 @@ class Config(DefaultConfig):
     # IMPORTANT: grant yourself admin rights! replace YourName with
     # your user name. See HelpOnAccessControlLists for more help.
     # All acl_rights_xxx options must use unicode [Unicode]
-    #acl_rights_before = u"YourName:read,write,delete,revert,admin"
+    #acl_rights_before = u"YourName:read,write,attach,delete,revert,admin"
 
     # Link spam protection for public wikis (Uncomment to enable)
     # Needs a reliable internet connection.
diff -r a63c473a100d -r 0c2f60b3c01f wiki/config/wikifarm/farmconfig.py
--- a/wiki/config/wikifarm/farmconfig.py        Sat May 05 15:26:15 2007 +0200
+++ b/wiki/config/wikifarm/farmconfig.py        Sat May 12 02:27:03 2007 +0200
@@ -110,7 +110,7 @@ class FarmConfig(DefaultConfig):
     # IMPORTANT: grant yourself admin rights! replace YourName with
     # your user name. See HelpOnAccessControlLists for more help.
     # All acl_rights_xxx options must use unicode [Unicode]
-    #acl_rights_before = u"YourName:read,write,delete,revert,admin"
+    #acl_rights_before = u"YourName:read,write,attach,delete,revert,admin"
 
     # Link spam protection for public wikis (uncomment to enable).
     # Needs a reliable internet connection.
diff -r a63c473a100d -r 0c2f60b3c01f wikiconfig.py
--- a/wikiconfig.py     Sat May 05 15:26:15 2007 +0200
+++ b/wikiconfig.py     Sat May 12 02:27:03 2007 +0200
@@ -15,7 +15,7 @@ class Config(DefaultConfig):
     data_underlay_dir = os.path.join(moinmoin_dir, 'wiki', 'underlay')
 
     DesktopEdition = True # give all local users full powers
-    acl_rights_default = u"All:read,write,delete,revert,admin"
+    acl_rights_default = u"All:read,write,attach,delete,revert,admin"
     surge_action_limits = None # no surge protection
     sitename = u'MoinMoin DesktopEdition'
     logo_string = u'<img src="/moin_static170/common/moinmoin.png" alt="MoinMoin Logo">'

Diff to moin-1.5.8 (tested)

diff -ur a/MoinMoin/action/AttachFile.py b/MoinMoin/action/AttachFile.py
--- a/MoinMoin/action/AttachFile.py 2007-05-05 22:48:23.000000000 +0000
+++ b/MoinMoin/action/AttachFile.py       2007-05-17 08:52:07.000000000 +0000
@@ -276,7 +276,7 @@
                 viewlink += ' | <a href="%(baseurl)s/%(urlpagename)s?action=%(action)s&amp;do=install&amp;target=%(urlfile)s">%(label_install)s</a>' % parmdict
             elif (zipfile.is_zipfile(os.path.join(attach_dir,file).encode(config.charset)) and
                 request.user.may.read(pagename) and request.user.may.delete(pagename)
-                and request.user.may.write(pagename)):
+                and request.user.may.attach(pagename)):
                 viewlink += ' | <a href="%(baseurl)s/%(urlpagename)s?action=%(action)s&amp;do=unzip&amp;target=%(urlfile)s">%(label_unzip)s</a>' % parmdict


@@ -406,7 +406,7 @@
     request.write('<h2>' + _("Attached Files") + '</h2>')
     request.write(_get_filelist(request, pagename))

-    if not request.user.may.write(pagename):
+    if not request.user.may.attach(pagename):
         request.write('<p>%s</p>' % _('You are not allowed to attach a file to this page.'))
         return

@@ -467,7 +467,7 @@
     if action_name in request.cfg.actions_excluded:
         msg = _('File attachments are not allowed in this wiki!')
     elif request.form.has_key('filepath'):
-        if request.user.may.write(pagename):
+        if request.user.may.attach(pagename):
             save_drawing(pagename, request)
             request.http_headers()
             request.write("OK")
@@ -476,7 +476,7 @@
     elif do is None:
         upload_form(pagename, request)
     elif do == 'upload':
-        if request.user.may.write(pagename):
+        if request.user.may.attach(pagename):
             if request.form.has_key('file'):
                 do_upload(pagename, request)
             else:
@@ -514,7 +514,7 @@
         else:
             msg = _('You are not allowed to get attachments from this page.')
     elif do == 'unzip':
-         if request.user.may.delete(pagename) and request.user.may.read(pagename) and request.user.may.write(pagename):
+         if request.user.may.delete(pagename) and request.user.may.read(pagename) and request.user.may.attach(pagename):
             unzip_file(pagename, request)
          else:
             msg = _('You are not allowed to unzip attachments of this page.')
@@ -680,7 +680,7 @@
     _ = request.getText

     newpage = Page(request, new_pagename)
-    if newpage.exists(includeDeleted=1) and request.user.may.write(new_pagename) and request.user.may.delete(pagename):
+    if newpage.exists(includeDeleted=1) and request.user.may.attach(new_pagename) and request.user.may.delete(pagename):
         new_attachment_path = os.path.join(getAttachDir(request, new_pagename,
                               create=1), new_attachment).encode(config.charset)
         attachment_path = os.path.join(getAttachDir(request, pagename),
diff -ur a/MoinMoin/multiconfig.py b/MoinMoin/multiconfig.py
--- a/MoinMoin/multiconfig.py       2007-05-12 19:19:57.000000000 +0000
+++ b/MoinMoin/multiconfig.py     2007-05-17 08:52:39.000000000 +0000
@@ -173,10 +173,10 @@
     """ default config values """

     # All acl_right lines must use unicode!
-    acl_rights_default = u"Trusted:read,write,delete,revert Known:read,write,delete,revert All:read,write"
+    acl_rights_default = u"Trusted:read,write,attach,delete,revert Known:read,write,delete,revert All:read,write"
     acl_rights_before = u""
     acl_rights_after = u""
-    acl_rights_valid = ['read', 'write', 'delete', 'revert', 'admin']
+    acl_rights_valid = ['read', 'write', 'attach', 'delete', 'revert', 'admin']

     actions_excluded = [] # ['DeletePage', 'AttachFile', 'RenamePage']
     allow_xslt = 0


CategoryFeaturePatched

MoinMoin: FeatureRequest/AddAttachACLEntry (last edited 2007-10-29 19:12:35 by localhost)