Procházet zdrojové kódy

Code cleaned and modules renamed

*Radicale is probably broken now*
Guillaume Ayoub před 13 roky
rodič
revize
45afac5353

+ 8 - 2
config

@@ -36,8 +36,8 @@ request = utf-8
 stock = utf-8
 
 
-[acl]
-# Access method
+[auth]
+# Authentication method
 # Value: None | htpasswd | LDAP | PAM | courier
 type = None
 
@@ -78,6 +78,12 @@ pam_group_membership =
 courier_socket =
 
 
+[rights]
+# Rights management method
+# Value: None | owner_only
+type = None
+
+
 [storage]
 # Storage backend
 type = filesystem

+ 32 - 79
radicale/__init__.py

@@ -200,7 +200,7 @@ class Application(object):
 
         # Check rights
         if not items or not access or function == self.options:
-            # No collection, or no acl, or OPTIONS request: don't check rights
+            # No collection, or no auth, or OPTIONS request: don't check rights
             status, headers, answer = function(environ, items, content, None)
         else:
             # Ask authentication backend to check rights
@@ -213,23 +213,22 @@ class Application(object):
             else:
                 user = password = None
 
-
             if access.is_authenticated(user, password):
-
                 last_collection_allowed = None
                 allowed_items = []
                 for item in items:
                     log.LOGGER.debug("Testing %s" % (item.name))
                     if not isinstance(item, ical.Collection):
                         # item is not a colleciton, it's the child of the last
-                        # collection we've met in the loop. Only add this item if
-                        # this last collection was allowed.                        log.LOGGER.info("not a collection: " + collection.name)
-                        #  collections.append(collection)
+                        # collection we've met in the loop. Only add this item
+                        # if this last collection was allowed.
                         if last_collection_allowed:
                             allowed_items.append(item)
                     else:
-                        if access.may_read(user, item) or access.may_write(user, item):
-                            log.LOGGER.info(user + "has access to " + item.name)
+                        if access.read_authorized(user, item) or \
+                                access.write_authorized(user, item):
+                            log.LOGGER.info("%s has access to %s" % (
+                                user, item.name))
                             last_collection_allowed = True
                             allowed_items.append(item)
                         else:
@@ -242,18 +241,17 @@ class Application(object):
                 else:
                     # Good user and no collections found, redirect user to home
                     location = "/%s/" % str(quote(user))
-                    if path != location: 
+                    if path == location:
+                        # Send answer anyway since else we're getting into a
+                        # redirect loop
+                        status, headers, answer = function(
+                            environ, allowed_items, content, user)
+                    else:
                         log.LOGGER.info("redirecting to %s" % location)
                         status = client.FOUND
                         headers = {"Location": location}
                         answer = "Redirecting to %s" % location
-                    else:
-                        # Send answer anyway since else we're getting into a redirect loop
-                        status, headers, answer = function(
-                            environ, allowed_items, content, user)
-                            
             else:
-                
                 # Unknown or unauthorized user
                 log.LOGGER.info(
                     "%s refused" % (user or "Anonymous user"))
@@ -262,9 +260,6 @@ class Application(object):
                     "WWW-Authenticate":
                     "Basic realm=\"Radicale Server - Password Required\""}
                 answer = None
-                    
-                
-                
 
         # Set content length
         if answer:
@@ -279,18 +274,14 @@ class Application(object):
 
         # Return response content
         return [answer] if answer else []
-    
-    
-    
+
     def response_not_allowed(self):
+        """Return a standard "not allowed" response."""
         headers = {
             "WWW-Authenticate":
             "Basic realm=\"Radicale Server - Password Required\""}
         return client.FORBIDDEN, headers, None
 
-
-
-
     # All these functions must have the same parameters, some are useless
     # pylint: disable=W0612,W0613,R0201
 
@@ -311,20 +302,15 @@ class Application(object):
             etag = environ.get("HTTP_IF_MATCH", item.etag).replace("\\", "")
             if etag == item.etag:
                 # No ETag precondition or precondition verified, delete item
-                if access.may_write(user, collection):
+                if access.write_authorized(user, collection):
                     answer = xmlutils.delete(environ["PATH_INFO"], collection)
                     return client.OK, {}, answer
                 else:
                     return self.response_not_allowed()
