Use a more simple rights manager

This commit is contained in:
Guillaume Ayoub 2013-08-14 10:50:59 +02:00
parent 3cf2952cf8
commit 586773148e
8 changed files with 30 additions and 304 deletions

4
config
View File

@ -96,10 +96,10 @@ http_password_parameter =
[rights] [rights]
# Rights management method # Rights management method
# Value: None | owner_only | owner_write | from_file | regex # Value: None | owner_only | owner_write | from_file
type = None type = None
# File for rights management from_file or regex # File for rights management from_file
file = ~/.config/radicale/rights file = ~/.config/radicale/rights

View File

@ -131,7 +131,6 @@ class Application(object):
"""Initialize application.""" """Initialize application."""
super(Application, self).__init__() super(Application, self).__init__()
auth.load() auth.load()
rights.load()
storage.load() storage.load()
self.encoding = config.get("encoding", "request") self.encoding = config.get("encoding", "request")
if config.getboolean("logging", "full_environment"): if config.getboolean("logging", "full_environment"):
@ -190,7 +189,7 @@ class Application(object):
for item in items: for item in items:
if isinstance(item, ical.Collection): if isinstance(item, ical.Collection):
if rights.read_authorized(user, item): if rights.authorized(user, item, "r"):
log.LOGGER.debug( log.LOGGER.debug(
"%s has read access to collection %s" % "%s has read access to collection %s" %
(user or "Anonymous", item.url or "/")) (user or "Anonymous", item.url or "/"))
@ -202,7 +201,7 @@ class Application(object):
(user or "Anonymous", item.url or "/")) (user or "Anonymous", item.url or "/"))
read_last_collection_allowed = False read_last_collection_allowed = False
if rights.write_authorized(user, item): if rights.authorized(user, item, "w"):
log.LOGGER.debug( log.LOGGER.debug(
"%s has write access to collection %s" % "%s has write access to collection %s" %
(user or "Anonymous", item.url or "/")) (user or "Anonymous", item.url or "/"))

View File

