Exemple #1
0
def _pack_normalized_children(children, writekey, deep_immutable=False):
    """Take a dict that maps:
         children[unicode_nfc_name] = (IFileSystemNode, metadata_dict)
    and pack it into a single string, for use as the contents of the backing
    file. This is the same format as is returned by _unpack_contents. I also
    accept an AuxValueDict, in which case I'll use the auxilliary cached data
    as the pre-packed entry, which is faster than re-packing everything each
    time.

    If writekey is provided then I will superencrypt the child's writecap with
    writekey.

    If deep_immutable is True, I will require that all my children are deeply
    immutable, and will raise a MustBeDeepImmutableError if not.
    """
    precondition((writekey is None) or isinstance(writekey, str), writekey)

    has_aux = isinstance(children, AuxValueDict)
    entries = []
    for name in sorted(children.keys()):
        assert isinstance(name, unicode)
        entry = None
        (child, metadata) = children[name]
        child.raise_error()
        if deep_immutable and not child.is_allowed_in_immutable_directory():
            raise MustBeDeepImmutableError(
                "child %s is not allowed in an immutable directory" %
                quote_output(name, encoding='utf-8'), name)
        if has_aux:
            entry = children.get_aux(name)
        if not entry:
            assert IFilesystemNode.providedBy(child), (name, child)
            assert isinstance(metadata, dict)
            rw_uri = child.get_write_uri()
            if rw_uri is None:
                rw_uri = ""
            assert isinstance(rw_uri, str), rw_uri

            # should be prevented by MustBeDeepImmutableError check above
            assert not (rw_uri and deep_immutable)

            ro_uri = child.get_readonly_uri()
            if ro_uri is None:
                ro_uri = ""
            assert isinstance(ro_uri, str), ro_uri
            if writekey is not None:
                writecap = netstring(_encrypt_rw_uri(writekey, rw_uri))
            else:
                writecap = ZERO_LEN_NETSTR
            entry = "".join([
                netstring(name.encode("utf-8")),
                netstring(strip_prefix_for_ro(ro_uri, deep_immutable)),
                writecap,
                netstring(json.dumps(metadata))
            ])
        entries.append(netstring(entry))
    return "".join(entries)
Exemple #2
0
def from_string(u, deep_immutable=False, name=u"<unknown name>"):
    """Create URI from either unicode or byte string."""
    if isinstance(u, unicode):
        u = u.encode("utf-8")
    if not isinstance(u, bytes):
        raise TypeError("URI must be unicode string or bytes: %r" % (u, ))

    # We allow and check ALLEGED_READONLY_PREFIX or ALLEGED_IMMUTABLE_PREFIX
    # on all URIs, even though we would only strictly need to do so for caps of
    # new formats (post Tahoe-LAFS 1.6). URIs that are not consistent with their
    # prefix are treated as unknown. This should be revisited when we add the
    # new cap formats. See ticket #833 comment:31.
    s = u
    can_be_mutable = can_be_writeable = not deep_immutable
    if s.startswith(ALLEGED_IMMUTABLE_PREFIX):
        can_be_mutable = can_be_writeable = False
        s = s[len(ALLEGED_IMMUTABLE_PREFIX):]
    elif s.startswith(ALLEGED_READONLY_PREFIX):
        can_be_writeable = False
        s = s[len(ALLEGED_READONLY_PREFIX):]

    error = None
    try:
        if s.startswith(b'URI:CHK:'):
            return CHKFileURI.init_from_string(s)
        elif s.startswith(b'URI:CHK-Verifier:'):
            return CHKFileVerifierURI.init_from_string(s)
        elif s.startswith(b'URI:LIT:'):
            return LiteralFileURI.init_from_string(s)
        elif s.startswith(b'URI:SSK:'):
            if can_be_writeable:
                return WriteableSSKFileURI.init_from_string(s)
            kind = "URI:SSK file writecap"
        elif s.startswith(b'URI:SSK-RO:'):
            if can_be_mutable:
                return ReadonlySSKFileURI.init_from_string(s)
            kind = "URI:SSK-RO readcap to a mutable file"
        elif s.startswith(b'URI:SSK-Verifier:'):
            return SSKVerifierURI.init_from_string(s)
        elif s.startswith(b'URI:MDMF:'):
            if can_be_writeable:
                return WriteableMDMFFileURI.init_from_string(s)
            kind = "URI:MDMF file writecap"
        elif s.startswith(b'URI:MDMF-RO:'):
            if can_be_mutable:
                return ReadonlyMDMFFileURI.init_from_string(s)
            kind = "URI:MDMF-RO readcap to a mutable file"
        elif s.startswith(b'URI:MDMF-Verifier:'):
            return MDMFVerifierURI.init_from_string(s)
        elif s.startswith(b'URI:DIR2:'):
            if can_be_writeable:
                return DirectoryURI.init_from_string(s)
            kind = "URI:DIR2 directory writecap"
        elif s.startswith(b'URI:DIR2-RO:'):
            if can_be_mutable:
                return ReadonlyDirectoryURI.init_from_string(s)
            kind = "URI:DIR2-RO readcap to a mutable directory"
        elif s.startswith(b'URI:DIR2-Verifier:'):
            return DirectoryURIVerifier.init_from_string(s)
        elif s.startswith(b'URI:DIR2-CHK:'):
            return ImmutableDirectoryURI.init_from_string(s)
        elif s.startswith(b'URI:DIR2-CHK-Verifier:'):
            return ImmutableDirectoryURIVerifier.init_from_string(s)
        elif s.startswith(b'URI:DIR2-LIT:'):
            return LiteralDirectoryURI.init_from_string(s)
        elif s.startswith(b'URI:DIR2-MDMF:'):
            if can_be_writeable:
                return MDMFDirectoryURI.init_from_string(s)
            kind = "URI:DIR2-MDMF directory writecap"
        elif s.startswith(b'URI:DIR2-MDMF-RO:'):
            if can_be_mutable:
                return ReadonlyMDMFDirectoryURI.init_from_string(s)
            kind = "URI:DIR2-MDMF-RO readcap to a mutable directory"
        elif s.startswith(b'URI:DIR2-MDMF-Verifier:'):
            return MDMFDirectoryURIVerifier.init_from_string(s)
        elif s.startswith(
                b'x-tahoe-future-test-writeable:') and not can_be_writeable:
            # For testing how future writeable caps would behave in read-only contexts.
            kind = "x-tahoe-future-test-writeable: testing cap"
        elif s.startswith(
                b'x-tahoe-future-test-mutable:') and not can_be_mutable:
            # For testing how future mutable readcaps would behave in immutable contexts.
            kind = "x-tahoe-future-test-mutable: testing cap"
        else:
            return UnknownURI(u)

        # We fell through because a constraint was not met.
        # Prefer to report the most specific constraint.
        if not can_be_mutable:
            error = MustBeDeepImmutableError(
                kind + " used in an immutable context", name)
        else:
            error = MustBeReadonlyError(kind + " used in a read-only context",
                                        name)

    except BadURIError as e:
        error = e

    return UnknownURI(u, error=error)
