123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- import time
-
- from tracim_backend.lib.webdav.model import Lock, Url2Token
- from wsgidav import util
- from wsgidav.lock_manager import normalizeLockRoot, lockString, generateLockToken, validateLock
- from wsgidav.rw_lock import ReadWriteLock
-
- _logger = util.getModuleLogger(__name__)
-
-
- def from_dict_to_base(lock):
- return Lock(
- token=lock["token"],
- depth=lock["depth"],
- root=lock["root"],
- type=lock["type"],
- scopre=lock["scope"],
- owner=lock["owner"],
- timeout=lock["timeout"],
- principal=lock["principal"],
- expire=lock["expire"]
- )
-
-
- def from_base_to_dict(lock):
- return {
- 'token': lock.token,
- 'depth': lock.depth,
- 'root': lock.root,
- 'type': lock.type,
- 'scope': lock.scope,
- 'owner': lock.owner,
- 'timeout': lock.timeout,
- 'principal': lock.principal,
- 'expire': lock.expire
- }
-
-
- class LockStorage(object):
- LOCK_TIME_OUT_DEFAULT = 604800 # 1 week, in seconds
- LOCK_TIME_OUT_MAX = 4 * 604800 # 1 month, in seconds
-
- def __init__(self):
- self._session = None# todo Session()
- self._lock = ReadWriteLock()
-
- def __repr__(self):
- return "C'est bien mon verrou..."
-
- def __del__(self):
- pass
-
- def get_lock_db_from_token(self, token):
- return self._session.query(Lock).filter(Lock.token == token).one_or_none()
-
- def _flush(self):
- """Overloaded by Shelve implementation."""
- pass
-
- def open(self):
- """Called before first use.
-
- May be implemented to initialize a storage.
- """
- pass
-
- def close(self):
- """Called on shutdown."""
- pass
-
- def cleanup(self):
- """Purge expired locks (optional)."""
- pass
-
- def clear(self):
- """Delete all entries."""
- self._session.query(Lock).all().delete(synchronize_session=False)
- self._session.commit()
-
- def get(self, token):
- """Return a lock dictionary for a token.
-
- If the lock does not exist or is expired, None is returned.
-
- token:
- lock token
- Returns:
- Lock dictionary or <None>
-
- Side effect: if lock is expired, it will be purged and None is returned.
- """
- self._lock.acquireRead()
- try:
- lock_base = self._session.query(Lock).filter(Lock.token == token).one_or_none()
- if lock_base is None:
- # Lock not found: purge dangling URL2TOKEN entries
- _logger.debug("Lock purged dangling: %s" % token)
- self.delete(token)
- return None
- expire = float(lock_base.expire)
- if 0 <= expire < time.time():
- _logger.debug("Lock timed-out(%s): %s" % (expire, lockString(from_base_to_dict(lock_base))))
- self.delete(token)
- return None
- return from_base_to_dict(lock_base)
- finally:
- self._lock.release()
-
- def create(self, path, lock):
- """Create a direct lock for a resource path.
-
- path:
- Normalized path (utf8 encoded string, no trailing '/')
- lock:
- lock dictionary, without a token entry
- Returns:
- New unique lock token.: <lock
-
- **Note:** the lock dictionary may be modified on return:
-
- - lock['root'] is ignored and set to the normalized <path>
- - lock['timeout'] may be normalized and shorter than requested
- - lock['token'] is added
- """
- self._lock.acquireWrite()
- try:
- # We expect only a lock definition, not an existing lock
- assert lock.get("token") is None
- assert lock.get("expire") is None, "Use timeout instead of expire"
- assert path and "/" in path
-
- # Normalize root: /foo/bar
- org_path = path
- path = normalizeLockRoot(path)
- lock["root"] = path
-
- # Normalize timeout from ttl to expire-date
- timeout = float(lock.get("timeout"))
- if timeout is None:
- timeout = LockStorage.LOCK_TIME_OUT_DEFAULT
- elif timeout < 0 or timeout > LockStorage.LOCK_TIME_OUT_MAX:
- timeout = LockStorage.LOCK_TIME_OUT_MAX
-
- lock["timeout"] = timeout
- lock["expire"] = time.time() + timeout
-
- validateLock(lock)
-
- token = generateLockToken()
- lock["token"] = token
-
- # Store lock
- lock_db = from_dict_to_base(lock)
-
- self._session.add(lock_db)
-
- # Store locked path reference
- url2token = Url2Token(
- path=path,
- token=token
- )
-
- self._session.add(url2token)
- self._session.commit()
-
- self._flush()
- _logger.debug("LockStorageDict.set(%r): %s" % (org_path, lockString(lock)))
- # print("LockStorageDict.set(%r): %s" % (org_path, lockString(lock)))
- return lock
- finally:
- self._lock.release()
-
- def refresh(self, token, timeout):
- """Modify an existing lock's timeout.
-
- token:
- Valid lock token.
- timeout:
- Suggested lifetime in seconds (-1 for infinite).
- The real expiration time may be shorter than requested!
- Returns:
- Lock dictionary.
- Raises ValueError, if token is invalid.
- """
- lock_db = self._session.query(Lock).filter(Lock.token == token).one_or_none()
- assert lock_db is not None, "Lock must exist"
- assert timeout == -1 or timeout > 0
- if timeout < 0 or timeout > LockStorage.LOCK_TIME_OUT_MAX:
- timeout = LockStorage.LOCK_TIME_OUT_MAX
-
- self._lock.acquireWrite()
- try:
- # Note: shelve dictionary returns copies, so we must reassign values:
- lock_db.timeout = timeout
- lock_db.expire = time.time() + timeout
- self._session.commit()
- self._flush()
- finally:
- self._lock.release()
- return from_base_to_dict(lock_db)
-
- def delete(self, token):
- """Delete lock.
-
- Returns True on success. False, if token does not exist, or is expired.
- """
- self._lock.acquireWrite()
- try:
- lock_db = self._session.query(Lock).filter(Lock.token == token).one_or_none()
- _logger.debug("delete %s" % lockString(from_base_to_dict(lock_db)))
- if lock_db is None:
- return False
- # Remove url to lock mapping
- url2token = self._session.query(Url2Token).filter(
- Url2Token.path == lock_db.root,
- Url2Token.token == token).one_or_none()
- if url2token is not None:
- self._session.delete(url2token)
- # Remove the lock
- self._session.delete(lock_db)
- self._session.commit()
-
- self._flush()
- finally:
- self._lock.release()
- return True
-
- def getLockList(self, path, includeRoot, includeChildren, tokenOnly):
- """Return a list of direct locks for <path>.
-
- Expired locks are *not* returned (but may be purged).
-
- path:
- Normalized path (utf8 encoded string, no trailing '/')
- includeRoot:
- False: don't add <path> lock (only makes sense, when includeChildren
- is True).
- includeChildren:
- True: Also check all sub-paths for existing locks.
- tokenOnly:
- True: only a list of token is returned. This may be implemented
- more efficiently by some providers.
- Returns:
- List of valid lock dictionaries (may be empty).
- """
- assert path and path.startswith("/")
- assert includeRoot or includeChildren
-
- def __appendLocks(toklist):
- # Since we can do this quickly, we use self.get() even if
- # tokenOnly is set, so expired locks are purged.
- for token in toklist:
- lock_db = self.get_lock_db_from_token(token)
- if lock_db:
- if tokenOnly:
- lockList.append(lock_db.token)
- else:
- lockList.append(from_base_to_dict(lock_db))
-
- path = normalizeLockRoot(path)
- self._lock.acquireRead()
- try:
- tokList = self._session.query(Url2Token.token).filter(Url2Token.path == path).all()
- lockList = []
- if includeRoot:
- __appendLocks(tokList)
-
- if includeChildren:
- for url, in self._session.query(Url2Token.path).group_by(Url2Token.path):
- if util.isChildUri(path, url):
- __appendLocks(self._session.query(Url2Token.token).filter(Url2Token.path == url))
-
- return lockList
- finally:
- self._lock.release()
|