-                    
 
         # No item or ETag precondition not verified, do not delete item
         return client.PRECONDITION_FAILED, {}, None
 
-
-
-
-
     def get(self, environ, collections, content, user):
         """Manage GET request.
 
@@ -346,23 +332,23 @@ class Application(object):
             # Get collection item
             item = collection.get_item(item_name)
             if item:
-                if access.may_read(user, collection):
+                if access.read_authorized(user, collection):
                     items = collection.timezones
                     items.append(item)
                     answer_text = ical.serialize(
                         collection.tag, collection.headers, items)
                     etag = item.etag
                 else:
-                    return self.response_not_allowed()                    
+                    return self.response_not_allowed()
             else:
                 return client.GONE, {}, None
         else:
             # Create the collection if it does not exist
-            if not collection.exists and access.may_write(user, collection):
+            if not collection.exists and access.write_authorized(user, collection):
                 log.LOGGER.debug("creating collection " + collection.name)
                 collection.write()
 
-            if access.may_read(user, collection):
+            if access.read_authorized(user, collection):
                 # Get whole collection
                 answer_text = collection.text
                 etag = collection.etag
@@ -376,18 +362,11 @@ class Application(object):
         answer = answer_text.encode(self.encoding)
         return client.OK, headers, answer
 
-
-
-
     def head(self, environ, collections, content, user):
         """Manage HEAD request."""
         status, headers, answer = self.get(environ, collections, content, user)
         return status, headers, None
 
-
-
-
-
     def mkcalendar(self, environ, collections, content, user):
         """Manage MKCALENDAR request."""
         collection = collections[0]
@@ -399,15 +378,12 @@ class Application(object):
         with collection.props as collection_props:
             for key, value in props.items():
                 collection_props[key] = value
-        if access.may_write(user, collection):
+        if access.write_authorized(user, collection):
             collection.write()
         else:
             return self.response_not_allowed()
         return client.CREATED, {}, None
 
-
-
-
     def mkcol(self, environ, collections, content, user):
         """Manage MKCOL request."""
         collection = collections[0]
@@ -415,15 +391,12 @@ class Application(object):
         with collection.props as collection_props:
             for key, value in props.items():
                 collection_props[key] = value
-        if access.may_write(user, collection):
+        if access.write_authorized(user, collection):
             collection.write()
         else:
             return self.response_not_allowed()
         return client.CREATED, {}, None
 
-
-
-
     def move(self, environ, collections, content, user):
         """Manage MOVE request."""
         from_collection = collections[0]
@@ -439,7 +412,8 @@ class Application(object):
                     to_path, to_name = to_url.rstrip("/").rsplit("/", 1)
                     to_collection = ical.Collection.from_path(
                         to_path, depth="0")[0]
-                    if access.may_write(user, to_collection) and access.may_write(user.from_collection):
+                    if access.write_authorized(user, to_collection) and \
+                            access.write_authorized(user.from_collection):
                         to_collection.append(to_name, item.text)
                         from_collection.remove(from_name)
                         return client.CREATED, {}, None
@@ -455,22 +429,14 @@ class Application(object):
             # Moving collections, not supported
             return client.FORBIDDEN, {}, None
 
-
-
-
-
     def options(self, environ, collections, content, user):
         """Manage OPTIONS request."""
         headers = {
-            "Allow": "DELETE, HEAD, GET, MKCALENDAR, MKCOL, MOVE, " \
-                "OPTIONS, PROPFIND, PROPPATCH, PUT, REPORT",
+            "Allow": ("DELETE, HEAD, GET, MKCALENDAR, MKCOL, MOVE, "
+                      "OPTIONS, PROPFIND, PROPPATCH, PUT, REPORT"),
             "DAV": "1, 2, 3, calendar-access, addressbook, extended-mkcol"}
         return client.OK, headers, None
 
-
-
-
-
     def propfind(self, environ, collections, content, user):
         """Manage PROPFIND request."""
         headers = {
@@ -480,10 +446,6 @@ class Application(object):
             environ["PATH_INFO"], content, collections, user)
         return client.MULTI_STATUS, headers, answer
 
-
-
-
-
     def proppatch(self, environ, collections, content, user):
         """Manage PROPPATCH request."""
         collection = collections[0]
@@ -493,10 +455,6 @@ class Application(object):
             "Content-Type": "text/xml"}
         return client.MULTI_STATUS, headers, answer
 
-
-
-
-
     def put(self, environ, collections, content, user):
         """Manage PUT request."""
         collection = collections[0]
@@ -513,13 +471,13 @@ class Application(object):
             # Case 1: No item and no ETag precondition: Add new item
             # Case 2: Item and ETag precondition verified: Modify item
             # Case 3: Item and no Etag precondition: Force modifying item
-            if access.may_write(user, collection):
+            if access.write_authorized(user, collection):
                 xmlutils.put(environ["PATH_INFO"], content, collection)
                 status = client.CREATED
-                # Try to return the etag in the header
-                # If the added item does't have the same name as the one given by
-                # the client, then there's no obvious way to generate an etag, we
-                # can safely ignore it.
+                # Try to return the etag in the header.
+                # If the added item does't have the same name as the one given
+                # by the client, then there's no obvious way to generate an
+                # etag, we can safely ignore it.
                 new_item = collection.get_item(item_name)
                 if new_item:
                     headers["ETag"] = new_item.etag
@@ -530,16 +488,11 @@ class Application(object):
             status = client.PRECONDITION_FAILED
         return status, headers, None
 
-
-
-
-
-
     def report(self, environ, collections, content, user):
         """Manage REPORT request."""
         collection = collections[0]
         headers = {"Content-Type": "text/xml"}
-        if access.may_read(user, collection):
+        if access.read_authorized(user, collection):
             answer = xmlutils.report(environ["PATH_INFO"], content, collection)
             return client.MULTI_STATUS, headers, answer
         else:

+ 24 - 30
radicale/access.py

@@ -19,49 +19,43 @@
 """
 Radicale access module.
 
