Esempio n. 1
0
 def __init__(self, storage):
     """
     storage:
         LockManagerStorage object
     """
     assert hasattr(storage, "getLockList")
     self._lock = ReadWriteLock()
     self.storage = storage
     self.storage.open()
Esempio n. 2
0
 def __init__(self, storage):
     """
     storage:
         LockManagerStorage object
     """
     assert hasattr(storage, "getLockList")
     self._lock = ReadWriteLock()
     self.storage = storage
     self.storage.open()
Esempio n. 3
0
    def __init__(self, path: str):
        self.config = configparser.RawConfigParser(comment_prefixes='¤',
                                                   allow_no_value=True)
        self.config_path = path
        self.config.read(path)
        self.constraints = {}
        self.sections = self.config.sections()

        self.lock = ReadWriteLock()
 def __init__(self, config: Configuration, database: TelemetryDB):
     self.config = config
     self.db = database
     self.lock = ReadWriteLock()
     self.last_status = RelayStatus.NO_REQUESTS
     self.request_counter = 0
     self.failed_in_a_row = 0
     self.relay_active = False
     """ Start thread to relay packets every set interval. """
     threading.Thread(target=self.relay_unrelayed_packets_every_interval,
                      daemon=True).start()
Esempio n. 5
0
 def __init__(self):
     self._dict = None
     self._loaded = False      
     self._lock = ReadWriteLock()
     self._verbose = 2
Esempio n. 6
0
class PropertyManager(object):
    """
    An in-memory property manager implementation using a dictionary.
    
    This is obviously not persistent, but should be enough in some cases.
    For a persistent implementation, see property_manager.ShelvePropertyManager().
    """
    def __init__(self):
        self._dict = None
        self._loaded = False      
        self._lock = ReadWriteLock()
        self._verbose = 2


    def __repr__(self):
        return "PropertyManager"


    def __del__(self):
        if __debug__ and self._verbose >= 2:
            self._check()         
        self._close()


    def _lazyOpen(self):
        _logger.debug("_lazyOpen()")
        self._lock.acquireWrite()
        try:
            self._dict = {}
            self._loaded = True
        finally:
            self._lock.release()         


    def _sync(self):
        pass

    
    def _close(self):
        _logger.debug("_close()")
        self._lock.acquireWrite()
        try:
            self._dict = None
            self._loaded = False
        finally:
            self._lock.release()         

    
    def _check(self, msg=""):
        try:
            if not self._loaded:
                return True
#            for k in self._dict.keys():
#                print "%s" % k
#                print "  -> %s" % self._dict[k]
#            self._dump()
            for k, v in self._dict.items():
                _ = "%s, %s" % (k, v)
#            _logger.debug("%s checks ok %s" % (self.__class__.__name__, msg))
            return True
        except Exception:
            _logger.exception("%s _check: ERROR %s" % (self.__class__.__name__, msg))
#            traceback.print_exc()
#            raise
#            sys.exit(-1)
            return False


    def _dump(self, msg="", out=None):
        if out is None:
            out = sys.stdout
        print >>out, "%s(%s): %s" % (self.__class__.__name__, self.__repr__(), msg)
        if not self._loaded:
            self._lazyOpen()
            if self._verbose >= 2:
                return # Already dumped in _lazyOpen
        try:
            for k, v in self._dict.items():
                print >>out, "    ", k
                for k2, v2 in v.items():
                    try:
                        print >>out, "        %s: '%s'" % (k2, v2)
                    except Exception, e:
                        print >>out, "        %s: ERROR %s" % (k2, e)
            out.flush()
        except Exception, e:
            util.warn("PropertyManager._dump()  ERROR: %s" % e)
Esempio n. 7
0
 def __init__(self):
     self._dict = None
     self._lock = ReadWriteLock()
