Explorar el Código

Change a couple of things in regex-based rights manager

Guillaume Ayoub hace 12 años
padre
commit
faa331ccc3
Se han modificado 3 ficheros con 64 adiciones y 106 borrados
  1. 1 7
      config
  2. 1 3
      radicale/config.py
  3. 62 96
      radicale/rights/regex.py

+ 1 - 7
config

@@ -99,16 +99,10 @@ http_password_parameter =
 # Value: None | owner_only | owner_write | from_file | regex
 type = None
 
-# File for rights management from_file
+# File for rights management from_file or regex
 file = ~/.config/radicale/rights
 
 
-# File for rights management regex
-regex_file =~/.config/radicale/regex
-# use this as alternative method
-regex_secondary = owner_only
-
-
 [storage]
 # Storage backend
 # Value: filesystem | database

+ 1 - 3
radicale/config.py

@@ -74,9 +74,7 @@ INITIAL_CONFIG = {
         "http_password_parameter": ""},
     "rights": {
         "type": "None",
-        "file": "",
-        "regex_file": "",
-        "regex_secondary": "owner_only"},
+        "file": ""},
     "storage": {
         "type": "filesystem",
         "filesystem_folder": os.path.expanduser(

+ 62 - 96
radicale/rights/regex.py

@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 #
 # This file is part of Radicale Server - Calendar Server
-# Copyright © 2012-2013 Guillaume Ayoub
+# Copyright © 2013 Guillaume Ayoub
 #
 # This library is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -19,142 +19,108 @@
 """
 Regex-based rights.
 
-You can define an secondary rights management method. If not, it will use
-"owner_only", which implies the owners have all rights on their collections.
-Secondary rights management method is specified in the config (section
-"right", key "regex_secondary").
-
 Regexes are read from a file whose name is specified in the config (section
-"right", key "regex_file").
-
-Test string for regex is "user|collection" per default, because "|" is
-not allowed in an URL. You may set this string per rule, using Python's
-ConfigParser interpolation: %(user)s and %(collection)s
-In fact you may also set user/collection to a fixed value per rule. But you 
-should consider using a secondary rights management method (e.g. "from_file").
+"right", key "file").
 
+Authentication login is matched against the "user" key, and collection's path
+is matched against the "collection" key. You can use Python's ConfigParser
+interpolation values %(login)s and %(path)s. You can also get groups from the
+user regex in the collection with {0}, {1}, etc.
 
 Section names are only used for naming the rule.
-Leading or Ending "/"s are trimmed from collection names.
 
-Example:
+Leading or ending slashes are trimmed from collection's path.
+
+Examples:
 
 # This means all users starting with "admin" may read any collection
 [admin]
-regex: ^admin.*\|.+?$
+user: ^admin.*\|.+?$
+collection: .*
 permission: r
 
 # This means all users may read and write any collection starting with public.
 # We do so by just not testing against the user string.
 [public]
-glue: %(collection)s
-regex: ^public(/.+)?$
+user: .*
+collection: ^public(/.+)?$
 permission: rw
 
-# A little more complex:
-# Give read access to users from a domain for all collections of all the users:
+# A little more complex: give read access to users from a domain for all
+# collections of all the users (ie. user@domain.tld can read domain/*).
 [domain-wide-access]
-regex:  ^.+@(.+)\|.+@\1(/.+)?$
+user: ^.+@(.+)\..+$
+collection: ^{0}/.+$
+permission: r
+
+# Allow authenticated user to read all collections
+[allow-everyone-read]
+user: .*
+collection: .*
 permission: r
 
+# Give write access to owners
+[owner-write]
+user: .*
+collection: ^%(login)s/.+$
+permission: w
 
 """
 
 import os.path
 import re
 
-from radicale import config, log, rights
+from radicale import config, log
+
 # Manage Python2/3 different modules
 # pylint: disable=F0401
 try:
-    from configparser import (
-        ConfigParser as ConfigParser, NoSectionError, NoOptionError)
+    from configparser import ConfigParser
 except ImportError:
-    from ConfigParser import (
-        ConfigParser as ConfigParser, NoSectionError, NoOptionError)
+    from ConfigParser import ConfigParser
 # pylint: enable=F0401
 
 
 FILENAME = (
-    os.path.expanduser(config.get("rights", "regex_file")) or
+    os.path.expanduser(config.get("rights", "file")) or
     log.LOGGER.error("No file name configured for rights type 'regex'"))
-    
-    
-def _read_regex(user, collection):
-    """Load regex from file."""
-    regex = ConfigParser({'user': user, 'collection': collection})
+
+
+def _read_from_sections(user, collection, permission):
+    """Get regex sections."""
+    log.LOGGER.debug("Reading regex from file %s" % FILENAME)
+    regex = ConfigParser({"login": user, "path": collection})
     if not regex.read(FILENAME):
         log.LOGGER.error(
-            "File '%s' not found for rights management type 'regex'" % FILENAME)
-    return regex
-    
-def _read_from_sections(user, collection, perm):
-    """Get regex sections."""    
-    regex = _read_regex(user, collection)
-    try:  
-        for section in regex.sections():
-            if _matches_section(user, collection, section):
-                if perm in regex.get(section, "permission"):
-                    return True
-    except (NoSectionError, NoOptionError):
-        return False
-    return False
-    
-def _matches_section(user, collection, section):
-    """Regex section against user and collection"""
-    log.LOGGER.debug("Reading regex from file %s" % FILENAME)
-    regex = _read_regex(user, collection)
-    log.LOGGER.debug("Match against section '%s'" % section)
-
-    try:
-        test = regex.get(section, 'glue')
-    except (NoOptionError):
-        test = user+'|'+collection;
-    
-    try: 
-        match = re.match(regex.get(section, 'regex'), test)
-        if match:
-            return True;
-        log.LOGGER.debug("Test-String '%s' does not match against '%s' from section '%s'" % \
-            (test, regex.get(section, 'regex'), section))
-    except (NoSectionError, NoOptionError):
+            "File '%s' not found for rights management type 'regex'" %
+            FILENAME)
         return False
+
+    for section in regex.sections():
+        re_user = regex.get(section, "user")
+        re_collection = regex.get(section, "collection")
+        log.LOGGER.debug(
+            "Test if '%s:%s' matches against '%s:%s' from section '%s'" % (
+                user, collection, re_user, re_collection, section))
+        user_match = re.match(re_user, user)
+        if user_match:
+            re_collection = re_collection.format(*user_match.groups())
+            if re.match(re_collection, collection):
+                log.LOGGER.debug("Section '%s' matches" % section)
+                if permission in regex.get(section, "permission"):
+                    return True
+        log.LOGGER.debug("Section '%s' does not match" % section)
+
     return False
-    
-    
-
-def _get_secondary():
-    """Get secondary rights management method"""
-    try:  
-        secondary = config.get("rights", "regex_secondary")
-        if not secondary or secondary == None:
-            secondary = 'owner_only'
-        
-        root_module = __import__(
-            "rights.%s" % secondary, globals=globals(), level=2)
-        module = getattr(root_module, secondary)
-        return module
-    except (ImportError, NoSectionError, NoOptionError):
-        return None
-    
+
 
 def read_authorized(user, collection):
     """Check if the user is allowed to read the collection."""
-    if user is None:
-        return False
-    elif _get_secondary() != None and _get_secondary().read_authorized(user, collection):
-        return True
-    else:
-        return _read_from_sections(
-            user, collection.url.rstrip("/") or "/", "r")
+    return user and _read_from_sections(
+        user, collection.url.rstrip("/") or "/", "r")
 
 
 def write_authorized(user, collection):
     """Check if the user is allowed to write the collection."""
-    if user is None:
-        return False
-    elif _get_secondary() != None and _get_secondary().write_authorized(user, collection):
-        return True
-    else:
-        return _read_from_sections(
-            user, collection.url.rstrip("/") or "/", "w")
+    return user and _read_from_sections(
+        user, collection.url.rstrip("/") or "/", "w")