Merge pull request #490 from Unrud/hook

Move hook into storage.Collection
This commit is contained in:
Guillaume Ayoub 2016-08-26 15:58:42 +02:00 committed by GitHub
commit ac61b18237
2 changed files with 26 additions and 31 deletions

View File

@ -33,11 +33,9 @@ import itertools
import os import os
import posixpath import posixpath
import pprint import pprint
import shlex
import socket import socket
import socketserver import socketserver
import ssl import ssl
import subprocess
import threading import threading
import urllib import urllib
import wsgiref.simple_server import wsgiref.simple_server
@ -313,12 +311,12 @@ class Application:
if user and is_authenticated: if user and is_authenticated:
principal_path = "/%s/" % user principal_path = "/%s/" % user
if self.authorized(user, principal_path, "w"): if self.authorized(user, principal_path, "w"):
with self._lock_collection("r", user): with self.Collection.acquire_lock("r", user):
principal = next( principal = next(
self.Collection.discover(principal_path, depth="1"), self.Collection.discover(principal_path, depth="1"),
None) None)
if not principal: if not principal:
with self._lock_collection("w", user): with self.Collection.acquire_lock("w", user):
self.Collection.create_collection(principal_path) self.Collection.create_collection(principal_path)
# Verify content length # Verify content length
@ -391,20 +389,6 @@ class Application:
allowed |= self.authorized(user, parent_path, permission) allowed |= self.authorized(user, parent_path, permission)
return allowed return allowed
@contextmanager
def _lock_collection(self, lock_mode, user):
"""Lock the collection with ``permission`` and execute hook."""
with self.Collection.acquire_lock(lock_mode) as value:
yield value
hook = self.configuration.get("storage", "hook")
if lock_mode == "w" and hook:
self.logger.debug("Running hook")
folder = os.path.expanduser(self.configuration.get(
"storage", "filesystem_folder"))
subprocess.check_call(
hook % {"user": shlex.quote(user or "Anonymous")},
shell=True, cwd=folder)
def _read_content(self, environ): def _read_content(self, environ):
content_length = int(environ.get("CONTENT_LENGTH") or 0) content_length = int(environ.get("CONTENT_LENGTH") or 0)
if content_length > 0: if content_length > 0:
@ -419,7 +403,7 @@ class Application:
"""Manage DELETE request.""" """Manage DELETE request."""
if not self._access(user, path, "w"): if not self._access(user, path, "w"):
return NOT_ALLOWED return NOT_ALLOWED
with self._lock_collection("w", user): with self.Collection.acquire_lock("w", user):
item = next(self.Collection.discover(path), None) item = next(self.Collection.discover(path), None)
if not self._access(user, path, "w", item): if not self._access(user, path, "w", item):
return NOT_ALLOWED return NOT_ALLOWED
@ -444,7 +428,7 @@ class Application:
return client.OK, headers, answer return client.OK, headers, answer
if not self._access(user, path, "r"): if not self._access(user, path, "r"):
return NOT_ALLOWED return NOT_ALLOWED
with self._lock_collection("r", user): with self.Collection.acquire_lock("r", user):
item = next(self.Collection.discover(path), None) item = next(self.Collection.discover(path), None)
if not self._access(user, path, "r", item): if not self._access(user, path, "r", item):
return NOT_ALLOWED return NOT_ALLOWED
@ -473,7 +457,7 @@ class Application:
if not self.authorized(user, path, "w"): if not self.authorized(user, path, "w"):
return NOT_ALLOWED return NOT_ALLOWED
content = self._read_content(environ) content = self._read_content(environ)
with self._lock_collection("w", user): with self.Collection.acquire_lock("w", user):
item = next(self.Collection.discover(path), None) item = next(self.Collection.discover(path), None)
if item: if item:
return client.CONFLICT, {}, None return client.CONFLICT, {}, None
@ -489,7 +473,7 @@ class Application:
if not self.authorized(user, path, "w"): if not self.authorized(user, path, "w"):
return NOT_ALLOWED return NOT_ALLOWED
content = self._read_content(environ) content = self._read_content(environ)
with self._lock_collection("w", user): with self.Collection.acquire_lock("w", user):
item = next(self.Collection.discover(path), None) item = next(self.Collection.discover(path), None)
if item: if item:
return client.CONFLICT, {}, None return client.CONFLICT, {}, None
@ -509,7 +493,7 @@ class Application:
if not self._access(user, to_path, "w"): if not self._access(user, to_path, "w"):
return NOT_ALLOWED return NOT_ALLOWED
with self._lock_collection("w", user): with self.Collection.acquire_lock("w", user):
item = next(self.Collection.discover(path), None) item = next(self.Collection.discover(path), None)
if not self._access(user, path, "w", item): if not self._access(user, path, "w", item):
return NOT_ALLOWED return NOT_ALLOWED
@ -547,7 +531,7 @@ class Application:
if not self._access(user, path, "r"): if not self._access(user, path, "r"):
return NOT_ALLOWED return NOT_ALLOWED
content = self._read_content(environ) content = self._read_content(environ)
with self._lock_collection("r", user): with self.Collection.acquire_lock("r", user):
items = self.Collection.discover( items = self.Collection.discover(
path, environ.get("HTTP_DEPTH", "0")) path, environ.get("HTTP_DEPTH", "0"))
# take root item for rights checking # take root item for rights checking
@ -572,7 +556,7 @@ class Application:
if not self.authorized(user, path, "w"): if not self.authorized(user, path, "w"):
return NOT_ALLOWED return NOT_ALLOWED
content = self._read_content(environ) content = self._read_content(environ)
with self._lock_collection("w", user): with self.Collection.acquire_lock("w", user):
item = next(self.Collection.discover(path), None) item = next(self.Collection.discover(path), None)
if not isinstance(item, self.Collection): if not isinstance(item, self.Collection):
return client.CONFLICT, {}, None return client.CONFLICT, {}, None
@ -585,7 +569,7 @@ class Application:
if not self._access(user, path, "w"): if not self._access(user, path, "w"):
return NOT_ALLOWED return NOT_ALLOWED
content = self._read_content(environ) content = self._read_content(environ)
with self._lock_collection("w", user): with self.Collection.acquire_lock("w", user):
parent_path = storage.sanitize_path( parent_path = storage.sanitize_path(
"/%s/" % posixpath.dirname(path.strip("/"))) "/%s/" % posixpath.dirname(path.strip("/")))
item = next(self.Collection.discover(path), None) item = next(self.Collection.discover(path), None)
@ -640,7 +624,7 @@ class Application:
if not self._access(user, path, "w"): if not self._access(user, path, "w"):
return NOT_ALLOWED return NOT_ALLOWED
content = self._read_content(environ) content = self._read_content(environ)
with self._lock_collection("r", user): with self.Collection.acquire_lock("r", user):
item = next(self.Collection.discover(path), None) item = next(self.Collection.discover(path), None)
if not self._access(user, path, "w", item): if not self._access(user, path, "w", item):
return NOT_ALLOWED return NOT_ALLOWED

