Пример #1
0
    def getAlias(self, aliasid, token, path):
        """Returns a LibraryFileAlias, or raises LookupError.

        A LookupError is raised if no record with the given ID exists
        or if not related LibraryFileContent exists.

        :param token: The token for the file. If None no token is present.
            When a token is supplied, it is looked up with path.
        :param path: The path the request is for, unused unless a token
            is supplied; when supplied it must match the token. The
            value of path is expected to be that from a twisted request.args
            e.g. /foo/bar.
        """
        restricted = self.restricted
        if token and path:
            # with a token and a path we may be able to serve restricted files
            # on the public port.
            store = session_store()
            token_found = store.find(TimeLimitedToken,
                SQL("age(created) < interval '1 day'"),
                TimeLimitedToken.token == token,
                TimeLimitedToken.path==path).is_empty()
            store.reset()
            if token_found:
                raise LookupError("Token stale/pruned/path mismatch")
            else:
                restricted = True
        alias = LibraryFileAlias.selectOne(And(
            LibraryFileAlias.id == aliasid,
            LibraryFileAlias.contentID == LibraryFileContent.q.id,
            LibraryFileAlias.restricted == restricted))
        if alias is None:
            raise LookupError("No file alias with LibraryFileContent")
        return alias
Пример #2
0
    def allocate(url):
        """Allocate a token for url path in the librarian.

        :param url: A url bytestring. e.g.
            https://i123.restricted.launchpad-librarian.net/123/foo.txt
            Note that the token is generated for 123/foo.txt
        :return: A url fragment token ready to be attached to the url.
            e.g. 'a%20token'
        """
        # We use random.random to get a string which varies reasonably, and we
        # hash it to distribute it widely and get a easily copy and pastable
        # single string (nice for debugging). The randomness is not a key
        # factor here: as long as tokens are not guessable, they are hidden by
        # https, not exposed directly in the API (tokens will be allocated by
        # the appropriate objects), not by direct access to the
        # TimeLimitedToken class.
        baseline = str(random.random())
        hashed = md5(baseline).hexdigest()
        token = hashed
        store = session_store()
        path = TimeLimitedToken.url_to_token_path(url)
        store.add(TimeLimitedToken(path, token))
        # The session isn't part of the main transaction model, and in fact it
        # has autocommit on. The commit here is belts and bracers: after
        # allocation the external librarian must be able to serve the file
        # immediately.
        store.commit()
        return token
Пример #3
0
    def allocate(url):
        """Allocate a token for url path in the librarian.

        :param url: A url bytestring. e.g.
            https://i123.restricted.launchpad-librarian.net/123/foo.txt
            Note that the token is generated for 123/foo.txt
        :return: A url fragment token ready to be attached to the url.
            e.g. 'a%20token'
        """
        # We use random.random to get a string which varies reasonably, and we
        # hash it to distribute it widely and get a easily copy and pastable
        # single string (nice for debugging). The randomness is not a key
        # factor here: as long as tokens are not guessable, they are hidden by
        # https, not exposed directly in the API (tokens will be allocated by
        # the appropriate objects), not by direct access to the
        # TimeLimitedToken class.
        baseline = str(random.random())
        hashed = md5(baseline).hexdigest()
        token = hashed
        store = session_store()
        path = TimeLimitedToken.url_to_token_path(url)
        store.add(TimeLimitedToken(path, token))
        # The session isn't part of the main transaction model, and in fact it
        # has autocommit on. The commit here is belts and bracers: after
        # allocation the external librarian must be able to serve the file
        # immediately.
        store.commit()
        return token
Пример #4
0
 def test_TimeLimitedTokenPruner(self):
     # Ensure there are no tokens
     store = sqlbase.session_store()
     map(store.remove, store.find(TimeLimitedToken))
     store.flush()
     self.assertEqual(0, len(list(store.find(TimeLimitedToken,
         path="sample path"))))
     # One to clean and one to keep
     store.add(TimeLimitedToken(path="sample path", token="foo",
         created=datetime(2008, 01, 01, tzinfo=UTC)))
 def test_allocate(self):
     store = session_store()
     store.find(TimeLimitedToken).remove()
     token1 = TimeLimitedToken.allocate('foo://')
     token2 = TimeLimitedToken.allocate('foo://')
     # We must get unique tokens
     self.assertNotEqual(token1, token2)
     # They must be bytestrings (as a surrogate for valid url fragment')
     self.assertIsInstance(token1, str)
     self.assertIsInstance(token2, str)
 def test_allocate(self):
     store = session_store()
     store.find(TimeLimitedToken).remove()
     token1 = TimeLimitedToken.allocate('foo://')
     token2 = TimeLimitedToken.allocate('foo://')
     # We must get unique tokens
     self.assertNotEqual(token1, token2)
     # They must be bytestrings (as a surrogate for valid url fragment')
     self.assertIsInstance(token1, str)
     self.assertIsInstance(token2, str)
Пример #7
0
 def _get_secret(self):
     # Because our CookieClientIdManager is not persistent, we need to
     # pull the secret from some other data store - failing to do this
     # would mean a new secret is generated every time the server is
     # restarted, invalidating all old session information.
     # Secret is looked up here rather than in __init__, because
     # we can't be sure the database connections are setup at that point.
     if self._secret is None:
         store = session_store()
         result = store.execute("SELECT secret FROM secret")
         self._secret = result.get_one()[0]
     return self._secret