Exemple #3
0
    def __init__(self,
                 given_rw_uri,
                 given_ro_uri,
                 deep_immutable=False,
                 name=u"<unknown name>"):
        assert given_rw_uri is None or isinstance(given_rw_uri, str)
        assert given_ro_uri is None or isinstance(given_ro_uri, str)
        given_rw_uri = given_rw_uri or None
        given_ro_uri = given_ro_uri or None

        # We don't raise errors when creating an UnknownNode; we instead create an
        # opaque node (with rw_uri and ro_uri both None) that records the error.
        # This avoids breaking operations that never store the opaque node.
        # Note that this means that if a stored dirnode has only a rw_uri, it
        # might be dropped. Any future "write-only" cap formats should have a dummy
        # unusable readcap to stop that from happening.

        self.error = None
        self.rw_uri = self.ro_uri = None
        if given_rw_uri:
            if deep_immutable:
                if given_rw_uri.startswith(
                        ALLEGED_IMMUTABLE_PREFIX) and not given_ro_uri:
                    # We needed an immutable cap, and were given one. It was given in the
                    # rw_uri slot, but that's fine; we'll move it to ro_uri below.
                    pass
                elif not given_ro_uri:
                    self.error = MustNotBeUnknownRWError(
                        "cannot attach unknown rw cap as immutable child",
                        name, True)
                    return  # node will be opaque
                else:
                    # We could report either error, but this probably makes more sense.
                    self.error = MustBeDeepImmutableError(
                        "cannot attach unknown rw cap as immutable child",
                        name)
                    return  # node will be opaque

            if not given_ro_uri:
                # We were given a single cap argument, or a rw_uri with no ro_uri.

                if not (given_rw_uri.startswith(ALLEGED_READONLY_PREFIX)
                        or given_rw_uri.startswith(ALLEGED_IMMUTABLE_PREFIX)):
                    # If the single cap is unprefixed, then we cannot tell whether it is a
                    # writecap, and we don't know how to diminish it to a readcap if it is one.
                    # If it didn't *already* have at least an ALLEGED_READONLY_PREFIX, then
                    # prefixing it would be a bad idea because we have been given no reason
                    # to believe that it is a readcap, so we might be letting a client
                    # inadvertently grant excess write authority.
                    self.error = MustNotBeUnknownRWError(
                        "cannot attach unknown rw cap as child", name, False)
                    return  # node will be opaque

                # OTOH, if the single cap already had a prefix (which is of the required
                # strength otherwise an error would have been thrown above), then treat it
                # as though it had been given in the ro_uri slot. This has a similar effect
                # to the use for known caps of 'bigcap = writecap or readcap' in
                # nodemaker.py: create_from_cap. It enables copying of unknown readcaps to
                # work in as many cases as we can securely allow.
                given_ro_uri = given_rw_uri
                given_rw_uri = None
            elif given_ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX):
                # Strange corner case: we were given a cap in both slots, with the ro_uri
                # alleged to be immutable. A real immutable object wouldn't have a writecap.
                self.error = MustBeDeepImmutableError(
                    "cannot accept a child entry that specifies "
                    "both rw_uri, and ro_uri with an imm. prefix", name)
                return  # node will be opaque

        # If the ro_uri definitely fails the constraint, it should be treated as opaque and
        # the error recorded.
        if given_ro_uri:
            read_cap = uri.from_string(given_ro_uri,
                                       deep_immutable=deep_immutable,
                                       name=name)
            if isinstance(read_cap, uri.UnknownURI):
                self.error = read_cap.get_error()
                if self.error:
                    assert self.rw_uri is None and self.ro_uri is None
                    return

        if deep_immutable:
            assert self.rw_uri is None
            # strengthen the constraint on ro_uri to ALLEGED_IMMUTABLE_PREFIX
            if given_ro_uri:
                if given_ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX):
                    self.ro_uri = given_ro_uri
                elif given_ro_uri.startswith(ALLEGED_READONLY_PREFIX):
                    self.ro_uri = ALLEGED_IMMUTABLE_PREFIX + given_ro_uri[
                        len(ALLEGED_READONLY_PREFIX):]
                else:
                    self.ro_uri = ALLEGED_IMMUTABLE_PREFIX + given_ro_uri
        else:
            # not immutable, so a writecap is allowed
            self.rw_uri = given_rw_uri
            # strengthen the constraint on ro_uri to ALLEGED_READONLY_PREFIX
            if given_ro_uri:
                if (given_ro_uri.startswith(ALLEGED_READONLY_PREFIX)
                        or given_ro_uri.startswith(ALLEGED_IMMUTABLE_PREFIX)):
                    self.ro_uri = given_ro_uri
                else:
                    self.ro_uri = ALLEGED_READONLY_PREFIX + given_ro_uri