Esempio n. 8
0
class LockStorageDict(object):
    """
    An in-memory lock manager storage implementation using a dictionary.
    
    R/W access is guarded by a thread.lock object.
    
    Also, to make it work with a Shelve dictionary, modifying dictionary
    members is done by re-assignment and we call a _flush() method.
    
    This is obviously not persistent, but should be enough in some cases.
    For a persistent implementation, see lock_manager.LockStorageShelve().

    Notes:
        expire is stored as expiration date in seconds since epoch (not in
        seconds until expiration).

    The dictionary is built like::
    
        { 'URL2TOKEN:/temp/litmus/lockme': ['opaquelocktoken:0x1d7b86...', 
                                            'opaquelocktoken:0xd7d4c0...'],
          'opaquelocktoken:0x1d7b86...': { 'depth': '0',
                                           'owner': "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\\n<owner xmlns="DAV:">litmus test suite</owner>\\n",
                                           'principal': 'tester',
                                           'root': '/temp/litmus/lockme',
                                           'scope': 'shared',
                                           'expire': 1261328382.4530001,
                                           'token': 'opaquelocktoken:0x1d7b86...',
                                           'type': 'write',
                                           },
          'opaquelocktoken:0xd7d4c0...': { 'depth': '0',
                                           'owner': '<?xml version=\'1.0\' encoding=\'UTF-8\'?>\\n<owner xmlns="DAV:">litmus: notowner_sharedlock</owner>\\n',
                                           'principal': 'tester',
                                           'root': '/temp/litmus/lockme',
                                           'scope': 'shared',
                                           'expire': 1261328381.6040001,
                                           'token': 'opaquelocktoken:0xd7d4c0...',
                                           'type': 'write'
                                           },
         }
    """
    LOCK_TIME_OUT_DEFAULT = 604800  # 1 week, in seconds
    LOCK_TIME_OUT_MAX = 4 * 604800  # 1 month, in seconds

    def __init__(self):
        self._dict = None
        self._lock = ReadWriteLock()

    def __repr__(self):
        return self.__class__.__name__

    def __del__(self):
        pass

    def _flush(self):
        """Overloaded by Shelve implementation."""
        pass

    def open(self):
        """Called before first use.
        
        May be implemented to initialize a storage.
        """
        assert self._dict is None
        self._dict = {}

    def close(self):
        """Called on shutdown."""
        self._dict = None

    def cleanup(self):
        """Purge expired locks (optional)."""
        pass

    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 = self._dict.get(token)
            if lock is None:
                # Lock not found: purge dangling URL2TOKEN entries
                _logger.debug("Lock purged dangling: %s" % token)
                self.delete(token)
                return None
            expire = float(lock["expire"])
            if expire >= 0 and expire < time.time():
                _logger.debug("Lock timed-out(%s): %s" %
                              (expire, lockString(lock)))
                self.delete(token)
                return None
            return lock
        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 = LockStorageDict.LOCK_TIME_OUT_DEFAULT
            elif timeout < 0 or timeout > LockStorageDict.LOCK_TIME_OUT_MAX:
                timeout = LockStorageDict.LOCK_TIME_OUT_MAX

            lock["timeout"] = timeout
            lock["expire"] = time.time() + timeout

            validateLock(lock)

            token = generateLockToken()
            lock["token"] = token

            # Store lock
            self._dict[token] = lock

            # Store locked path reference
            key = "URL2TOKEN:%s" % path
            if not key in self._dict:
                self._dict[key] = [token]
            else:
                # Note: Shelve dictionary returns copies, so we must reassign values:
                tokList = self._dict[key]
                tokList.append(token)
                self._dict[key] = tokList
            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. 
        """
        assert token in self._dict, "Lock must exist"
        assert timeout == -1 or timeout > 0
        if timeout < 0 or timeout > LockStorageDict.LOCK_TIME_OUT_MAX:
            timeout = LockStorageDict.LOCK_TIME_OUT_MAX

        self._lock.acquireWrite()
        try:
            # Note: shelve dictionary returns copies, so we must reassign values:
            lock = self._dict[token]
            lock["timeout"] = timeout
            lock["expire"] = time.time() + timeout
            self._dict[token] = lock
            self._flush()
        finally:
            self._lock.release()
        return lock

    def delete(self, token):
        """Delete lock.
        
        Returns True on success. False, if token does not exist, or is expired.
        """
        self._lock.acquireWrite()
        try:
            lock = self._dict.get(token)
            _logger.debug("delete %s" % lockString(lock))
            if lock is None:
                return False
            # Remove url to lock mapping
            key = "URL2TOKEN:%s" % lock.get("root")
            if key in self._dict:
                #                _logger.debug("    delete token %s from url %s" % (token, lock.get("root")))
                tokList = self._dict[key]
                if len(tokList) > 1:
                    # Note: shelve dictionary returns copies, so we must reassign values:
                    tokList.remove(token)
                    self._dict[key] = tokList
                else:
                    del self._dict[key]
            # Remove the lock
            del self._dict[token]

            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 = self.get(token)
                if lock:
                    if tokenOnly:
                        lockList.append(lock["token"])
                    else:
                        lockList.append(lock)

        path = normalizeLockRoot(path)
        self._lock.acquireRead()
        try:
            key = "URL2TOKEN:%s" % path
            tokList = self._dict.get(key, [])
            lockList = []
            if includeRoot:
                __appendLocks(tokList)

            if includeChildren:
                for u, ltoks in self._dict.items():
                    if util.isChildUri(key, u):
                        __appendLocks(ltoks)

            return lockList
        finally:
            self._lock.release()
Esempio n. 9
0
class LockManager(object):
    """
    Implements locking functionality using a custom storage layer.
    
    """
    LOCK_TIME_OUT_DEFAULT = 604800  # 1 week, in seconds

    def __init__(self, storage):
        """
        storage:
            LockManagerStorage object
        """
        assert hasattr(storage, "getLockList")
        self._lock = ReadWriteLock()
        self.storage = storage
        self.storage.open()

    def __del__(self):
        self.storage.close()

    def __repr__(self):
        return "%s(%r)" % (self.__class__.__name__, self.storage)

    def _dump(self, msg="", out=None):
        if out is None:
            out = sys.stdout

        urlDict = {}  # { <url>: [<tokenlist>] }
        ownerDict = {}  # { <LOCKOWNER>: [<tokenlist>] }
        userDict = {}  # { <LOCKUSER>: [<tokenlist>] }
        tokenDict = {}  # { <token>: <LOCKURLS> }

        print >> out, "%s: %s" % (self, msg)

        for lock in self.storage.getLockList("/",
                                             includeRoot=True,
                                             includeChildren=True,
                                             tokenOnly=False):
            tok = lock["token"]
            tokenDict[tok] = lockString(lock)
            userDict.setdefault(lock["principal"], []).append(tok)
            ownerDict.setdefault(lock["owner"], []).append(tok)
            urlDict.setdefault(lock["root"], []).append(tok)

#            assert ("URL2TOKEN:" + v["root"]) in self._dict, "Inconsistency: missing URL2TOKEN:%s" % v["root"]
#            assert v["token"] in self._dict["URL2TOKEN:" + v["root"]], "Inconsistency: missing token %s in URL2TOKEN:%s" % (v["token"], v["root"])

        print >> out, "Locks:"
        pprint(tokenDict, indent=0, width=255)
        if tokenDict:
            print >> out, "Locks by URL:"
            pprint(urlDict, indent=4, width=255, stream=out)
            print >> out, "Locks by principal:"
            pprint(userDict, indent=4, width=255, stream=out)
            print >> out, "Locks by owner:"
            pprint(ownerDict, indent=4, width=255, stream=out)

    def _generateLock(self, principal, locktype, lockscope, lockdepth,
                      lockowner, path, timeout):
        """Acquire lock and return lockDict.

        principal
            Name of the principal.
        locktype
            Must be 'write'.
        lockscope
            Must be 'shared' or 'exclusive'.
        lockdepth
            Must be '0' or 'infinity'.
        lockowner
            String identifying the owner.
        path
            Resource URL.
        timeout
            Seconds to live
            
        This function does NOT check, if the new lock creates a conflict!
        """
        if timeout is None:
            timeout = LockManager.LOCK_TIME_OUT_DEFAULT
        elif timeout < 0:
            timeout = -1

        lockDict = {
            "root": path,
            "type": locktype,
            "scope": lockscope,
            "depth": lockdepth,
            "owner": lockowner,
            "timeout": timeout,
            "principal": principal,
        }
        #
        self.storage.create(path, lockDict)
        return lockDict

    def acquire(self, url, locktype, lockscope, lockdepth, lockowner, timeout,
                principal, tokenList):
        """Check for permissions and acquire a lock.
        
        On success return new lock dictionary.
        On error raise a DAVError with an embedded DAVErrorCondition.
        """
        url = normalizeLockRoot(url)
        self._lock.acquireWrite()
        try:
            # Raises DAVError on conflict:
            self._checkLockPermission(url, locktype, lockscope, lockdepth,
                                      tokenList, principal)
            return self._generateLock(principal, locktype, lockscope,
                                      lockdepth, lockowner, url, timeout)
        finally:
            self._lock.release()

    def refresh(self, token, timeout=None):
        """Set new timeout for lock, if existing and valid."""
        if timeout is None:
            timeout = LockManager.LOCK_TIME_OUT_DEFAULT
        return self.storage.refresh(token, timeout)

    def getLock(self, token, key=None):
        """Return lockDict, or None, if not found or invalid. 
        
        Side effect: if lock is expired, it will be purged and None is returned.

        key: 
            name of lock attribute that will be returned instead of a dictionary. 
        """
        assert key in (None, "type", "scope", "depth", "owner", "root",
                       "timeout", "principal", "token")
        lock = self.storage.get(token)
        if key is None or lock is None:
            return lock
        return lock[key]

    def release(self, token):
        """Delete lock."""
        self.storage.delete(token)

    def isTokenLockedByUser(self, token, principal):
        """Return True, if <token> exists, is valid, and bound to <principal>."""
        return self.getLock(token, "principal") == principal

#    def getUrlLockList(self, url, principal=None):

    def getUrlLockList(self, url):
        """Return list of lockDict, if <url> is protected by at least one direct, valid lock.
        
        Side effect: expired locks for this url are purged.
        """
        url = normalizeLockRoot(url)
        lockList = self.storage.getLockList(url,
                                            includeRoot=True,
                                            includeChildren=False,
                                            tokenOnly=False)
        return lockList

    def getIndirectUrlLockList(self, url, principal=None):
        """Return a list of valid lockDicts, that protect <path> directly or indirectly.
        
        If a principal is given, only locks owned by this principal are returned.
        Side effect: expired locks for this path and all parents are purged.
        """
        url = normalizeLockRoot(url)
        lockList = []
        u = url
        while u:
            ll = self.storage.getLockList(u,
                                          includeRoot=True,
                                          includeChildren=False,
                                          tokenOnly=False)
            for l in ll:
                if u != url and l["depth"] != "infinity":
                    continue  # We only consider parents with Depth: infinity
                # TODO: handle shared locks in some way?
#                if l["scope"] == "shared" and lockscope == "shared" and principal != l["principal"]:
#                    continue  # Only compatible with shared locks by other users
                if principal is None or principal == l["principal"]:
                    lockList.append(l)
            u = util.getUriParent(u)
        return lockList

    def isUrlLocked(self, url):
        """Return True, if url is directly locked."""
        lockList = self.getUrlLockList(url)
        return len(lockList) > 0

    def isUrlLockedByToken(self, url, locktoken):
        """Check, if url (or any of it's parents) is locked by locktoken."""
        lockUrl = self.getLock(locktoken, "root")
        return lockUrl and util.isEqualOrChildUri(lockUrl, url)

    def removeAllLocksFromUrl(self, url):
        self._lock.acquireWrite()
        try:
            lockList = self.getUrlLockList(url)
            for lock in lockList:
                self.release(lock["token"])
        finally:
            self._lock.release()

    def _checkLockPermission(self, url, locktype, lockscope, lockdepth,
                             tokenList, principal):
        """Check, if <principal> can lock <url>, otherwise raise an error.
        
        If locking <url> would create a conflict, DAVError(HTTP_LOCKED) is 
        raised. An embedded DAVErrorCondition contains the conflicting resource. 

        @see http://www.webdav.org/specs/rfc4918.html#lock-model

        - Parent locks WILL NOT be conflicting, if they are depth-0.
        - Exclusive depth-infinity parent locks WILL be conflicting, even if 
          they are owned by <principal>.
        - Child locks WILL NOT be conflicting, if we request a depth-0 lock.
        - Exclusive child locks WILL be conflicting, even if they are owned by 
          <principal>. (7.7)
        - It is not enough to check whether a lock is owned by <principal>, but 
          also the token must be passed with the request. (Because <principal> 
          may run two different applications on his client.)
        - <principal> cannot lock-exclusive, if he holds a parent shared-lock.
          (This would only make sense, if he was the only shared-lock holder.)
        - TODO: litmus tries to acquire a shared lock on one resource twice 
          (locks: 27 'double_sharedlock') and fails, when we return HTTP_LOCKED. 
          So we allow multi shared locks on a resource even for the same 
          principal.
        
        @param url: URL that shall be locked
        @param locktype: "write"
        @param lockscope: "shared"|"exclusive"
        @param lockdepth: "0"|"infinity"
        @param tokenList: list of lock tokens, that the user submitted in If: header
        @param principal: name of the principal requesting a lock 

        @return: None (or raise)
        """
        assert locktype == "write"
        assert lockscope in ("shared", "exclusive")
        assert lockdepth in ("0", "infinity")

        _logger.debug("checkLockPermission(%s, %s, %s, %s)" %
                      (url, lockscope, lockdepth, principal))

        # Error precondition to collect conflicting URLs
        errcond = DAVErrorCondition(PRECONDITION_CODE_LockConflict)

        self._lock.acquireRead()
        try:
            # Check url and all parents for conflicting locks
            u = url
            while u:
                ll = self.getUrlLockList(u)
                for l in ll:
                    _logger.debug("    check parent %s, %s" %
                                  (u, lockString(l)))
                    if u != url and l["depth"] != "infinity":
                        # We only consider parents with Depth: infinity
                        continue
                    elif l["scope"] == "shared" and lockscope == "shared":
                        # Only compatible with shared locks (even by same principal)
                        continue
                    # Lock conflict
                    _logger.debug(" -> DENIED due to locked parent %s" %
                                  lockString(l))
                    errcond.add_href(l["root"])
                u = util.getUriParent(u)

            if lockdepth == "infinity":
                # Check child URLs for conflicting locks
                childLocks = self.storage.getLockList(url,
                                                      includeRoot=False,
                                                      includeChildren=True,
                                                      tokenOnly=False)

                for l in childLocks:
                    assert util.isChildUri(url, l["root"])
                    #                    if util.isChildUri(url, l["root"]):
                    _logger.debug(" -> DENIED due to locked child %s" %
                                  lockString(l))
                    errcond.add_href(l["root"])
        finally:
            self._lock.release()

        # If there were conflicts, raise HTTP_LOCKED for <url>, and pass
        # conflicting resource with 'no-conflicting-lock' precondition
        if len(errcond.hrefs) > 0:
            raise DAVError(HTTP_LOCKED, errcondition=errcond)
        return

    def checkWritePermission(self, url, depth, tokenList, principal):
        """Check, if <principal> can modify <url>, otherwise raise HTTP_LOCKED.
        
        If modifying <url> is prevented by a lock, DAVError(HTTP_LOCKED) is 
        raised. An embedded DAVErrorCondition contains the conflicting locks. 

        <url> may be modified by <principal>, if it is not currently locked
        directly or indirectly (i.e. by a locked parent).
        For depth-infinity operations, <url> also must not have locked children. 

        It is not enough to check whether a lock is owned by <principal>, but 
        also the token must be passed with the request. Because <principal> may 
        run two different applications.  

        See http://www.webdav.org/specs/rfc4918.html#lock-model
            http://www.webdav.org/specs/rfc4918.html#rfc.section.7.4

        TODO: verify assumptions:
        - Parent locks WILL NOT be conflicting, if they are depth-0.
        - Exclusive child locks WILL be conflicting, even if they are owned by <principal>.
        
        @param url: URL that shall be modified, created, moved, or deleted
        @param depth: "0"|"infinity"
        @param tokenList: list of lock tokens, that the principal submitted in If: header
        @param principal: name of the principal requesting a lock 

        @return: None or raise error
        """
        assert depth in ("0", "infinity")
        _logger.debug("checkWritePermission(%s, %s, %s, %s)" %
                      (url, depth, tokenList, principal))

        # Error precondition to collect conflicting URLs
        errcond = DAVErrorCondition(PRECONDITION_CODE_LockConflict)

        self._lock.acquireRead()
        try:
            # Check url and all parents for conflicting locks
            u = url
            while u:
                ll = self.getUrlLockList(u)
                _logger.debug("  checking %s" % u)
                for l in ll:
                    _logger.debug("     l=%s" % lockString(l))
                    if u != url and l["depth"] != "infinity":
                        # We only consider parents with Depth: inifinity
                        continue
                    elif principal == l["principal"] and l[
                            "token"] in tokenList:
                        # User owns this lock
                        continue
                    else:
                        # Token is owned by principal, but not passed with lock list
                        _logger.debug(" -> DENIED due to locked parent %s" %
                                      lockString(l))
                        errcond.add_href(l["root"])
                u = util.getUriParent(u)

            if depth == "infinity":
                # Check child URLs for conflicting locks
                childLocks = self.storage.getLockList(url,
                                                      includeRoot=False,
                                                      includeChildren=True,
                                                      tokenOnly=False)

                for l in childLocks:
                    assert util.isChildUri(url, l["root"])
                    #                    if util.isChildUri(url, l["root"]):
                    _logger.debug(" -> DENIED due to locked child %s" %
                                  lockString(l))
                    errcond.add_href(l["root"])
        finally:
            self._lock.release()

        # If there were conflicts, raise HTTP_LOCKED for <url>, and pass
        # conflicting resource with 'no-conflicting-lock' precondition
        if len(errcond.hrefs) > 0:
            raise DAVError(HTTP_LOCKED, errcondition=errcond)
        return
Esempio n. 10
0
 def __init__(self):
     self._dict = None
     self._loaded = False
     self._lock = ReadWriteLock()
     self._verbose = 2
Esempio n. 11
0
class PropertyManager(object):
    """
    An in-memory property manager implementation using a dictionary.
    
    This is obviously not persistent, but should be enough in some cases.
    For a persistent implementation, see property_manager.ShelvePropertyManager().
    """
    def __init__(self):
        self._dict = None
        self._loaded = False
        self._lock = ReadWriteLock()
        self._verbose = 2

    def __repr__(self):
        return "PropertyManager"

    def __del__(self):
        if __debug__ and self._verbose >= 2:
            self._check()
        self._close()

    def _lazyOpen(self):
        _logger.debug("_lazyOpen()")
        self._lock.acquireWrite()
        try:
            self._dict = {}
            self._loaded = True
        finally:
            self._lock.release()

    def _sync(self):
        pass

    def _close(self):
        _logger.debug("_close()")
        self._lock.acquireWrite()
        try:
            self._dict = None
            self._loaded = False
        finally:
            self._lock.release()

    def _check(self, msg=""):
        try:
            if not self._loaded:
                return True
#            for k in self._dict.keys():
#                print "%s" % k
#                print "  -> %s" % self._dict[k]
#            self._dump()
            for k, v in self._dict.items():
                _ = "%s, %s" % (k, v)
#            _logger.debug("%s checks ok %s" % (self.__class__.__name__, msg))
            return True
        except Exception:
            _logger.exception("%s _check: ERROR %s" %
                              (self.__class__.__name__, msg))
            #            traceback.print_exc()
            #            raise
            #            sys.exit(-1)
            return False

    def _dump(self, msg="", out=None):
        if out is None:
            out = sys.stdout
        print >> out, "%s(%s): %s" % (self.__class__.__name__, self.__repr__(),
                                      msg)
        if not self._loaded:
            self._lazyOpen()
            if self._verbose >= 2:
                return  # Already dumped in _lazyOpen
        try:
            for k, v in self._dict.items():
                print >> out, "    ", k
                for k2, v2 in v.items():
                    try:
                        print >> out, "        %s: '%s'" % (k2, v2)
                    except Exception, e:
                        print >> out, "        %s: ERROR %s" % (k2, e)
            out.flush()
        except Exception, e:
            util.warn("PropertyManager._dump()  ERROR: %s" % e)
Esempio n. 12
0
 def __init__(self):
     self._dict = None
     self._lock = ReadWriteLock()
Esempio n. 13
0
class LockStorageDict(object):
    """
    An in-memory lock manager storage implementation using a dictionary.

    R/W access is guarded by a thread.lock object.

    Also, to make it work with a Shelve dictionary, modifying dictionary
    members is done by re-assignment and we call a _flush() method.

    This is obviously not persistent, but should be enough in some cases.
    For a persistent implementation, see lock_manager.LockStorageShelve().

    Notes:
        expire is stored as expiration date in seconds since epoch (not in
        seconds until expiration).

    The dictionary is built like::

        { 'URL2TOKEN:/temp/litmus/lockme': ['opaquelocktoken:0x1d7b86...',
                                            'opaquelocktoken:0xd7d4c0...'],
          'opaquelocktoken:0x1d7b86...': { 'depth': '0',
                                           'owner': "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\\n<owner xmlns="DAV:">litmus test suite</owner>\\n",
                                           'principal': 'tester',
                                           'root': '/temp/litmus/lockme',
                                           'scope': 'shared',
                                           'expire': 1261328382.4530001,
                                           'token': 'opaquelocktoken:0x1d7b86...',
                                           'type': 'write',
                                           },
          'opaquelocktoken:0xd7d4c0...': { 'depth': '0',
                                           'owner': '<?xml version=\'1.0\' encoding=\'UTF-8\'?>\\n<owner xmlns="DAV:">litmus: notowner_sharedlock</owner>\\n',
                                           'principal': 'tester',
                                           'root': '/temp/litmus/lockme',
                                           'scope': 'shared',
                                           'expire': 1261328381.6040001,
                                           'token': 'opaquelocktoken:0xd7d4c0...',
                                           'type': 'write'
                                           },
         }
    """
    LOCK_TIME_OUT_DEFAULT = 604800 # 1 week, in seconds
    LOCK_TIME_OUT_MAX = 4 * 604800 # 1 month, in seconds

    def __init__(self):
        self._dict = None
        self._lock = ReadWriteLock()


    def __repr__(self):
        return self.__class__.__name__


    def __del__(self):
        pass


    def _flush(self):
        """Overloaded by Shelve implementation."""
        pass


    def open(self):
        """Called before first use.

        May be implemented to initialize a storage.
        """
        assert self._dict is None
        self._dict = {}


    def close(self):
        """Called on shutdown."""
        self._dict = None


    def cleanup(self):
        """Purge expired locks (optional)."""
        pass


    def clear(self):
        """Delete all entries."""
        if self._dict is not None:
            self._dict.clear()


    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 = self._dict.get(token)
            if lock is None:
                # Lock not found: purge dangling URL2TOKEN entries
                _logger.debug("Lock purged dangling: %s" % token)
                self.delete(token)
                return None
            expire = float(lock["expire"])
            if expire >= 0 and expire < time.time():
                _logger.debug("Lock timed-out(%s): %s" % (expire, lockString(lock)))
                self.delete(token)
                return None
            return lock
        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 = LockStorageDict.LOCK_TIME_OUT_DEFAULT
            elif timeout < 0 or timeout > LockStorageDict.LOCK_TIME_OUT_MAX:
                timeout = LockStorageDict.LOCK_TIME_OUT_MAX

            lock["timeout"] = timeout
            lock["expire"] = time.time() + timeout

            validateLock(lock)

            token = generateLockToken()
            lock["token"] = token

            # Store lock
            self._dict[token] = lock

            # Store locked path reference
            key = "URL2TOKEN:%s" % path
            if not key in self._dict:
                self._dict[key] = [ token ]
            else:
                # Note: Shelve dictionary returns copies, so we must reassign values:
                tokList = self._dict[key]
                tokList.append(token)
                self._dict[key] = tokList
            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.
        """
        assert token in self._dict, "Lock must exist"
        assert timeout == -1 or timeout > 0
        if timeout < 0 or timeout > LockStorageDict.LOCK_TIME_OUT_MAX:
            timeout = LockStorageDict.LOCK_TIME_OUT_MAX

        self._lock.acquireWrite()
        try:
            # Note: shelve dictionary returns copies, so we must reassign values:
            lock = self._dict[token]
            lock["timeout"] = timeout
            lock["expire"] = time.time() + timeout
            self._dict[token] = lock
            self._flush()
        finally:
            self._lock.release()
        return lock


    def delete(self, token):
        """Delete lock.

        Returns True on success. False, if token does not exist, or is expired.
        """
        self._lock.acquireWrite()
        try:
            lock = self._dict.get(token)
            _logger.debug("delete %s" % lockString(lock))
            if lock is None:
                return False
            # Remove url to lock mapping
            key = "URL2TOKEN:%s" % lock.get("root")
            if key in self._dict:
#                _logger.debug("    delete token %s from url %s" % (token, lock.get("root")))
                tokList = self._dict[key]
                if len(tokList) > 1:
                    # Note: shelve dictionary returns copies, so we must reassign values:
                    tokList.remove(token)
                    self._dict[key] = tokList
                else:
                    del self._dict[key]
            # Remove the lock
            del self._dict[token]

            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 = self.get(token)
                if lock:
                    if tokenOnly:
                        lockList.append(lock["token"])
                    else:
                        lockList.append(lock)

        path = normalizeLockRoot(path)
        self._lock.acquireRead()
        try:
            key = "URL2TOKEN:%s" % path
            tokList = self._dict.get(key, [])
            lockList = []
            if includeRoot:
                __appendLocks(tokList)

            if includeChildren:
                for u, ltoks in self._dict.items():
                    if util.isChildUri(key, u):
                        __appendLocks(ltoks)

            return lockList
        finally:
            self._lock.release()
Esempio n. 14
0
class LockManager(object):
    """
    Implements locking functionality using a custom storage layer.
    
    """
    LOCK_TIME_OUT_DEFAULT = 604800 # 1 week, in seconds

    def __init__(self, storage):
        """
        storage:
            LockManagerStorage object
        """
        assert hasattr(storage, "getLockList")
        self._lock = ReadWriteLock()
        self.storage = storage
        self.storage.open()


    def __del__(self):
        self.storage.close()


    def __repr__(self):
        return "%s(%r)" % (self.__class__.__name__, self.storage)
    
    
    def _dump(self, msg="", out=None):
        if out is None:
            out = sys.stdout

        urlDict = {} # { <url>: [<tokenlist>] }
        ownerDict = {} # { <LOCKOWNER>: [<tokenlist>] }
        userDict = {} # { <LOCKUSER>: [<tokenlist>] }
        tokenDict = {} # { <token>: <LOCKURLS> } 
        
        print >>out, "%s: %s" % (self, msg)
        
        for lock in self.storage.getLockList("/", includeRoot=True, 
                                             includeChildren=True, 
                                             tokenOnly=False):
            tok = lock["token"]
            tokenDict[tok] = lockString(lock)
            userDict.setdefault(lock["principal"], []).append(tok)
            ownerDict.setdefault(lock["owner"], []).append(tok)
            urlDict.setdefault(lock["root"], []).append(tok)
            
#            assert ("URL2TOKEN:" + v["root"]) in self._dict, "Inconsistency: missing URL2TOKEN:%s" % v["root"]
#            assert v["token"] in self._dict["URL2TOKEN:" + v["root"]], "Inconsistency: missing token %s in URL2TOKEN:%s" % (v["token"], v["root"])
                
        print >>out, "Locks:" 
        pprint(tokenDict, indent=0, width=255)
        if tokenDict:
            print >>out, "Locks by URL:" 
            pprint(urlDict, indent=4, width=255, stream=out)
            print >>out, "Locks by principal:" 
            pprint(userDict, indent=4, width=255, stream=out)
            print >>out, "Locks by owner:" 
            pprint(ownerDict, indent=4, width=255, stream=out)


    def _generateLock(self, principal, 
                      locktype, lockscope, lockdepth, lockowner, path, timeout):
        """Acquire lock and return lockDict.

        principal
            Name of the principal.
        locktype
            Must be 'write'.
        lockscope
            Must be 'shared' or 'exclusive'.
        lockdepth
            Must be '0' or 'infinity'.
        lockowner
            String identifying the owner.
        path
            Resource URL.
        timeout
            Seconds to live
            
        This function does NOT check, if the new lock creates a conflict!
        """
        if timeout is None:
            timeout = LockManager.LOCK_TIME_OUT_DEFAULT
        elif timeout < 0:
            timeout = -1      
        
        lockDict = {"root": path,
                    "type": locktype,
                    "scope": lockscope,
                    "depth": lockdepth,
                    "owner": lockowner,
                    "timeout": timeout,
                    "principal": principal, 
                    }
        #
        self.storage.create(path, lockDict)
        return lockDict


    def acquire(self, url, locktype, lockscope, lockdepth, lockowner, timeout, 
                principal, tokenList):
        """Check for permissions and acquire a lock.
        
        On success return new lock dictionary.
        On error raise a DAVError with an embedded DAVErrorCondition.
        """
        url = normalizeLockRoot(url)
        self._lock.acquireWrite()
        try:
            # Raises DAVError on conflict:
            self._checkLockPermission(url, locktype, lockscope, lockdepth, tokenList, principal)
            return self._generateLock(principal, locktype, lockscope, lockdepth, lockowner, url, timeout)
        finally:
            self._lock.release()
        

    def refresh(self, token, timeout=None):
        """Set new timeout for lock, if existing and valid."""
        if timeout is None:
            timeout = LockManager.LOCK_TIME_OUT_DEFAULT
        return self.storage.refresh(token, timeout)


    def getLock(self, token, key=None):
        """Return lockDict, or None, if not found or invalid. 
        
        Side effect: if lock is expired, it will be purged and None is returned.

        key: 
            name of lock attribute that will be returned instead of a dictionary. 
        """
        assert key in (None, "type", "scope", "depth", "owner", "root", 
                       "timeout", "principal", "token")
        lock = self.storage.get(token)
        if key is None or lock is None:
            return lock
        return lock[key]


    def release(self, token):
        """Delete lock."""
        self.storage.delete(token)


    def isTokenLockedByUser(self, token, principal):
        """Return True, if <token> exists, is valid, and bound to <principal>."""   
        return self.getLock(token, "principal") == principal


#    def getUrlLockList(self, url, principal=None):
    def getUrlLockList(self, url):
        """Return list of lockDict, if <url> is protected by at least one direct, valid lock.
        
        Side effect: expired locks for this url are purged.
        """
        url = normalizeLockRoot(url)
        lockList = self.storage.getLockList(url, includeRoot=True, 
                                            includeChildren=False, 
                                            tokenOnly=False)
        return lockList


    def getIndirectUrlLockList(self, url, principal=None):
        """Return a list of valid lockDicts, that protect <path> directly or indirectly.
        
        If a principal is given, only locks owned by this principal are returned.
        Side effect: expired locks for this path and all parents are purged.
        """   
        url = normalizeLockRoot(url)
        lockList = []
        u = url 
        while u:
            ll = self.storage.getLockList(u, includeRoot=True, 
                                          includeChildren=False, 
                                          tokenOnly=False)
            for l in ll:
                if u != url and l["depth"] != "infinity":
                    continue  # We only consider parents with Depth: infinity
                # TODO: handle shared locks in some way?
#                if l["scope"] == "shared" and lockscope == "shared" and principal != l["principal"]:
#                    continue  # Only compatible with shared locks by other users 
                if principal is None or principal == l["principal"]:
                    lockList.append(l)
            u = util.getUriParent(u)
        return lockList


    def isUrlLocked(self, url):
        """Return True, if url is directly locked."""
        lockList = self.getUrlLockList(url)
        return len(lockList) > 0

        
    def isUrlLockedByToken(self, url, locktoken):
        """Check, if url (or any of it's parents) is locked by locktoken."""
        lockUrl = self.getLock(locktoken, "root")
        return lockUrl and util.isEqualOrChildUri(lockUrl, url) 


    def removeAllLocksFromUrl(self, url):
        self._lock.acquireWrite()
        try:
            lockList = self.getUrlLockList(url)
            for lock in lockList:
                self.release(lock["token"])
        finally:
            self._lock.release()               


    def _checkLockPermission(self, url, locktype, lockscope, lockdepth, 
                             tokenList, principal):
        """Check, if <principal> can lock <url>, otherwise raise an error.
        
        If locking <url> would create a conflict, DAVError(HTTP_LOCKED) is 
        raised. An embedded DAVErrorCondition contains the conflicting resource. 

        @see http://www.webdav.org/specs/rfc4918.html#lock-model

        - Parent locks WILL NOT be conflicting, if they are depth-0.
        - Exclusive depth-infinity parent locks WILL be conflicting, even if 
          they are owned by <principal>.
        - Child locks WILL NOT be conflicting, if we request a depth-0 lock.
        - Exclusive child locks WILL be conflicting, even if they are owned by 
          <principal>. (7.7)
        - It is not enough to check whether a lock is owned by <principal>, but 
          also the token must be passed with the request. (Because <principal> 
          may run two different applications on his client.)
        - <principal> cannot lock-exclusive, if he holds a parent shared-lock.
          (This would only make sense, if he was the only shared-lock holder.)
        - TODO: litmus tries to acquire a shared lock on one resource twice 
          (locks: 27 'double_sharedlock') and fails, when we return HTTP_LOCKED. 
          So we allow multi shared locks on a resource even for the same 
          principal.
        
        @param url: URL that shall be locked
        @param locktype: "write"
        @param lockscope: "shared"|"exclusive"
        @param lockdepth: "0"|"infinity"
        @param tokenList: list of lock tokens, that the user submitted in If: header
        @param principal: name of the principal requesting a lock 

        @return: None (or raise)
        """
        self.removeAllLocksFromUrl(url)
        return


    def checkWritePermission(self, url, depth, tokenList, principal):
        """Check, if <principal> can modify <url>, otherwise raise HTTP_LOCKED.
        
        If modifying <url> is prevented by a lock, DAVError(HTTP_LOCKED) is 
        raised. An embedded DAVErrorCondition contains the conflicting locks. 

        <url> may be modified by <principal>, if it is not currently locked
        directly or indirectly (i.e. by a locked parent).
        For depth-infinity operations, <url> also must not have locked children. 

        It is not enough to check whether a lock is owned by <principal>, but 
        also the token must be passed with the request. Because <principal> may 
        run two different applications.  

        See http://www.webdav.org/specs/rfc4918.html#lock-model
            http://www.webdav.org/specs/rfc4918.html#rfc.section.7.4

        TODO: verify assumptions:
        - Parent locks WILL NOT be conflicting, if they are depth-0.
        - Exclusive child locks WILL be conflicting, even if they are owned by <principal>.
        
        @param url: URL that shall be modified, created, moved, or deleted
        @param depth: "0"|"infinity"
        @param tokenList: list of lock tokens, that the principal submitted in If: header
        @param principal: name of the principal requesting a lock 

        @return: None or raise error
        """
        self.removeAllLocksFromUrl(url)
        return