From 8b34f020ad2117cbbee5c3b1e885d514dd22126a Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone <exarkun@twistedmatrix.com> Date: Tue, 20 Aug 2019 10:46:09 -0400 Subject: [PATCH] Split a bunch of Foolscap stuff off into its own module --- src/_zkapauthorizer/_storage_server.py | 117 ++---------------------- src/_zkapauthorizer/foolscap.py | 118 +++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 110 deletions(-) create mode 100644 src/_zkapauthorizer/foolscap.py diff --git a/src/_zkapauthorizer/_storage_server.py b/src/_zkapauthorizer/_storage_server.py index e917100..d9c7739 100644 --- a/src/_zkapauthorizer/_storage_server.py +++ b/src/_zkapauthorizer/_storage_server.py @@ -20,6 +20,10 @@ This is the server part of a storage access protocol. The client part is implemented in ``_storage_client.py``. """ +from __future__ import ( + absolute_import, +) + import attr from attr.validators import ( provides, @@ -29,127 +33,20 @@ from zope.interface import ( implementer_only, ) -from foolscap.constraint import ( - ByteStringConstraint, -) from foolscap.api import ( - ListOf, Referenceable, ) from foolscap.ipb import ( IReferenceable, IRemotelyCallable, ) -from foolscap.remoteinterface import ( - RemoteMethodSchema, - RemoteInterface, -) - from allmydata.interfaces import ( RIStorageServer, ) -# The Foolscap convention seems to be to try to constrain inputs to valid -# values. So we'll try to limit the number of tokens a client can supply. -# Foolscap may be moving away from this so we may eventually drop this as -# well. Though it may still make sense on a non-Foolscap protocol (eg HTTP) -# which Tahoe-LAFS may eventually support. -# -# In any case, for now, pick some fairly arbitrary value. I am deliberately -# picking a small number here and expect to have to raise. However, ideally, -# a client could accomplish a lot with a few tokens while also not wasting a -# lot of value. -MAXIMUM_TOKENS_PER_CALL = 10 - -# This is the length of a serialized PrivacyPass pass (there's a lot of -# confusion between "tokens" and "passes" here, sadly). -TOKEN_LENGTH = 97 - -# Take those values and turn them into the appropriate Foolscap constraint -# objects. Foolscap seems to have a convention of representing these as -# CamelCase module-level values so I replicate that here. -Token = ByteStringConstraint(maxLength=TOKEN_LENGTH, minLength=TOKEN_LENGTH) -TokenList = ListOf(Token, maxLength=MAXIMUM_TOKENS_PER_CALL) - - -def add_tokens(schema): - """ - Add a ``tokens`` parameter to the given method schema. - - :param foolscap.remoteinterface.RemoteMethodSchema schema: An existing - method schema to modify. - - :return foolscap.remoteinterface.RemoteMethodSchema: A schema like - ``schema`` but with one additional required argument. - """ - return add_arguments(schema, [(b"tokens", TokenList)]) - - -def add_arguments(schema, kwargs): - """ - Create a new schema like ``schema`` but with the arguments given by - ``kwargs`` prepended to the signature. - - :param foolscap.remoteinterface.RemoteMethodSchema schema: The existing - schema. - - :param list[(bytes, foolscap.IConstraint)] kwargs: The arguments to - prepend to the signature of ``schema``. - - :return foolscap.remoteinterface.RemoteMethodSchema: The new schema - object. - """ - new_kwargs = dict(schema.argConstraints) - new_kwargs.update(kwargs) - modified_schema = RemoteMethodSchema(**new_kwargs) - # Initialized from **new_kwargs, RemoteMethodSchema.argumentNames is in - # some arbitrary, probably-incorrect order. This breaks user code which - # tries to use positional arguments. Put them back in the order they were - # in originally (in the input ``schema``), prepended with the newly added - # arguments. - modified_schema.argumentNames = ( - # The new arguments - list(argName for (argName, _) in kwargs) + - # The original arguments in the original order - schema.argumentNames - ) - return modified_schema - - - -class RITokenAuthorizedStorageServer(RemoteInterface): - """ - An object which can store and retrieve shares, subject to token-based - authorization. - - This is much the same as ``allmydata.interfaces.RIStorageServer`` but - several of its methods take an additional ``tokens`` parameter. Clients - are expected to supply suitable tokens and only after the tokens have been - validated is service provided. - """ - __remote_name__ = ( - "RITokenAuthorizedStorageServer.tahoe.privatestorage.io" - ) - - get_version = RIStorageServer["get_version"] - - allocate_buckets = add_tokens(RIStorageServer["allocate_buckets"]) - - add_lease = add_tokens(RIStorageServer["add_lease"]) - - renew_lease = add_tokens(RIStorageServer["renew_lease"]) - - get_buckets = RIStorageServer["get_buckets"] - - slot_readv = RIStorageServer["slot_readv"] - - slot_testv_and_readv_and_writev = add_tokens( - RIStorageServer["slot_testv_and_readv_and_writev"], - ) - - advise_corrupt_share = RIStorageServer["advise_corrupt_share"] - - +from .foolscap import ( + RITokenAuthorizedStorageServer, +) @implementer_only(RITokenAuthorizedStorageServer, IReferenceable, IRemotelyCallable) # It would be great to use `frozen=True` (value-based hashing) instead of diff --git a/src/_zkapauthorizer/foolscap.py b/src/_zkapauthorizer/foolscap.py new file mode 100644 index 0000000..224614d --- /dev/null +++ b/src/_zkapauthorizer/foolscap.py @@ -0,0 +1,118 @@ +from __future__ import ( + absolute_import, +) + +from foolscap.constraint import ( + ByteStringConstraint, +) +from foolscap.api import ( + ListOf, +) +from foolscap.remoteinterface import ( + RemoteMethodSchema, + RemoteInterface, +) + +from allmydata.interfaces import ( + RIStorageServer, +) + +# The Foolscap convention seems to be to try to constrain inputs to valid +# values. So we'll try to limit the number of tokens a client can supply. +# Foolscap may be moving away from this so we may eventually drop this as +# well. Though it may still make sense on a non-Foolscap protocol (eg HTTP) +# which Tahoe-LAFS may eventually support. +# +# In any case, for now, pick some fairly arbitrary value. I am deliberately +# picking a small number here and expect to have to raise. However, ideally, +# a client could accomplish a lot with a few tokens while also not wasting a +# lot of value. +MAXIMUM_TOKENS_PER_CALL = 10 + +# This is the length of a serialized PrivacyPass pass (there's a lot of +# confusion between "tokens" and "passes" here, sadly). +TOKEN_LENGTH = 97 + +# Take those values and turn them into the appropriate Foolscap constraint +# objects. Foolscap seems to have a convention of representing these as +# CamelCase module-level values so I replicate that here. +Token = ByteStringConstraint(maxLength=TOKEN_LENGTH, minLength=TOKEN_LENGTH) +TokenList = ListOf(Token, maxLength=MAXIMUM_TOKENS_PER_CALL) + + +def add_tokens(schema): + """ + Add a ``tokens`` parameter to the given method schema. + + :param foolscap.remoteinterface.RemoteMethodSchema schema: An existing + method schema to modify. + + :return foolscap.remoteinterface.RemoteMethodSchema: A schema like + ``schema`` but with one additional required argument. + """ + return add_arguments(schema, [(b"tokens", TokenList)]) + + +def add_arguments(schema, kwargs): + """ + Create a new schema like ``schema`` but with the arguments given by + ``kwargs`` prepended to the signature. + + :param foolscap.remoteinterface.RemoteMethodSchema schema: The existing + schema. + + :param list[(bytes, foolscap.IConstraint)] kwargs: The arguments to + prepend to the signature of ``schema``. + + :return foolscap.remoteinterface.RemoteMethodSchema: The new schema + object. + """ + new_kwargs = dict(schema.argConstraints) + new_kwargs.update(kwargs) + modified_schema = RemoteMethodSchema(**new_kwargs) + # Initialized from **new_kwargs, RemoteMethodSchema.argumentNames is in + # some arbitrary, probably-incorrect order. This breaks user code which + # tries to use positional arguments. Put them back in the order they were + # in originally (in the input ``schema``), prepended with the newly added + # arguments. + modified_schema.argumentNames = ( + # The new arguments + list(argName for (argName, _) in kwargs) + + # The original arguments in the original order + schema.argumentNames + ) + return modified_schema + + + +class RITokenAuthorizedStorageServer(RemoteInterface): + """ + An object which can store and retrieve shares, subject to token-based + authorization. + + This is much the same as ``allmydata.interfaces.RIStorageServer`` but + several of its methods take an additional ``tokens`` parameter. Clients + are expected to supply suitable tokens and only after the tokens have been + validated is service provided. + """ + __remote_name__ = ( + "RITokenAuthorizedStorageServer.tahoe.privatestorage.io" + ) + + get_version = RIStorageServer["get_version"] + + allocate_buckets = add_tokens(RIStorageServer["allocate_buckets"]) + + add_lease = add_tokens(RIStorageServer["add_lease"]) + + renew_lease = add_tokens(RIStorageServer["renew_lease"]) + + get_buckets = RIStorageServer["get_buckets"] + + slot_readv = RIStorageServer["slot_readv"] + + slot_testv_and_readv_and_writev = add_tokens( + RIStorageServer["slot_testv_and_readv_and_writev"], + ) + + advise_corrupt_share = RIStorageServer["advise_corrupt_share"] -- GitLab