-Manages access to collections.
+Manage access to collections.
 
 """
 
-import os
-import sys
-
-from radicale import acl, authorization, log
+from radicale import auth, rights, log
 
+AUTH = None
+RIGHTS = None
 
 
 def load():
-    log.LOGGER.debug("access.load()")
-    global aacl ; aacl = acl.load()
-    global aauthorization ; aauthorization = authorization.load()
-
+    """Load authentication and rights modules."""
+    global AUTH, RIGHTS
+    AUTH = auth.load()
+    RIGHTS = rights.load()
 
 
 def is_authenticated(user, password):
-    if (not user): 
-        # No user given
-        return False
-
-    return aacl.is_authenticated(user, password)
-     
-
+    """Check if the user is authenticated."""
+    return AUTH.is_authenticated(user, password) if user else False
 
 
-def may_read(user, collection):
-    """Check if the user is allowed to read the collection"""
-    
-    user_authorized = aauthorization.read_authorized(user, collection)
-
-    log.LOGGER.debug("read %s %s -- %i" % (user, collection.owner, user_authorized))
+def read_authorized(user, collection):
+    """Check if the user is allowed to read the collection."""
+    if RIGHTS is None:
+        return True
+    user_authorized = RIGHTS.read_authorized(user, collection)
+    log.LOGGER.debug(
+        "Read %s %s -- %i" % (user, collection.owner, user_authorized))
     return user_authorized
 
 
-
-
-def may_write(user, collection):
-    """Check if the user is allowed to write the collection"""
-    
-    user_authorized = aauthorization.write_authorized(user, collection)
-    
-    log.LOGGER.debug("write %s %s -- %i" % (user, collection.owner, user_authorized))
+def write_authorized(user, collection):
+    """Check if the user is allowed to write the collection."""
+    if RIGHTS is None:
+        return True
+    user_authorized = RIGHTS.write_authorized(user, collection)
+    log.LOGGER.debug(
+        "Write %s %s -- %i" % (user, collection.owner, user_authorized))
     return user_authorized

+ 12 - 12
radicale/acl/IMAP.py → radicale/auth/IMAP.py

@@ -17,7 +17,7 @@
 # along with Radicale.  If not, see <http://www.gnu.org/licenses/>.
 
 """
-IMAP ACL.
+IMAP authentication.
 
 Secure authentication based on the ``imaplib`` module.
 
@@ -32,17 +32,17 @@ Python 3.2 or newer is required for TLS.
 
 import imaplib
 
-from radicale import acl, config, log
+from radicale import config, log
 
