|
|
@@ -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")
|