View File

@ -29,7 +29,9 @@ import errno
import json import json
import os import os
import posixpath import posixpath
import shlex
import stat import stat
import subprocess
import threading import threading
import time import time
from contextlib import contextmanager from contextlib import contextmanager
@ -371,12 +373,14 @@ class BaseCollection:
@classmethod @classmethod
@contextmanager @contextmanager
def acquire_lock(cls, mode): def acquire_lock(cls, mode, user=None):
"""Set a context manager to lock the whole storage. """Set a context manager to lock the whole storage.
``mode`` must either be "r" for shared access or "w" for exclusive ``mode`` must either be "r" for shared access or "w" for exclusive
access. access.
``user`` is the name of the logged in user or empty.
""" """
raise NotImplementedError raise NotImplementedError
@ -743,13 +747,15 @@ class Collection(BaseCollection):
@classmethod @classmethod
@contextmanager @contextmanager
def acquire_lock(cls, mode): def acquire_lock(cls, mode, user=None):
def condition(): def condition():
if mode == "r": if mode == "r":
return not cls._writer return not cls._writer
else: else:
return not cls._writer and cls._readers == 0 return not cls._writer and cls._readers == 0
folder = os.path.expanduser(cls.configuration.get(
"storage", "filesystem_folder"))
# Use a primitive lock which only works within one process as a # Use a primitive lock which only works within one process as a
# precondition for inter-process file-based locking # precondition for inter-process file-based locking
with cls._lock: with cls._lock:
@ -770,8 +776,6 @@ class Collection(BaseCollection):
else: else:
cls._writer = True cls._writer = True
if not cls._lock_file: if not cls._lock_file:
folder = os.path.expanduser(
cls.configuration.get("storage", "filesystem_folder"))
cls._makedirs_synced(folder) cls._makedirs_synced(folder)
lock_path = os.path.join(folder, ".Radicale.lock") lock_path = os.path.join(folder, ".Radicale.lock")
cls._lock_file = open(lock_path, "w+") cls._lock_file = open(lock_path, "w+")
@ -797,6 +801,13 @@ class Collection(BaseCollection):
cls._lock_file_locked = True cls._lock_file_locked = True
try: try:
yield yield
# execute hook
hook = cls.configuration.get("storage", "hook")
if mode == "w" and hook:
cls.logger.debug("Running hook")
subprocess.check_call(
hook % {"user": shlex.quote(user or "Anonymous")},
shell=True, cwd=folder)
finally: finally:
with cls._lock: with cls._lock:
if mode == "r": if mode == "r":