Exemple #4
0
def from_string(u, deep_immutable=False, name=u"<unknown name>"):
    if not isinstance(u, str):
        raise TypeError("unknown URI type: %s.." % str(u)[:100])

    # We allow and check ALLEGED_READONLY_PREFIX or ALLEGED_IMMUTABLE_PREFIX
    # on all URIs, even though we would only strictly need to do so for caps of
    # new formats (post Tahoe-LAFS 1.6). URIs that are not consistent with their
    # prefix are treated as unknown. This should be revisited when we add the
    # new cap formats. See <http://allmydata.org/trac/tahoe/ticket/833#comment:31>.
    s = u
    can_be_mutable = can_be_writeable = not deep_immutable
    if s.startswith(ALLEGED_IMMUTABLE_PREFIX):
        can_be_mutable = can_be_writeable = False
        s = s[len(ALLEGED_IMMUTABLE_PREFIX):]
    elif s.startswith(ALLEGED_READONLY_PREFIX):
        can_be_writeable = False
        s = s[len(ALLEGED_READONLY_PREFIX):]

    error = None
    kind = "cap"
    try:
        if s.startswith('URI:CHK:'):
            return CHKFileURI.init_from_string(s)
        elif s.startswith('URI:CHK-Verifier:'):
            return CHKFileVerifierURI.init_from_string(s)
        elif s.startswith('URI:LIT:'):
            return LiteralFileURI.init_from_string(s)
        elif s.startswith('URI:SSK:'):
            if can_be_writeable:
                return WriteableSSKFileURI.init_from_string(s)
            kind = "URI:SSK file writecap"
        elif s.startswith('URI:SSK-RO:'):
            if can_be_mutable:
                return ReadonlySSKFileURI.init_from_string(s)
            kind = "URI:SSK-RO readcap to a mutable file"
        elif s.startswith('URI:SSK-Verifier:'):
            return SSKVerifierURI.init_from_string(s)
        elif s.startswith('URI:DIR2:'):
            if can_be_writeable:
                return DirectoryURI.init_from_string(s)
            kind = "URI:DIR2 directory writecap"
        elif s.startswith('URI:DIR2-RO:'):
            if can_be_mutable:
                return ReadonlyDirectoryURI.init_from_string(s)
            kind = "URI:DIR2-RO readcap to a mutable directory"
        elif s.startswith('URI:DIR2-Verifier:'):
            return DirectoryURIVerifier.init_from_string(s)
        elif s.startswith('URI:DIR2-CHK:'):
            return ImmutableDirectoryURI.init_from_string(s)
        elif s.startswith('URI:DIR2-LIT:'):
            return LiteralDirectoryURI.init_from_string(s)
        elif s.startswith('x-tahoe-future-test-writeable:') and not can_be_writeable:
            # For testing how future writeable caps would behave in read-only contexts.
            kind = "x-tahoe-future-test-writeable: testing cap"
        elif s.startswith('x-tahoe-future-test-mutable:') and not can_be_mutable:
            # For testing how future mutable readcaps would behave in immutable contexts.
            kind = "x-tahoe-future-test-mutable: testing cap"
        else:
            return UnknownURI(u)

        # We fell through because a constraint was not met.
        # Prefer to report the most specific constraint.
        if not can_be_mutable:
            error = MustBeDeepImmutableError(kind + " used in an immutable context", name)
        else:
            error = MustBeReadonlyError(kind + " used in a read-only context", name)
            
    except BadURIError, e:
        error = e