-IMAP_SERVER = config.get("acl", "imap_auth_host_name")
-IMAP_SERVER_PORT = config.get("acl", "imap_auth_host_port")
+IMAP_SERVER = config.get("auth", "imap_auth_host_name")
+IMAP_SERVER_PORT = config.get("auth", "imap_auth_host_port")
 
 
 def is_authenticated(user, password):
     """Check if ``user``/``password`` couple is valid."""
 
     log.LOGGER.debug(
-        "[IMAP ACL] Connecting to %s:%s." % (IMAP_SERVER, IMAP_SERVER_PORT,))
+        "[IMAP AUTH] Connecting to %s:%s." % (IMAP_SERVER, IMAP_SERVER_PORT,))
     connection = imaplib.IMAP4(host=IMAP_SERVER, port=IMAP_SERVER_PORT)
 
     server_is_local = (IMAP_SERVER == "localhost")
@@ -50,20 +50,20 @@ def is_authenticated(user, password):
     connection_is_secure = False
     try:
         connection.starttls()
-        log.LOGGER.debug("[IMAP ACL] Server connection changed to TLS.")
+        log.LOGGER.debug("IMAP server connection changed to TLS.")
         connection_is_secure = True
     except AttributeError:
         if not server_is_local:
             log.LOGGER.error(
-                "[IMAP ACL] Python 3.2 or newer is required for TLS.")
+                "Python 3.2 or newer is required for IMAP + TLS.")
     except (imaplib.IMAP4.error, imaplib.IMAP4.abort) as exception:
         log.LOGGER.warning(
-            "[IMAP ACL] Server at %s failed to accept TLS connection "
+            "IMAP server at %s failed to accept TLS connection "
             "because of: %s" % (IMAP_SERVER, exception))
 
     if server_is_local and not connection_is_secure:
         log.LOGGER.warning(
-            "[IMAP ACL] Server is local. "
+            "IMAP server is local. "
             "Will allow transmitting unencrypted credentials.")
 
     if connection_is_secure or server_is_local:
@@ -71,16 +71,16 @@ def is_authenticated(user, password):
             connection.login(user, password)
             connection.logout()
             log.LOGGER.debug(
-                "[IMAP ACL] Authenticated user %s "
+                "Authenticated IMAP user %s "
                 "via %s." % (user, IMAP_SERVER))
             return True
         except (imaplib.IMAP4.error, imaplib.IMAP4.abort) as exception:
             log.LOGGER.error(
-                "[IMAP ACL] Server could not authenticate user %s "
+                "IMAP server could not authenticate user %s "
                 "because of: %s" % (user, exception))
     else:
         log.LOGGER.critical(
-            "[IMAP ACL] Server did not support TLS and is not ``localhost``. "
+            "IMAP server did not support TLS and is not ``localhost``. "
             "Refusing to transmit passwords under these conditions. "
             "Authentication attempt aborted.")
     return False  # authentication failed

+ 10 - 10
radicale/acl/LDAP.py → radicale/auth/LDAP.py

@@ -18,7 +18,7 @@
 # along with Radicale.  If not, see <http://www.gnu.org/licenses/>.
 
 """
-LDAP ACL.
+LDAP authentication.
 
 Authentication based on the ``python-ldap`` module
 (http://www.python-ldap.org/).
@@ -26,16 +26,16 @@ Authentication based on the ``python-ldap`` module
 """
 
 import ldap
-from radicale import acl, config, log
+from radicale import config, log
 
 
-BASE = config.get("acl", "ldap_base")
-ATTRIBUTE = config.get("acl", "ldap_attribute")
-FILTER = config.get("acl", "ldap_filter")
-CONNEXION = ldap.initialize(config.get("acl", "ldap_url"))
-BINDDN = config.get("acl", "ldap_binddn")
-PASSWORD = config.get("acl", "ldap_password")
-SCOPE = getattr(ldap, "SCOPE_%s" % config.get("acl", "ldap_scope").upper())
+BASE = config.get("auth", "ldap_base")
+ATTRIBUTE = config.get("auth", "ldap_attribute")
+FILTER = config.get("auth", "ldap_filter")
+CONNEXION = ldap.initialize(config.get("auth", "ldap_url"))
+BINDDN = config.get("auth", "ldap_binddn")
+PASSWORD = config.get("auth", "ldap_password")
+SCOPE = getattr(ldap, "SCOPE_%s" % config.get("auth", "ldap_scope").upper())
 
 
 def is_authenticated(user, password):
