regex.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. # -*- coding: utf-8 -*-
  2. #
  3. # This file is part of Radicale Server - Calendar Server
  4. # Copyright © 2012-2013 Guillaume Ayoub
  5. #
  6. # This library is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This library is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. Regex-based rights.
  20. You can define an secondary rights management method. If not, it will use
  21. "owner_only", which implies the owners have all rights on their collections.
  22. Secondary rights management method is specified in the config (section
  23. "right", key "regex_secondary").
  24. Regexes are read from a file whose name is specified in the config (section
  25. "right", key "regex_file").
  26. Test string for regex is "user|collection" per default, because "|" is
  27. not allowed in an URL. You may set this string per rule, using Python's
  28. ConfigParser interpolation: %(user)s and %(collection)s
  29. In fact you may also set user/collection to a fixed value per rule. But you
  30. should consider using a secondary rights management method (e.g. "from_file").
  31. Section names are only used for naming the rule.
  32. Leading or Ending "/"s are trimmed from collection names.
  33. Example:
  34. # This means all users starting with "admin" may read any collection
  35. [admin]
  36. regex: ^admin.*\|.+?$
  37. permission: r
  38. # This means all users may read and write any collection starting with public.
  39. # We do so by just not testing against the user string.
  40. [public]
  41. glue: %(collection)s
  42. regex: ^public(/.+)?$
  43. permission: rw
  44. # A little more complex:
  45. # Give read access to users from a domain for all collections of all the users:
  46. [domain-wide-access]
  47. regex: ^.+@(.+)\|.+@\1(/.+)?$
  48. permission: r
  49. """
  50. import os.path
  51. import re
  52. from radicale import config, log, rights
  53. # Manage Python2/3 different modules
  54. # pylint: disable=F0401
  55. try:
  56. from configparser import (
  57. ConfigParser as ConfigParser, NoSectionError, NoOptionError)
  58. except ImportError:
  59. from ConfigParser import (
  60. ConfigParser as ConfigParser, NoSectionError, NoOptionError)
  61. # pylint: enable=F0401
  62. FILENAME = (
  63. os.path.expanduser(config.get("rights", "regex_file")) or
  64. log.LOGGER.error("No file name configured for rights type 'regex'"))
  65. def _read_regex(user, collection):
  66. """Load regex from file."""
  67. regex = ConfigParser({'user': user, 'collection': collection})
  68. if not regex.read(FILENAME):
  69. log.LOGGER.error(
  70. "File '%s' not found for rights management type 'regex'" % FILENAME)
  71. return regex
  72. def _read_from_sections(user, collection, perm):
  73. """Get regex sections."""
  74. regex = _read_regex(user, collection)
  75. try:
  76. for section in regex.sections():
  77. if _matches_section(user, collection, section):
  78. if perm in regex.get(section, "permission"):
  79. return True
  80. except (NoSectionError, NoOptionError):
  81. return False
  82. return False
  83. def _matches_section(user, collection, section):
  84. """Regex section against user and collection"""
  85. log.LOGGER.debug("Reading regex from file %s" % FILENAME)
  86. regex = _read_regex(user, collection)
  87. log.LOGGER.debug("Match against section '%s'" % section)
  88. try:
  89. test = regex.get(section, 'glue')
  90. except (NoOptionError):
  91. test = user+'|'+collection;
  92. try:
  93. match = re.match(regex.get(section, 'regex'), test)
  94. if match:
  95. return True;
  96. log.LOGGER.debug("Test-String '%s' does not match against '%s' from section '%s'" % \
  97. (test, regex.get(section, 'regex'), section))
  98. except (NoSectionError, NoOptionError):
  99. return False
  100. return False
  101. def _get_secondary():
  102. """Get secondary rights management method"""
  103. try:
  104. secondary = config.get("rights", "regex_secondary")
  105. if not secondary or secondary == None:
  106. secondary = 'owner_only'
  107. root_module = __import__(
  108. "rights.%s" % secondary, globals=globals(), level=2)
  109. module = getattr(root_module, secondary)
  110. return module
  111. except (ImportError, NoSectionError, NoOptionError):
  112. return None
  113. def read_authorized(user, collection):
  114. """Check if the user is allowed to read the collection."""
  115. if user is None:
  116. return False
  117. elif _get_secondary() != None and _get_secondary().read_authorized(user, collection):
  118. return True
  119. else:
  120. return _read_from_sections(
  121. user, collection.url.rstrip("/") or "/", "r")
  122. def write_authorized(user, collection):
  123. """Check if the user is allowed to write the collection."""
  124. if user is None:
  125. return False
  126. elif _get_secondary() != None and _get_secondary().write_authorized(user, collection):
  127. return True
  128. else:
  129. return _read_from_sections(
  130. user, collection.url.rstrip("/") or "/", "w")