@ -74,7 +74,7 @@ INITIAL_CONFIG = {
"http_password_parameter": ""}, "http_password_parameter": ""},
"rights": { "rights": {
"type": "None", "type": "None",
"file": ""}, "file": "~/.config/radicale/rights"},
"storage": { "storage": {
"type": "filesystem", "type": "filesystem",
"filesystem_folder": os.path.expanduser( "filesystem_folder": os.path.expanduser(

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2013 Guillaume Ayoub # Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# Copyright © 2008-2013 Guillaume Ayoub
# #
# This library is free software: you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -17,60 +19,14 @@
# along with Radicale. If not, see <http://www.gnu.org/licenses/>. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
""" """
Regex-based rights. Rights management.
Regexes are read from a file whose name is specified in the config (section
"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 slashes are trimmed from collection's path.
Examples:
# This means all users starting with "admin" may read any collection
[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]
user: .*
collection: ^public(/.+)?$
permission: rw
# 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]
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 import re
import os.path
from radicale import config, log from . import config, log
# Manage Python2/3 different modules # Manage Python2/3 different modules
# pylint: disable=F0401 # pylint: disable=F0401
@ -84,16 +40,27 @@ except ImportError:
FILENAME = ( FILENAME = (
os.path.expanduser(config.get("rights", "file")) or os.path.expanduser(config.get("rights", "file")) or
log.LOGGER.error("No file name configured for rights type 'regex'")) log.LOGGER.error("No file name configured for rights type 'regex'"))
DEFINED_RIGHTS = {
"none": "[rw]\nuser:.*\ncollection:.*\npermission:rw",
"owner_write": "[r]\nuser:.*\ncollection:.*\npermission:r\n"
"[w]\nuser:.*\ncollection:^%(login)s/.+$\npermission:w",
"owner_only": "[rw]\nuser:.\ncollection: ^%(login)s/.+$\npermission:rw"}
def _read_from_sections(user, collection, permission): def _read_from_sections(user, collection, permission):
"""Get regex sections.""" """Get regex sections."""
log.LOGGER.debug("Reading regex from file %s" % FILENAME)
regex = ConfigParser({"login": user, "path": collection}) regex = ConfigParser({"login": user, "path": collection})
rights_type = config.get("rights", "type").lower()
if rights_type in DEFINED_RIGHTS:
log.LOGGER.debug("Rights type '%s'" % rights_type)
regex.read_string(DEFINED_RIGHTS[rights_type])
elif rights_type == "from_file":
log.LOGGER.debug("Reading rights from file %s" % FILENAME)
if not regex.read(FILENAME): if not regex.read(FILENAME):
log.LOGGER.error( log.LOGGER.error("File '%s' not found for rights" % FILENAME)
"File '%s' not found for rights management type 'regex'" % return False
FILENAME) else:
log.LOGGER.error("Unknown rights type '%s'" % rights_type)
return False return False
for section in regex.sections(): for section in regex.sections():
@ -110,17 +77,10 @@ def _read_from_sections(user, collection, permission):
if permission in regex.get(section, "permission"): if permission in regex.get(section, "permission"):
return True return True
log.LOGGER.debug("Section '%s' does not match" % section) log.LOGGER.debug("Section '%s' does not match" % section)
return False return False
def read_authorized(user, collection): def authorized(user, collection, right):
"""Check if the user is allowed to read the collection.""" """Check if the user is allowed to read or write the collection."""
return user and _read_from_sections( return user and _read_from_sections(
user, collection.url.rstrip("/") or "/", "r") user, collection.url.rstrip("/") or "/", right)
def write_authorized(user, collection):
"""Check if the user is allowed to write the collection."""
return user and _read_from_sections(
user, collection.url.rstrip("/") or "/", "w")

View File

@ -1,62 +0,0 @@
# -*- coding: utf-8 -*-
#
# This file is part of Radicale Server - Calendar Server
# Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter
# Copyright © 2008-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
# 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/>.
"""
Rights management.
"""
import sys
from .. import config, log
def load():
"""Load list of available ACL managers."""
rights_type = config.get("rights", "type")
log.LOGGER.debug("Rights type is %s" % rights_type)
if rights_type == "None":
return None
else:
root_module = __import__(
"rights.%s" % rights_type, globals=globals(), level=2)
module = getattr(root_module, rights_type)
# Override rights.[read|write]_authorized
sys.modules[__name__].read_authorized = module.read_authorized
sys.modules[__name__].write_authorized = module.write_authorized
return module
def read_authorized(user, collection):
"""Check if the user is allowed to read the collection.
This method is overriden if an auth module is loaded.
"""
return True
def write_authorized(user, collection):
"""Check if the user is allowed to write the collection.
This method is overriden if an auth module is loaded.
"""
return True

View File

@ -1,102 +0,0 @@
# -*- coding: utf-8 -*-
#
# This file is part of Radicale Server - Calendar Server
# Copyright © 2012-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
# 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/>.
"""
File-based rights.
The owners are implied to have all rights on their collections.
Rights are read from a file whose name is specified in the config (section
"right", key "file").
Example:
# This means user1 may read, user2 may write, user3 has full access.
[user0/calendar]
user1: r
user2: w
user3: rw
# user0 can read user1/cal.
[user1/cal]
user0: r
# If a collection a/b is shared and other users than the owner are supposed to
# find the collection in a propfind request, an additional line for a has to
# be in the defintions.
[user0]
user1: r
"""
import os.path
from radicale import config, log
from radicale.rights import owner_only
# Manage Python2/3 different modules
# pylint: disable=F0401
try:
from configparser import (
RawConfigParser as ConfigParser, NoSectionError, NoOptionError)
except ImportError:
from ConfigParser import (
RawConfigParser as ConfigParser, NoSectionError, NoOptionError)
# pylint: enable=F0401
FILENAME = (
os.path.expanduser(config.get("rights", "file")) or
log.LOGGER.error("No file name configured for rights type 'from_file'"))
def _read_rights():
"""Update the rights according to the configuration file."""
log.LOGGER.debug("Reading rights from file %s" % FILENAME)
rights = ConfigParser()
if not rights.read(FILENAME):
log.LOGGER.error(
"File '%s' not found for rights management" % FILENAME)
return rights
def read_authorized(user, collection):
"""Check if the user is allowed to read the collection."""
if user is None:
return False
elif owner_only.read_authorized(user, collection):
return True
else:
try:
return "r" in _read_rights().get(
collection.url.rstrip("/") or "/", user)
except (NoSectionError, NoOptionError):
return False
def write_authorized(user, collection):
"""Check if the user is allowed to write the collection."""
if user is None:
return False
elif owner_only.write_authorized(user, collection):
return True
else:
try:
return "w" in _read_rights().get(
collection.url.rstrip("/") or "/", user)
except (NoSectionError, NoOptionError):
return False

View File

@ -1,34 +0,0 @@
# -*- coding: utf-8 -*-
#
# This file is part of Radicale Server - Calendar Server
# Copyright © 2012-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
# 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/>.
"""
Owner-only based rights.
Only owners have read and write access to their own collections.
"""
def read_authorized(user, collection):
"""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."""
return user == collection.owner

View File

@ -1,35 +0,0 @@
# -*- coding: utf-8 -*-
#
# This file is part of Radicale Server - Calendar Server
# Copyright © 2012-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
# 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/>.
"""
Owner-only write based rights.
Authenticated users have read access to all calendars, but only owners have
write access to their own collections.
"""
def read_authorized(user, collection):
"""Check if the user is allowed to read the collection."""
return True
def write_authorized(user, collection):
"""Check if the user is allowed to write the collection."""
return user and user == collection.owner