@@ -46,7 +46,7 @@ def is_authenticated(user, password):
         CONNEXION.whoami_s()
     except:
         log.LOGGER.debug("Reconnecting the LDAP server")
-        CONNEXION = ldap.initialize(config.get("acl", "ldap_url"))
+        CONNEXION = ldap.initialize(config.get("auth", "ldap_url"))
 
     if BINDDN and PASSWORD:
         log.LOGGER.debug("Initial LDAP bind as %s" % BINDDN)

+ 3 - 3
radicale/acl/PAM.py → radicale/auth/PAM.py

@@ -17,7 +17,7 @@
 # along with Radicale.  If not, see <http://www.gnu.org/licenses/>.
 
 """
-PAM ACL.
+PAM authentication.
 
 Authentication based on the ``pam-python`` module.
 
@@ -27,10 +27,10 @@ import grp
 import pam
 import pwd
 
-from radicale import acl, config, log
+from radicale import config, log
 
 
-GROUP_MEMBERSHIP = config.get("acl", "pam_group_membership")
+GROUP_MEMBERSHIP = config.get("auth", "pam_group_membership")
 
 
 def is_authenticated(user, password):

+ 15 - 24
radicale/authorization/allauthenticated.py → radicale/auth/__init__.py

@@ -1,7 +1,9 @@
 # -*- coding: utf-8 -*-
 #
 # This file is part of Radicale Server - Calendar Server
-# Copyright © 2011-2012 Guillaume Ayoub
+# Copyright © 2008-2012 Guillaume Ayoub
+# Copyright © 2008 Nicolas Kandel
+# Copyright © 2008 Pascal Halter
 #
 # 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
@@ -17,31 +19,20 @@
 # along with Radicale.  If not, see <http://www.gnu.org/licenses/>.
 
 """
-Radicale authorization module.
-
-Manages who is authorized to access a collection.
-
-The policy here is that all authenticated users
-have read and write access to all collections.
+Authentication management.
 
 """
 
-import os
-import sys
-
-from radicale import authorization, config, log
-
-
-
-
-def read_authorized(user, collection):
-    """Check if the user is allowed to read the collection"""
-    log.LOGGER.debug("read_authorized '" + user + "' in '" + collection.name + "'");
-    return True
+from radicale import config, log
 
 
-def write_authorized(user, collection):
-    """Check if the user is allowed to write the collection"""
-    log.LOGGER.debug("write_authorized '" + user + "' in '" + collection.name + "'");
-    return True
-    
+def load():
+    """Load list of available authentication managers."""
+    auth_type = config.get("auth", "type")
+    log.LOGGER.debug("Authentication type is %s" % auth_type)
+    if auth_type == "None":
+        return None
+    else:
+        module = __import__(
+            "auth.%s" % auth_type, globals=globals(), level=2)
+        return getattr(module, auth_type)

+ 4 - 3
radicale/acl/courier.py → radicale/auth/courier.py

@@ -17,16 +17,17 @@
 # along with Radicale.  If not, see <http://www.gnu.org/licenses/>.
 
 """
-Courier-Authdaemon ACL.
+Courier-Authdaemon authentication.
 
 """
 
 import sys
 import socket
-from radicale import acl, config, log
 
+from radicale import config, log
 
-COURIER_SOCKET = config.get("acl", "courier_socket")
+
+COURIER_SOCKET = config.get("auth", "courier_socket")
 
 
 def is_authenticated(user, password):

+ 4 - 4
radicale/acl/htpasswd.py → radicale/auth/htpasswd.py

@@ -19,7 +19,7 @@
 # along with Radicale.  If not, see <http://www.gnu.org/licenses/>.
 
 """
-Htpasswd ACL.
+Htpasswd authentication.
 
 Load the list of login/password couples according a the configuration file
 created by Apache ``htpasswd`` command. Plain-text, crypt and sha1 are
@@ -30,11 +30,11 @@ supported, but md5 is not (see ``htpasswd`` man page to understand why).
 import base64
 import hashlib
 