Пример #8
0
    def getAlias(self, aliasid, token, path):
        """Returns a LibraryFileAlias, or raises LookupError.

        A LookupError is raised if no record with the given ID exists
        or if not related LibraryFileContent exists.

        :param aliasid: A `LibraryFileAlias` ID.
        :param token: The token for the file. If None no token is present.
            When a token is supplied, it is looked up with path.
        :param path: The path the request is for, unused unless a token
            is supplied; when supplied it must match the token. The
            value of path is expected to be that from a twisted request.args
            e.g. /foo/bar.
        """
        restricted = self.restricted
        if token and path:
            # With a token and a path we may be able to serve restricted files
            # on the public port.
            if isinstance(token, Macaroon):
                # Macaroons have enough other constraints that they don't
                # need to be path-specific; it's simpler and faster to just
                # check the alias ID.
                token_ok = threads.blockingCallFromThread(
                    default_reactor, self._verifyMacaroon, token, aliasid)
            else:
                # The URL-encoding of the path may have changed somewhere
                # along the line, so reencode it canonically. LFA.filename
                # can't contain slashes, so they're safe to leave unencoded.
                # And urllib.quote erroneously excludes ~ from its safe set,
                # while RFC 3986 says it should be unescaped and Chromium
                # forcibly decodes it in any URL that it sees.
                #
                # This needs to match url_path_quote.
                normalised_path = urllib.quote(urllib.unquote(path),
                                               safe='/~+')
                store = session_store()
                token_ok = not store.find(
                    TimeLimitedToken, SQL("age(created) < interval '1 day'"),
                    TimeLimitedToken.token
                    == hashlib.sha256(token).hexdigest(), TimeLimitedToken.path
                    == normalised_path).is_empty()
                store.reset()
            if token_ok:
                restricted = True
            else:
                raise LookupError("Token stale/pruned/path mismatch")
        alias = LibraryFileAlias.selectOne(
            And(LibraryFileAlias.id == aliasid,
                LibraryFileAlias.contentID == LibraryFileContent.q.id,
                LibraryFileAlias.restricted == restricted))
        if alias is None:
            raise LookupError("No file alias with LibraryFileContent")
        return alias
Пример #9
0
 def _get_secret(self):
     # Because our CookieClientIdManager is not persistent, we need to
     # pull the secret from some other data store - failing to do this
     # would mean a new secret is generated every time the server is
     # restarted, invalidating all old session information.
     # Secret is looked up here rather than in __init__, because
     # we can't be sure the database connections are setup at that point.
     if self._secret is None:
         store = session_store()
         result = store.execute("SELECT secret FROM secret")
         self._secret = result.get_one()[0]
     return self._secret
Пример #10
0
 def test_restricted_with_expired_token(self):
     fileAlias, url = self.get_restricted_file_and_public_url()
     # We have the base url for a restricted file; grant access to it
     # for a short time.
     token = TimeLimitedToken.allocate(url)
     # But time has passed
     store = session_store()
     tokens = store.find(TimeLimitedToken, TimeLimitedToken.token==token)
     tokens.set(
         TimeLimitedToken.created==SQL("created - interval '1 week'"))
     url = url + "?token=%s" % token
     # Now, as per test_restricted_no_token we should get a 404.
     self.require404(url)
Пример #11
0
 def test_restricted_with_expired_token(self):
     fileAlias, url = self.get_restricted_file_and_public_url()
     # We have the base url for a restricted file; grant access to it
     # for a short time.
     token = TimeLimitedToken.allocate(url)
     # But time has passed
     store = session_store()
     tokens = store.find(
         TimeLimitedToken,
         TimeLimitedToken.token == hashlib.sha256(token).hexdigest())
     tokens.set(
         TimeLimitedToken.created == SQL("created - interval '1 week'"))
     # Now, as per test_restricted_no_token we should get a 404.
     self.require404(url, params={"token": token})
Пример #12
0
    def allocate(url):
        """Allocate a token for url path in the librarian.

        :param url: A url bytestring. e.g.
            https://i123.restricted.launchpad-librarian.net/123/foo.txt
            Note that the token is generated for 123/foo.txt
        :return: A url fragment token ready to be attached to the url.
            e.g. 'a%20token'
        """
        store = session_store()
        path = TimeLimitedToken.url_to_token_path(url)
        token = create_token(32).encode('ascii')
        store.add(TimeLimitedToken(path, token))
        # The session isn't part of the main transaction model, and in fact it
        # has autocommit on. The commit here is belts and bracers: after
        # allocation the external librarian must be able to serve the file
        # immediately.
        store.commit()
        return token
Пример #13
0
def session_default_store(cls):
    """Adapt an Session database object to an `IStore`."""
    return session_store()
Пример #14
0
def session_slave_store(cls):
    """Adapt a Session database object to an `ISlaveStore`."""
    return session_store()
Пример #15
0
def session_master_store(cls):
    """Adapt a Session database object to an `IMasterStore`."""
    return session_store()
Пример #16
0
def session_default_store(cls):
    """Adapt an Session database object to an `IStore`."""
    return session_store()
Пример #17
0
def session_slave_store(cls):
    """Adapt a Session database object to an `ISlaveStore`."""
    return session_store()
Пример #18
0
def session_master_store(cls):
    """Adapt a Session database object to an `IMasterStore`."""
    return session_store()