Use a more simple rights manager
This commit is contained in:
parent
3cf2952cf8
commit
586773148e
4
config
4
config
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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 "/"))
|
||||||
|
@ -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(
|
||||||
|
@ -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")
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
Loading…
Reference in New Issue
Block a user