-from radicale import acl, config
+from radicale import config
 
 
-FILENAME = config.get("acl", "htpasswd_filename")
-ENCRYPTION = config.get("acl", "htpasswd_encryption")
+FILENAME = config.get("auth", "htpasswd_filename")
+ENCRYPTION = config.get("auth", "htpasswd_encryption")
 
 
 def _plain(hash_value, password):

+ 0 - 76
radicale/authorization/__init__.py

@@ -1,76 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of Radicale Server - Calendar Server
-# Copyright © 2008-2012 Guillaume Ayoub
-# Copyright © 2008 Nicolas Kandel
-# Copyright © 2008 Pascal Halter
-#
-# 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
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Radicale.  If not, see <http://www.gnu.org/licenses/>.
-
-"""
-Users and rights management.
-
-This module loads a list of users with access rights, according to the acl
-configuration.
-
-"""
-
-from radicale import config, log
-
-
-AUTHORIZATION_PREFIX = "authorization"
-
-PUBLIC_USERS = []
-PRIVATE_USERS = []
-
-
-
-def _config_users(name):
-    """Get an iterable of strings from the configuraton string [acl] ``name``.
-
-    The values must be separated by a comma. The whitespace characters are
-    stripped at the beginning and at the end of the values.
-
-    """
-    for user in config.get(AUTHORIZATION_PREFIX, name).split(","):
-        user = user.strip()
-        yield None if user == "None" else user
-
-
-def load():
-    """Load list of available ACL managers."""
-    
-    PUBLIC_USERS.extend(_config_users("public_users"))
-    PRIVATE_USERS.extend(_config_users("private_users"))
-    
-    authorization_type = config.get(AUTHORIZATION_PREFIX, "type")
-    log.LOGGER.debug("auth type = " + authorization_type)
-    if authorization_type == "None":
-        return None
-    else:
-        module = __import__("authorization.%s" % authorization_type, globals=globals(), level=2)
-        return getattr(module, authorization_type)
-
-
-def may_read(user, collection):
-    if (collection.owner not in PRIVATE_USERS and user != collection.owner):
-        # owner is not private and is not user, forbidden
-        return False
-
-    return read_authorized(user, collection)
-    
-def may_write(user, collection):
-    return write_authorized(user, collection)
-    
-    

+ 0 - 65
radicale/authorization/static.py

@@ -1,65 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of Radicale Server - Calendar Server
-# Copyright © 2011-2012 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
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Radicale.  If not, see <http://www.gnu.org/licenses/>.
-
-"""
-Radicale authorization module.
-
-Manages who is authorized to access a collection.
-
-The policy is that the owner may read and write in
-all collections and some special rights are hardcoded.
-
-"""
-
-import os
-import sys
-
-from radicale import authorization, config, log
-from radicale.authorization import owneronly
-
-
-
-def read_authorized(user, collection):
-    """Check if the user is allowed to read the collection"""
-    log.LOGGER.debug("read_authorized '" + user + "' in '" + collection.owner + "/" + collection.name + "'");
-    
-    if owneronly.read_authorized(user, collection):
-        return True
-    
-    if user == "user1" and collection.owner == "user2" and collection.name == "user2sharedwithuser1":
-        return True
-    if user == "user2" and collection.owner == "user1" and collection.name == "user1sharedwithuser2":
-        return True
-    
-    return False
-
-
-def write_authorized(user, collection):
-    """Check if the user is allowed to write the collection"""
-    log.LOGGER.debug("write_authorized '" + user + "' in '" + collection.owner + "/" + collection.name + "'");
-
-    if owneronly.write_authorized(user, collection):
-        return True
-    
-    if user == "user1" and collection.owner == "user2" and collection.name == "user2sharedwithuser1":
-        return True
-    if user == "user2" and collection.owner == "user1" and collection.name == "user1sharedwithuser2":
-        return False
-
-    return False
-    

+ 1 - 1
radicale/config.py

