From b7a61d5c0ce18858499bc1ac945eb99ee9267a48 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 7 Jun 2011 17:33:59 +0200 Subject: [PATCH 01/14] add .wsgi file (mod_wsgi and uWSGI compliant) --- radicale.wsgi | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100755 radicale.wsgi diff --git a/radicale.wsgi b/radicale.wsgi new file mode 100755 index 0000000..9cb9f80 --- /dev/null +++ b/radicale.wsgi @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# This file is part of Radicale Server - Calendar Server +# Copyright © 2011 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 . + +""" +Radicale wsgi file (mod_wsgi and uWSGI compliant). + +""" + +import radicale +application = radicale.Application() From 4bd425608d0313b57d82210f0ee77952d73d0be7 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 7 Jun 2011 17:44:53 +0200 Subject: [PATCH 02/14] get config filename from env variable RADICALE_CONFIG --- radicale/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radicale/config.py b/radicale/config.py index c86ccb3..94134b1 100644 --- a/radicale/config.py +++ b/radicale/config.py @@ -74,6 +74,8 @@ for section, values in INITIAL_CONFIG.items(): _CONFIG_PARSER.read("/etc/radicale/config") _CONFIG_PARSER.read(os.path.expanduser("~/.config/radicale/config")) +if os.environ.get('RADICALE_CONFIG', None): + _CONFIG_PARSER.read(os.environ['RADICALE_CONFIG']) # Wrap config module into ConfigParser instance sys.modules[__name__] = _CONFIG_PARSER From a14a52fe110db956e50365c25f41b90bbfe5e9f6 Mon Sep 17 00:00:00 2001 From: Pieter Naaijkens Date: Tue, 7 Jun 2011 21:57:00 +0200 Subject: [PATCH 03/14] Remove pidfile on shutdown --- radicale.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radicale.py b/radicale.py index a5349b5..2901106 100755 --- a/radicale.py +++ b/radicale.py @@ -159,3 +159,7 @@ finally: "Closing server listening to %s port %s" % ( server.server_name, server.server_port)) server.shutdown() + + # remove pidfile + if options.pid: + os.unlink(options.pid) From 4acc9effb89515dda7882b5c57b6c1f2c9c06ba1 Mon Sep 17 00:00:00 2001 From: Pieter Naaijkens Date: Tue, 7 Jun 2011 21:59:31 +0200 Subject: [PATCH 04/14] Fix amount of whitespace --- radicale.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale.py b/radicale.py index 2901106..e4746f0 100755 --- a/radicale.py +++ b/radicale.py @@ -162,4 +162,4 @@ finally: # remove pidfile if options.pid: - os.unlink(options.pid) + os.unlink(options.pid) From 303dc4d3126b699aea9f00b681ef8868ddac8108 Mon Sep 17 00:00:00 2001 From: Pieter Naaijkens Date: Tue, 7 Jun 2011 22:24:04 +0200 Subject: [PATCH 05/14] Only remove pid file if started as daemon --- radicale.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale.py b/radicale.py index e4746f0..de2e5c7 100755 --- a/radicale.py +++ b/radicale.py @@ -161,5 +161,5 @@ finally: server.shutdown() # remove pidfile - if options.pid: + if options.pid and options.daemon: os.unlink(options.pid) From f605a002bdc3dee74250e19fe594c7e6637e6b69 Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Wed, 8 Jun 2011 08:02:19 +0200 Subject: [PATCH 06/14] Minor typo fix --- radicale.wsgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale.wsgi b/radicale.wsgi index 9cb9f80..3f0d0dc 100755 --- a/radicale.wsgi +++ b/radicale.wsgi @@ -18,7 +18,7 @@ # along with Radicale. If not, see . """ -Radicale wsgi file (mod_wsgi and uWSGI compliant). +Radicale WSGI file (mod_wsgi and uWSGI compliant). """ From fe71b346972155b7b0311fd56ac57e1680481fbe Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Wed, 8 Jun 2011 08:20:37 +0200 Subject: [PATCH 07/14] Use 'key in dict' instead of 'dict.get(key)' --- radicale/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale/config.py b/radicale/config.py index 94134b1..5204f4c 100644 --- a/radicale/config.py +++ b/radicale/config.py @@ -74,7 +74,7 @@ for section, values in INITIAL_CONFIG.items(): _CONFIG_PARSER.read("/etc/radicale/config") _CONFIG_PARSER.read(os.path.expanduser("~/.config/radicale/config")) -if os.environ.get('RADICALE_CONFIG', None): +if 'RADICALE_CONFIG' in os.environ: _CONFIG_PARSER.read(os.environ['RADICALE_CONFIG']) # Wrap config module into ConfigParser instance From 8771e55ba3eb0698c67f11e2d225e27c8fe8e986 Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Sat, 11 Jun 2011 16:55:27 +0200 Subject: [PATCH 08/14] Typo fix --- radicale.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale.py b/radicale.py index de2e5c7..a5dd44f 100755 --- a/radicale.py +++ b/radicale.py @@ -160,6 +160,6 @@ finally: server.server_name, server.server_port)) server.shutdown() - # remove pidfile + # Remove PID file if options.pid and options.daemon: os.unlink(options.pid) From 5e3fda9f2feafc46a25d8f6f605de278771af7ae Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Sat, 11 Jun 2011 16:59:49 +0200 Subject: [PATCH 09/14] Add the PID file support in NEWS --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 183ebeb..2ac2108 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,7 @@ * Apple iCal 4 and iPhone support (by Łukasz Langa) * LDAP auth backend (by Corentin Le Bail) * Owner-less calendars (by René Neumann) +* PID file * Journal entries support * Drop Python 2.5 support From abb70cef49a5857145dceba5d4dc2718b047ef37 Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Sat, 11 Jun 2011 17:34:25 +0200 Subject: [PATCH 10/14] Remove the Python 3.0 support --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index a95caa9..6e68682 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,6 @@ setup( "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.0", "Programming Language :: Python :: 3.1", "Programming Language :: Python :: 3.2", "Topic :: Office/Business :: Groupware"]) From 31496437067d5d763370aac59e22717b755d9ec2 Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Sat, 11 Jun 2011 18:14:08 +0200 Subject: [PATCH 11/14] Log "nobody" instead of "None" as anonymous calendars owner. --- radicale/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radicale/__init__.py b/radicale/__init__.py index 491846b..74ab55d 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -184,7 +184,8 @@ class Application(object): calendars.append(calendar) continue log.LOGGER.info( - "Checking rights for calendar owned by %s" % calendar.owner) + "Checking rights for calendar owned by %s" % ( + calendar.owner or "nobody")) if self.acl.has_right(calendar.owner, user, password): log.LOGGER.info("%s allowed" % (user or "anonymous user")) From a28bd4a5fa8272c436f67c1555b36e96e2d831d7 Mon Sep 17 00:00:00 2001 From: Pieter Naaijkens Date: Sun, 12 Jun 2011 12:29:31 +0200 Subject: [PATCH 12/14] Use atexit.register to cleanup after shutdown --- radicale.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/radicale.py b/radicale.py index a5dd44f..ed88a4a 100755 --- a/radicale.py +++ b/radicale.py @@ -32,6 +32,7 @@ Launch the server according to configuration and command-line options. """ +import atexit import os import sys import optparse @@ -101,6 +102,14 @@ if options.daemon: sys.exit() sys.stdout = sys.stderr = open(os.devnull, "w") +# Register exit function +def cleanup(): + radicale.log.LOGGER.debug("Cleaning up") + # Remove PID file + if options.pid and options.daemon: + os.unlink(options.pid) + +atexit.register(cleanup) radicale.log.LOGGER.info("Starting Radicale") # Create calendar servers @@ -159,7 +168,3 @@ finally: "Closing server listening to %s port %s" % ( server.server_name, server.server_port)) server.shutdown() - - # Remove PID file - if options.pid and options.daemon: - os.unlink(options.pid) From f9836ab093fe527390568c0dc99c020855ec219e Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Mon, 13 Jun 2011 22:15:52 +0200 Subject: [PATCH 13/14] Clean support of public calendars, add support of private calendars --- NEWS | 2 +- config | 4 ++++ radicale/__init__.py | 21 ++++++++++++++------- radicale/acl/LDAP.py | 6 +++--- radicale/acl/__init__.py | 16 ++++++++++++++++ radicale/acl/htpasswd.py | 4 ++-- radicale/config.py | 2 ++ 7 files changed, 42 insertions(+), 13 deletions(-) diff --git a/NEWS b/NEWS index 2ac2108..491d0a0 100644 --- a/NEWS +++ b/NEWS @@ -14,7 +14,7 @@ * Smart, verbose and configurable logs * Apple iCal 4 and iPhone support (by Łukasz Langa) * LDAP auth backend (by Corentin Le Bail) -* Owner-less calendars (by René Neumann) +* Public and private calendars (by René Neumann) * PID file * Journal entries support * Drop Python 2.5 support diff --git a/config b/config index 49e4d90..8f7b616 100644 --- a/config +++ b/config @@ -36,6 +36,10 @@ stock = utf-8 # Access method # Value: None | htpasswd | LDAP type = None +# Usernames used for public calendars, separated by a comma +public_users = public +# Usernames used for private calendars, separated by a comma +private_users = private # Htpasswd filename htpasswd_filename = /etc/radicale/users # Htpasswd encryption method diff --git a/radicale/__init__.py b/radicale/__init__.py index 74ab55d..c82c125 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -183,17 +183,24 @@ class Application(object): if last_allowed: calendars.append(calendar) continue - log.LOGGER.info( - "Checking rights for calendar owned by %s" % ( - calendar.owner or "nobody")) - if self.acl.has_right(calendar.owner, user, password): - log.LOGGER.info("%s allowed" % (user or "anonymous user")) + if calendar.owner in acl.PUBLIC_USERS: + log.LOGGER.info("Public calendar") calendars.append(calendar) last_allowed = True else: - log.LOGGER.info("%s refused" % (user or "anonymous user")) - last_allowed = False + log.LOGGER.info( + "Checking rights for calendar owned by %s" % ( + calendar.owner or "nobody")) + if self.acl.has_right(calendar.owner, user, password): + log.LOGGER.info( + "%s allowed" % (user or "Anonymous user")) + calendars.append(calendar) + last_allowed = True + else: + log.LOGGER.info( + "%s refused" % (user or "Anonymous user")) + last_allowed = False if calendars: status, headers, answer = function(environ, calendars, content) diff --git a/radicale/acl/LDAP.py b/radicale/acl/LDAP.py index 16162b9..dfde79f 100644 --- a/radicale/acl/LDAP.py +++ b/radicale/acl/LDAP.py @@ -26,7 +26,7 @@ Authentication based on the ``python-ldap`` module """ import ldap -from radicale import config, log +from radicale import acl, config, log BASE = config.get("acl", "ldap_base") @@ -38,8 +38,8 @@ PASSWORD = config.get("acl", "ldap_password") def has_right(owner, user, password): """Check if ``user``/``password`` couple is valid.""" - if not user or (owner and user != owner): - # No user given, or owner is set and is not user, forbidden + if not user or (owner not in acl.PRIVATE_USERS and user != owner): + # No user given, or owner is not private and is not user, forbidden return False if BINDDN and PASSWORD: diff --git a/radicale/acl/__init__.py b/radicale/acl/__init__.py index 6745c76..f9166c3 100644 --- a/radicale/acl/__init__.py +++ b/radicale/acl/__init__.py @@ -29,11 +29,27 @@ configuration. from radicale import config +PUBLIC_USERS = [] +PRIVATE_USERS = [None] + + +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. + + """ + return (user.strip() for user in config.get("acl", name).split(",")) + + def load(): """Load list of available ACL managers.""" acl_type = config.get("acl", "type") if acl_type == "None": return None else: + PUBLIC_USERS.extend(_config_users("public_users")) + PRIVATE_USERS.extend(_config_users("private_users")) module = __import__("radicale.acl", fromlist=[acl_type]) return getattr(module, acl_type) diff --git a/radicale/acl/htpasswd.py b/radicale/acl/htpasswd.py index d100049..e624e91 100644 --- a/radicale/acl/htpasswd.py +++ b/radicale/acl/htpasswd.py @@ -30,7 +30,7 @@ supported, but md5 is not (see ``htpasswd`` man page to understand why). import base64 import hashlib -from radicale import config +from radicale import acl, config FILENAME = config.get("acl", "htpasswd_filename") @@ -63,6 +63,6 @@ def has_right(owner, user, password): for line in open(FILENAME).readlines(): if line.strip(): login, hash_value = line.strip().split(":") - if login == user and (not owner or owner == user): + if login == user and (owner in acl.PRIVATE_USERS or owner == user): return globals()["_%s" % ENCRYPTION](hash_value, password) return False diff --git a/radicale/config.py b/radicale/config.py index 5204f4c..63cab7b 100644 --- a/radicale/config.py +++ b/radicale/config.py @@ -50,6 +50,8 @@ INITIAL_CONFIG = { "stock": "utf-8"}, "acl": { "type": "None", + "public_users": "public", + "private_users": "private", "httpasswd_filename": "/etc/radicale/users", "httpasswd_encryption": "crypt", "ldap_url": "ldap://localhost:389/", From da421127403a71def2f3bcd3bcadbaf3b2232895 Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Mon, 13 Jun 2011 22:32:47 +0200 Subject: [PATCH 14/14] Allow the ``None`` value for public and private calendars --- radicale/acl/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/radicale/acl/__init__.py b/radicale/acl/__init__.py index f9166c3..8886ae2 100644 --- a/radicale/acl/__init__.py +++ b/radicale/acl/__init__.py @@ -30,7 +30,7 @@ from radicale import config PUBLIC_USERS = [] -PRIVATE_USERS = [None] +PRIVATE_USERS = [] def _config_users(name): @@ -40,7 +40,9 @@ def _config_users(name): stripped at the beginning and at the end of the values. """ - return (user.strip() for user in config.get("acl", name).split(",")) + for user in config.get("acl", name).split(","): + user = user.strip() + yield None if user == "None" else user def load():