from_file.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. # -*- coding: utf-8 -*-
  2. #
  3. # This file is part of Radicale Server - Calendar Server
  4. # Copyright © 2012 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. File-based rights.
  20. The owner is implied to have all rights on their collections.
  21. Rights are read from a file whose name is specified in the config
  22. (section "right", key "file").
  23. The file's format is per line:
  24. collectionpath ":" principal " " rights {", " principal " " rights}*
  25. collectionpath is the path part of the collection's url
  26. principal is a user name (no whitespace allowed)
  27. rights is a string w/o whitespace that contains "r" for reading rights,
  28. "w" for writing rights and a combination of these for all rights.
  29. Empty lines are ignored. Lines starting with "#" (hash sign) are comments.
  30. Example:
  31. # This means user1 may read, user2 may write, user3 has full access
  32. /user0/calendar : user1 r, user2 w, user3 rw
  33. # user0 can read /user1/cal
  34. /user1/cal : user0 r
  35. If a collection /a/b is shared and other users than the owner are
  36. supposed to find the collection in a propfind request, an additional
  37. line for /a has to be in the defintions. E.g.:
  38. /user0/cal: user
  39. """
  40. from radicale import config, log
  41. from radicale.rights import owner_only
  42. READ_AUTHORIZED = None
  43. WRITE_AUTHORIZED = None
  44. class ParsingError(BaseException):
  45. """Raised if the file cannot be parsed"""
  46. def read_authorized(user, collection):
  47. """Check if the user is allowed to read the collection."""
  48. if owner_only.read_authorized(user, collection):
  49. return True
  50. curl = _normalize_trail_slash(collection.url)
  51. return _dict_knows(READ_AUTHORIZED, curl, user)
  52. def write_authorized(user, collection):
  53. """Check if the user is allowed to write the collection."""
  54. if owner_only.read_authorized(user, collection):
  55. return True
  56. curl = _normalize_trail_slash(collection.url)
  57. return _dict_knows(WRITE_AUTHORIZED, curl, user)
  58. def _dict_knows(adict, url, user):
  59. return adict.has_key(url) and adict.get(url).count(user) != 0
  60. def _load():
  61. read = {}
  62. write = {}
  63. file_name = config.get("rights", "file")
  64. if file_name == "None":
  65. log.LOGGER.error("No file name configured for rights type 'from_file'")
  66. return
  67. log.LOGGER.debug("Reading rights from file %s" % file_name)
  68. lines = open(file_name, "r").readlines()
  69. for line in lines:
  70. _process(line, read, write)
  71. global READ_AUTHORIZED, WRITE_AUTHORIZED
  72. READ_AUTHORIZED = read
  73. WRITE_AUTHORIZED = write
  74. def _process(line, read, write):
  75. line = line.strip()
  76. if line == "":
  77. """Empty line"""
  78. return
  79. if line.startswith("#"):
  80. """Comment"""
  81. return
  82. collection, sep, rights_part = line.partition(":")
  83. rights_part = rights_part.strip()
  84. if rights_part == "":
  85. return
  86. collection = collection.strip()
  87. if collection == "":
  88. raise ParsingError
  89. collection = _normalize_trail_slash(collection)
  90. rights = rights_part.split(",")
  91. for right in rights:
  92. user, sep, right_defs = right.strip().partition(" ")
  93. if user == "" or right_defs == "":
  94. raise ParsingError
  95. user = user.strip()
  96. right_defs = right_defs.strip()
  97. for right_def in list(right_defs):
  98. if right_def == 'r':
  99. _append(read, collection, user)
  100. elif right_def == 'w':
  101. _append(write, collection, user)
  102. else:
  103. raise ParsingError
  104. def _append(rdict, key, value):
  105. if rdict.has_key(key):
  106. rlist = rdict[key]
  107. rlist.append(value)
  108. else:
  109. rlist = [value]
  110. rdict[key] = rlist
  111. def _normalize_trail_slash(s):
  112. """Removes a maybe existing trailing slash"""
  113. if s != "/" and s.endswith("/"):
  114. s, sep, empty = s.rpartition("/")
  115. return s
  116. _load()