@@ -49,7 +49,7 @@ INITIAL_CONFIG = {
     "encoding": {
         "request": "utf-8",
         "stock": "utf-8"},
-    "acl": {
+    "auth": {
         "type": "None",
         "public_users": "public",
         "private_users": "private",

+ 1 - 1
radicale/ical.py

@@ -345,7 +345,7 @@ class Collection(object):
         items = self.items
 
         for new_item in self._parse(
-            text, (Timezone, Event, Todo, Journal, Card), name):
+                text, (Timezone, Event, Todo, Journal, Card), name):
             if new_item.name not in (item.name for item in items):
                 items.append(new_item)
 

+ 7 - 26
radicale/acl/__init__.py → radicale/rights/__init__.py

@@ -19,39 +19,20 @@
 # along with Radicale.  If not, see <http://www.gnu.org/licenses/>.
 
 """
-Users management.
-
-ACL is basically the wrong name here since this package deals with authenticating users.
-
-The authorization part is done in the package "authorization".
-
-This module loads a list of users with access rights, according to the acl
-configuration.
+Rights management.
 
 """
 
 from radicale import config, log
 
-CONFIG_PREFIX = "acl"
-
-def _config_users(name):
-    """Get an iterable of strings from the configuraton string [acl] ``name``.
-
-    The values must be separated by a comma. The whitespace characters are
-    stripped at the beginning and at the end of the values.
-
-    """
-    for user in config.get(CONFIG_PREFIX, name).split(","):
-        user = user.strip()
-        yield None if user == "None" else user
-
 
 def load():
     """Load list of available ACL managers."""
-    acl_type = config.get(CONFIG_PREFIX, "type")
-    log.LOGGER.debug("acl_type = "  + acl_type)
-    if acl_type == "None":
+    rights_type = config.get("rights", "type")
+    log.LOGGER.debug("Rights type is %s" % rights_type)
+    if rights_type == "None":
         return None
     else:
-        module = __import__("acl.%s" % acl_type, globals=globals(), level=2)
-        return getattr(module, acl_type)
+        module = __import__(
+            "rights.%s" % rights_type, globals=globals(), level=2)
+        return getattr(module, rights_type)

+ 5 - 20
radicale/authorization/owneronly.py → radicale/rights/owner_only.py

@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 #
 # This file is part of Radicale Server - Calendar Server
-# Copyright © 2011-2012 Guillaume Ayoub
+# Copyright © 2012 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
@@ -17,33 +17,18 @@
 # along with Radicale.  If not, see <http://www.gnu.org/licenses/>.
 
 """
-Radicale authorization module.
+Owner-only based rights.
 
-Manages who is authorized to access a collection.
-
-The policy here is that owners have read and write access
-to their own collections.
+Only owners have read and write access to their own collections.
 
 """
 
-import os
-import sys
-
-from radicale import authorization, config, log
-
-
-
 
 def read_authorized(user, collection):
-    """Check if the user is allowed to read the collection"""
-    log.LOGGER.debug("read_authorized '" + user + "' in '" + collection.owner + "/" + collection.name + "'");
-    
+    """Check if the user is allowed to read the collection."""
     return user == collection.owner
 
 
 def write_authorized(user, collection):
-    """Check if the user is allowed to write the collection"""
-    log.LOGGER.debug("write_authorized '" + user + "' in '" + collection.owner + "/" + collection.name + "'");
-
+    """Check if the user is allowed to write the collection."""
     return user == collection.owner
-    

+ 1 - 1
radicale/xmlutils.py

@@ -200,7 +200,7 @@ def propfind(path, xml_request, collections, user=None):
     multistatus = ET.Element(_tag("D", "multistatus"))
 
     for collection in collections:
-        if access.may_read(user, collection):
+        if access.read_authorized(user, collection):
             response = _propfind_response(path, collection, props, user)
             multistatus.append(response)
 

+ 4 - 3
setup.py

@@ -51,11 +51,12 @@ setup(
     author="Guillaume Ayoub",
     author_email="guillaume.ayoub@kozea.fr",
     url="http://www.radicale.org/",
-    download_url="http://pypi.python.org/packages/source/R/Radicale/" \
-        "Radicale-%s.tar.gz" % radicale.VERSION,
+    download_url=("http://pypi.python.org/packages/source/R/Radicale/"
+                  "Radicale-%s.tar.gz" % radicale.VERSION),
     license="GNU GPL v3",
     platforms="Any",
-    packages=["radicale", "radicale.acl", "radicale.storage"],
+    packages=[
+        "radicale", "radicale.auth", "radicale.rights", "radicale.storage"],
     provides=["radicale"],
     scripts=["bin/radicale"],
     keywords=["calendar", "addressbook", "CalDAV", "CardDAV"],