Просмотр исходного кода

New right "i": Only allowing HTTP method GET

Unrud 5 лет назад
Родитель
Сommit
7f2d5cea62
5 измененных файлов с 55 добавлено и 15 удалено
  1. 6 4
      DOCUMENTATION.md
  2. 10 3
      radicale/app/get.py
  3. 6 4
      radicale/rights/__init__.py
  4. 24 0
      radicale/tests/test_rights.py
  5. 9 4
      rights

+ 6 - 4
DOCUMENTATION.md

@@ -963,10 +963,12 @@ if you want to use regular curly braces in the `user` and `collection` regexes.
 
 The following `permissions` are recognized:
 
-  * **R:** read a collection (excluding address book or calendar collections)
-  * **r:** read an address book or calendar collection
-  * **W:** write a collection (excluding address book or calendar collections)
-  * **w:** write an address book or calendar collection
+  * **R:** read collections (excluding address books and calendars)
+  * **r:** read address book and calendar collections
+  * **i:** subset of **r** that only allows direct access via HTTP method GET
+    (CalDAV/CardDAV is susceptible to expensive search requests)
+  * **W:** write collections (excluding address books and calendars)
+  * **w:** write address book and calendar collections
 
 ## Storage
 

+ 10 - 3
radicale/app/get.py

@@ -71,21 +71,28 @@ class ApplicationGetMixin:
         if path == "/.web" or path.startswith("/.web/"):
             return self._web.get(environ, base_prefix, path, user)
         access = app.Access(self._rights, user, path)
-        if not access.check("r"):
+        if not access.check("r") and "i" not in access.permissions:
             return httputils.NOT_ALLOWED
         with self._storage.acquire_lock("r", user):
             item = next(self._storage.discover(path), None)
             if not item:
                 return httputils.NOT_FOUND
-            if not access.check("r", item):
+            if access.check("r", item):
+                limited_access = False
+            elif "i" in access.permissions:
+                limited_access = True
+            else:
                 return httputils.NOT_ALLOWED
             if isinstance(item, storage.BaseCollection):
                 tag = item.get_meta("tag")
                 if not tag:
-                    return httputils.DIRECTORY_LISTING
+                    return (httputils.NOT_ALLOWED if limited_access else
+                            httputils.DIRECTORY_LISTING)
                 content_type = xmlutils.MIMETYPES[tag]
                 content_disposition = self._content_disposition_attachement(
                     propose_filename(item))
+            elif limited_access:
+                return httputils.NOT_ALLOWED
             else:
                 content_type = xmlutils.OBJECT_MIMETYPES[item.name]
                 content_disposition = ""

+ 6 - 4
radicale/rights/__init__.py

@@ -21,10 +21,12 @@ collections and entries.
 
 Permissions:
 
-  - R: read a collection (excluding address book or calendar collections)
-  - r: read an address book or calendar collection
-  - W: write a collection (excluding address book or calendar collections)
-  - w: write an address book or calendar collection
+  - R: read collections (excluding address books and calendars)
+  - r: read address book and calendar collections
+  - i: subset of **r** that only allows direct access via HTTP method GET
+       (CalDAV/CardDAV is susceptible to expensive search requests)
+  - W: write collections (excluding address books and calendars)
+  - w: write address book and calendar collections
 
 Take a look at the class ``BaseRights`` if you want to implement your own.
 

+ 24 - 0
radicale/tests/test_rights.py

@@ -136,6 +136,30 @@ permissions: Rr""")
         self._test_rights("from_file", "", "/custom/sub", "w", 401)
         self._test_rights("from_file", "tmp", "/custom/sub", "w", 403)
 
+    def test_from_file_limited_get(self):
+        rights_file_path = os.path.join(self.colpath, "rights")
+        with open(rights_file_path, "w") as f:
+            f.write("""\
+[write-all]
+user: tmp
+collection: .*
+permissions: RrWw
+[limited-public]
+user: .*
+collection: public/[^/]*
+permissions: i""")
+        self.configuration.update(
+            {"rights": {"type": "from_file",
+                        "file": rights_file_path}}, "test")
+        self.application = Application(self.configuration)
+        self.mkcalendar("/tmp/calendar", login="tmp:bepo")
+        self.mkcol("/public", login="tmp:bepo")
+        self.mkcalendar("/public/calendar", login="tmp:bepo")
+        self.get("/tmp/calendar", check=401)
+        self.get("/public/", check=401)
+        self.get("/public/calendar")
+        self.get("/public/calendar/1.ics", check=401)
+
     def test_custom(self):
         """Custom rights management."""
         self._test_rights("radicale.tests.custom.rights", "", "/", "r", 401)

+ 9 - 4
rights

@@ -75,19 +75,24 @@
 # Example: Allow everybody (including unauthenticated users) to read
 # the collection "public"
 
-# Allow reading collection "public"
+# Allow reading collection "public" for authenticated users
 #[public-principal]
-#user: .*
+#user: .+
 #collection: public
 #permissions: R
 
 # Allow reading all calendars and address books that are direct children of
-# the collection "public"
+# the collection "public" for authenticated users
 #[public-calendars]
-#user: .*
+#user: .+
 #collection: public/[^/]+
 #permissions: r
 
+# Allow access to public calendars and address books via HTTP GET for everyone
+#[public-calendars-restricted]
+#user: .*
+#collection: public/[^/]+
+#permissions: i
 
 # Example: Grant users of the form user@domain.tld read access to the
 # collection "domain.tld"