diff --git a/docs/source/backup.rst b/docs/source/backup.rst deleted file mode 100644 index b6f8c6e3065ecec790eefd33038ded693a321b00..0000000000000000000000000000000000000000 --- a/docs/source/backup.rst +++ /dev/null @@ -1,78 +0,0 @@ -ZKAP Backup/Restore -=================== - -A large part of the intended purpose of ZKAPs is to allow a value exchange between storage provider and storage consumer. -As such the ZKAPs themselves represent some value. -Thus it is to be expected that users will want that value safe-guarded. -One way to do this is for the internal state of ZKAPAuthorizer to be backed up periodically. - -Overview --------- - -ZKAPAuthorizer's internal state can be backed up and restored by backing up and restoring a SQLite3 database it maintains. -After a backup has been taken it is possible to update a small "checkpoint" that keeps track of spent ZKAPs. -This makes it relatively efficient to keep a backup up-to-date with respect to spending operations. -Whenever a new voucher is purchased a new complete backup must be made to capture the associated new state. - -Backup ------- - -The Database -~~~~~~~~~~~~ - -ZKAPAuthorizer keeps all of its internal state in a SQLite3 database. -This database is kept in the private directory of the Tahoe-LAFS node into which the plugin is installed. -The database filename is ``privatestorageio-zkapauthz-v1.sqlite3``. -For example, -for a Tahoe-LAFS node that keeps it state at ``~/.tahoe``, -the ZKAPAuthorizer database can be found at ``~/.tahoe/private/privatestorageio-zkapauthz-v1.sqlite3``. - -The existence of the databaes file is consider part of ZKAPAuthorizer's public interface. -The fact that all of ZKAPAuthorizer's internal state is stored in this database is considered part of the public interface as well. - -The exact schema and contents of this database are *not* considered part of the public interface. -Third-parties should feel free to back up this database file -(following SQLite3-recommended practices) -and restore it as necessary to recover using this backup. -Third-parties should not make any other assumptions about the file -(such as that it has a particular schema). - -The Checkpoint -~~~~~~~~~~~~~~ - -ZKAPAuthorizer spends ZKAPs in a deterministic order. -This means if the next ZKAP to be spent is known then it is possible to separate all other ZKAPs into "already spent" and "not spent" groups. -ZKAPAuthorizer exposes the next ZKAP to be spent like this:: - - GET /storage-plugins/privatestorageio-zkapauthz-v1/unblinded-token?limit=1 - -The checkpoint is the first element of the ``unblinded-tokens`` property of the response. - -See :file:interface.rst for details. - -Third-parties should periodically get this value and update the backup with it. - -Restore -------- - -The Database -~~~~~~~~~~~~ - -It is sufficient to copy the backed up database file into the correct location. -This is the same location from which it was originally copied, -relative to the Tahoe-LAFS node directory. - -This must be done while the Tahoe-LAFS node is not running. -It may be done prior to the first run. - -The Checkpoint -~~~~~~~~~~~~~~ - -After the Tahoe-LAFS node is started the checkpoint can be used to discard the "already spent" ZKAPs from the database:: - - PATCH /storage-plugins/privatestorageio-zkapauthz-v1/unblinded-token - Content-Type: application/json - - { "first-unspent": <checkpoint> } - -This shortens the time it takes for the node to complete the recovery process proportionally to the number of "already spent" ZKAPs are being discarded. diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 2828d4abe93b20bdfcc6e836f6abace6665d4c84..6b01d214f5d34346d68b4b27db7ed28e3cd9681e 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -8,9 +8,9 @@ To enable the plugin at all, add its name to the list of storage plugins in the (``tahoe.cfg`` in the relevant node directory):: [client] - storage.plugins = privatestorageio-zkapauthz-v1 + storage.plugins = privatestorageio-zkapauthz-v2 -Then configure the plugin as desired in the ``storageclient.plugins.privatestorageio-zkapauthz-v1`` section. +Then configure the plugin as desired in the ``storageclient.plugins.privatestorageio-zkapauthz-v2`` section. redeemer ~~~~~~~~ @@ -20,7 +20,7 @@ The ``dummy`` value is useful for testing purposes only. For example:: - [storageclient.plugins.privatestorageio-zkapauthz-v1] + [storageclient.plugins.privatestorageio-zkapauthz-v2] redeemer = dummy issuer-public-key = YXNkYXNkYXNkYXNkYXNkCg== @@ -31,7 +31,7 @@ In this case, the ``ristretto-issuer-root-url`` item is also required. For example:: - [storageclient.plugins.privatestorageio-zkapauthz-v1] + [storageclient.plugins.privatestorageio-zkapauthz-v2] redeemer = ristretto ristretto-issuer-root-url = https://issuer.example.invalid/ @@ -40,14 +40,14 @@ If the values are not the same, the client will decline to use the storage serve The client can also be configured with the value of a single pass:: - [storageclient.plugins.privatestorageio-zkapauthz-v1] + [storageclient.plugins.privatestorageio-zkapauthz-v2] pass-value = 1048576 The value given here must agree with the value servers use in their configuration or the storage service will be unusable. The client can also be configured with the number of passes to expect in exchange for one voucher:: - [storageclient.plugins.privatestorageio-zkapauthz-v1] + [storageclient.plugins.privatestorageio-zkapauthz-v2] default-token-count = 32768 The value given here must agree with the value the issuer uses in its configuration or redemption may fail. @@ -58,7 +58,7 @@ allowed-public-keys Regardless of which redeemer is selected, the client must also be configured with the public part of the issuer key pair which it will allow to sign tokens:: - [storageclient.plugins.privatestorageio-zkapauthz-v1] + [storageclient.plugins.privatestorageio-zkapauthz-v2] allowed-public-keys = AAAA...,BBBB...,CCCC... The ``allowed-public-keys`` value is a comma-separated list of encoded public keys. @@ -76,7 +76,7 @@ The client will try to make the average (mean) interval between runs equal to th The value is an integer number of seconds. For example to run on average every 26 days:: - [storageclient.plugins.privatestorageio-zkapauthz-v1] + [storageclient.plugins.privatestorageio-zkapauthz-v2] lease.crawl-interval.mean = 2246400 @@ -88,7 +88,7 @@ The random intervals between runs have a uniform distribution with this item's v The value is an integer number of seconds. For example to make all intervals fall within a 7 day period:: - [storageclient.plugins.privatestorageio-zkapauthz-v1] + [storageclient.plugins.privatestorageio-zkapauthz-v2] lease.crawl-interval.range = 302400 @@ -101,7 +101,7 @@ If the crawler encounters a lease with less time left than this then it will ren The value is an integer number of seconds. For example to renew leases on all shares which will expire in less than one week:: - [storageclient.plugins.privatestorageio-zkapauthz-v1] + [storageclient.plugins.privatestorageio-zkapauthz-v2] lease.min-time-remaining = 604800 Server @@ -111,16 +111,16 @@ To enable the plugin at all, add its name to the list of storage plugins in the (``tahoe.cfg`` in the relevant node directory):: [storage] - plugins = privatestorageio-zkapauthz-v1 + plugins = privatestorageio-zkapauthz-v2 Then also configure the Ristretto-flavored PrivacyPass issuer the server will announce to clients:: - [storageserver.plugins.privatestorageio-zkapauthz-v1] + [storageserver.plugins.privatestorageio-zkapauthz-v2] ristretto-issuer-root-url = https://issuer.example.invalid/ The value of a single pass in the system can be configured here as well:: - [storageserver.plugins.privatestorageio-zkapauthz-v1] + [storageserver.plugins.privatestorageio-zkapauthz-v2] pass-value = 1048576 If no ``pass-value`` is given then a default will be used. @@ -130,7 +130,7 @@ The storage server must also be configured with the path to the Ristretto-flavor To avoid placing secret material in tahoe.cfg, this configuration is done using a path:: - [storageserver.plugins.privatestorageio-zkapauthz-v1] + [storageserver.plugins.privatestorageio-zkapauthz-v2] ristretto-signing-key-path = /path/to/signing.key The signing key is the keystone secret to the entire system and must be managed with extreme care to prevent unintended disclosure. diff --git a/docs/source/interface.rst b/docs/source/interface.rst index d3223d6fe60e9a9af9a009050a8a2b181816fe9d..27fe303e275f2185e6cd0349c3d18ba319ea2b45 100644 --- a/docs/source/interface.rst +++ b/docs/source/interface.rst @@ -19,7 +19,7 @@ For example, if the secret token is ``ABCDEF``:: The correct value for the token can be read from the Tahoe-LAFS node's ``private/api_auth_token`` file. -``GET /storage-plugins/privatestorageio-zkapauthz-v1/version`` +``GET /storage-plugins/privatestorageio-zkapauthz-v2/version`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This endpoint returns the version of the ZKAPAuthorizer Python package in use by the Tahoe-LAFS client node. @@ -29,7 +29,7 @@ The response is **OK** with an ``application/json`` **Content-Type**:: { "version": <string> } -``PUT /storage-plugins/privatestorageio-zkapauthz-v1/voucher`` +``PUT /storage-plugins/privatestorageio-zkapauthz-v2/voucher`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This endpoint allows an external agent which has submitted a payment to cause the plugin to redeem the voucher for tokens. @@ -44,7 +44,7 @@ If the voucher cannot be accepted at the time of the request then the response c If the response is **OK** then a repeated request with the same body will have no effect. If the response is not **OK** then a repeated request with the same body will try to accept the number again. -``GET /storage-plugins/privatestorageio-zkapauthz-v1/voucher/<voucher>`` +``GET /storage-plugins/privatestorageio-zkapauthz-v2/voucher/<voucher>`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This endpoint allows an external agent to monitor the status of the redemption of a voucher. @@ -125,7 +125,7 @@ The *finished* timestamp gives the time when the unpaid error was encountered. The *finished* timestamp gives the time when this other error condition was encountered. The *details* string may give additional details about what the error was. -``GET /storage-plugins/privatestorageio-zkapauthz-v1/voucher`` +``GET /storage-plugins/privatestorageio-zkapauthz-v2/voucher`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This endpoint allows an external agent to retrieve the status of all vouchers. @@ -137,32 +137,20 @@ The response is **OK** with ``application/json`` content-type response body like The elements of the list are objects like the one returned by issuing a **GET** to a child of this collection resource. -``GET /storage-plugins/privatestorageio-zkapauthz-v1/unblinded-token`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This endpoint allows an external agent to retrieve unused unblinded tokens present in the node's database. -Unblinded tokens are returned in a stable order. -This order matches the order in which tokens will be used by the system. -This endpoint accepts several query arguments: +``GET /storage-plugins/privatestorageio-zkapauthz-v2/lease-maintenance`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * limit: An integer limiting the number of unblinded tokens to retrieve. - * position: A string which can be compared against unblinded token values. - Only unblinded tokens which follow this token in the stable order are returned. +This endpoint allows an external agent to retrieve information about automatic spending for lease maintenance. This endpoint accepts no request body. The response is **OK** with ``application/json`` content-type response body like:: - { "total": <integer> - , "spendable": <integer> - , "unblinded-tokens": [<unblinded token string>, ...] + { "spendable": <integer> , "lease-maintenance-spending": <spending object> } -The value associated with ``total`` gives the total number of unblinded tokens in the node's database -(independent of any limit placed on this query). The value associated with ``spendable`` gives the number of unblinded tokens in the node's database which can actually be spent. -The value associated with ``unblinded-tokens`` gives the requested list of unblinded tokens. The ``<spending object>`` may be ``null`` if the lease maintenance process has never run. If it has run, @@ -171,23 +159,7 @@ If it has run, * ``when``: associated with an ISO8601 datetime string giving the approximate time the process ran * ``count``: associated with a number giving the number of passes which would need to be spent to renew leases on all stored objects seen during the lease maintenance activity -``POST /storage-plugins/privatestorageio-zkapauthz-v1/unblinded-token`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This endpoint allows an external agent to insert new unblinded tokens into the node's database. -This allows for restoration of previously backed-up tokens in case the node is lost. -Tokens inserted with this API will be used after any tokens already in the database and in the order they appear in the given list. - -The request body must be ``application/json`` encoded and contain an object like:: - - { "unblinded-tokens": [<unblinded token string>, ...] - } - -The response is **OK** with ``application/json`` content-type response body like:: - - { } - -``POST /storage-plugins/privatestorageio-zkapauthz-v1/calculate-price`` +``POST /storage-plugins/privatestorageio-zkapauthz-v2/calculate-price`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This endpoint allows an agent to calculate the number of ZKAPs it will cost to store a collection of files of specified sizes. diff --git a/src/_zkapauthorizer/_plugin.py b/src/_zkapauthorizer/_plugin.py index 7d25ffac47d2e05b859bd56d92ecdacd3588da86..bc94b2e74277ec89c0e7e0d55e9de60c646e4b1d 100644 --- a/src/_zkapauthorizer/_plugin.py +++ b/src/_zkapauthorizer/_plugin.py @@ -80,7 +80,7 @@ class ZKAPAuthorizer(object): connection. """ - name = attr.ib(default="privatestorageio-zkapauthz-v1") + name = attr.ib() _stores = attr.ib(default=attr.Factory(WeakValueDictionary)) def _get_store(self, node_config): diff --git a/src/_zkapauthorizer/api.py b/src/_zkapauthorizer/api.py index f4bb2c21bfbbf1fcce7ca906538b1a802c677043..4076c7ca5a671762ffcd3f87228ec94965b2c097 100644 --- a/src/_zkapauthorizer/api.py +++ b/src/_zkapauthorizer/api.py @@ -20,6 +20,10 @@ __all__ = [ "ZKAPAuthorizer", ] +# The identifier for this plugin. This appears in URLs for resources the +# client plugin exposes, configuration files, etc. +NAME = "privatestorageio-zkapauthz-v2" + from ._storage_client import ZKAPAuthorizerStorageClient from ._storage_server import LeaseRenewalRequired, ZKAPAuthorizerStorageServer from .storage_common import MorePassesRequired diff --git a/src/_zkapauthorizer/config.py b/src/_zkapauthorizer/config.py index cb9dc3886a0749ca2cca54b4d30800bb48bd70be..f92cde0fa46a45dc490899a18edab5c70cf9fa2f 100644 --- a/src/_zkapauthorizer/config.py +++ b/src/_zkapauthorizer/config.py @@ -21,6 +21,7 @@ from typing import Optional from allmydata.node import _Config +from .api import NAME from .lease_maintenance import LeaseMaintenanceConfig @@ -124,7 +125,7 @@ def _read_duration(cfg, option, default): as a ``timedelta``. """ # type: (_Config, str) -> Optional[timedelta] - section_name = "storageclient.plugins.privatestorageio-zkapauthz-v1" + section_name = "storageclient.plugins." + NAME value_str = cfg.get_config( section=section_name, option=option, diff --git a/src/_zkapauthorizer/model.py b/src/_zkapauthorizer/model.py index 726be23690610c5681067e6f857f0c2a345bf30b..bccbf3d28d54d3a9cc01eb01e9b1ecea4b6180c6 100644 --- a/src/_zkapauthorizer/model.py +++ b/src/_zkapauthorizer/model.py @@ -75,6 +75,12 @@ class NotEnoughTokens(Exception): """ +# The version number in _zkapauthorizer.api.NAME doesn't match the version +# here because the database is persistent state and we need to be sure to load +# the older version even if we signal an API compatibility break by bumping +# the version number elsewhere. Consider this version number part of a +# different scheme where we're versioning our ability to open the database at +# all. The schema inside the database is versioned by yet another mechanism. CONFIG_DB_NAME = "privatestorageio-zkapauthz-v1.sqlite3" @@ -351,30 +357,6 @@ class VoucherStore(object): return list(Voucher.from_row(row) for row in refs) - def _insert_unblinded_tokens(self, cursor, unblinded_tokens, group_id): - """ - Helper function to really insert unblinded tokens into the database. - """ - cursor.executemany( - """ - INSERT INTO [unblinded-tokens] ([token], [redemption-group]) VALUES (?, ?) - """, - list((token, group_id) for token in unblinded_tokens), - ) - - @with_cursor - def insert_unblinded_tokens(self, cursor, unblinded_tokens, group_id): - """ - Store some unblinded tokens, for example as part of a backup-restore - process. - - :param list[str] unblinded_tokens: The unblinded tokens to store. - - :param int group_id: The unique identifier of the redemption group to - which these tokens belong. - """ - self._insert_unblinded_tokens(cursor, unblinded_tokens, group_id) - @with_cursor def insert_unblinded_tokens_for_voucher( self, cursor, voucher, public_key, unblinded_tokens, completed, spendable @@ -448,10 +430,14 @@ class VoucherStore(object): "Cannot insert tokens for unknown voucher; add voucher first" ) - self._insert_unblinded_tokens( - cursor, - list(t.unblinded_token.decode("ascii") for t in unblinded_tokens), - group_id, + cursor.executemany( + """ + INSERT INTO [unblinded-tokens] ([token], [redemption-group]) VALUES (?, ?) + """, + list( + (token.unblinded_token.decode("ascii"), group_id) + for token in unblinded_tokens + ), ) @with_cursor @@ -658,22 +644,6 @@ class VoucherStore(object): """, ) - @with_cursor - def backup(self, cursor): - """ - Read out all state necessary to recreate this database in the event it is - lost. - """ - cursor.execute( - """ - SELECT [token] FROM [unblinded-tokens] ORDER BY [rowid] - """, - ) - tokens = cursor.fetchall() - return { - "unblinded-tokens": list(token for (token,) in tokens), - } - def start_lease_maintenance(self): """ Get an object which can track a newly started round of lease maintenance diff --git a/src/_zkapauthorizer/resource.py b/src/_zkapauthorizer/resource.py index 2a37249287e27d0f1f1dd7ab4d1abd32d8ad6a16..a06c862cb9bf0c8d3c9dd2183b6d44bbf83cdc20 100644 --- a/src/_zkapauthorizer/resource.py +++ b/src/_zkapauthorizer/resource.py @@ -21,9 +21,7 @@ vouchers for fresh tokens. In the future it should also allow users to read statistics about token usage. """ -from itertools import islice -from json import load, loads -from sys import maxsize +from json import loads from twisted.logger import Logger from twisted.web.http import BAD_REQUEST @@ -34,6 +32,7 @@ from zope.interface import Attribute from . import __version__ as _zkapauthorizer_version from ._base64 import urlsafe_b64decode from ._json import dumps_utf8 +from .api import NAME from .config import get_configured_lease_duration from .controller import PaymentController, get_redeemer from .pricecalculator import PriceCalculator @@ -91,7 +90,7 @@ def from_configuration( ): """ Instantiate the plugin root resource using data from its configuration - section, **storageclient.plugins.privatestorageio-zkapauthz-v1**, in the + section, **storageclient.plugins.privatestorageio-zkapauthz-v2**, in the Tahoe-LAFS configuration file. See the configuration documentation for details of the configuration section. @@ -110,16 +109,15 @@ def from_configuration( :return IZKAPRoot: The root of the resource hierarchy presented by the client side of the plugin. """ - plugin_name = "privatestorageio-zkapauthz-v1" if redeemer is None: redeemer = get_redeemer( - plugin_name, + NAME, node_config, None, None, ) default_token_count = get_token_count( - plugin_name, + NAME, node_config, ) controller = PaymentController( @@ -178,8 +176,8 @@ def authorizationless_resource_tree( ), ) root.putChild( - b"unblinded-token", - _UnblindedTokenCollection( + b"lease-maintenance", + _LeaseMaintenanceResource( store, controller, ), @@ -321,10 +319,10 @@ class _ProjectVersion(Resource): ) -class _UnblindedTokenCollection(Resource): +class _LeaseMaintenanceResource(Resource): """ - This class implements inspection of unblinded tokens. Users **GET** this - resource to find out about unblinded tokens in the system. + This class implements inspection of lease maintenance activity. Users + **GET** this resource to learn about lease maintenance spending. """ _log = Logger() @@ -336,40 +334,16 @@ class _UnblindedTokenCollection(Resource): def render_GET(self, request): """ - Retrieve some unblinded tokens and associated information. + Retrieve the spending information. """ application_json(request) - state = self._store.backup() - unblinded_tokens = state["unblinded-tokens"] - - limit = request.args.get(b"limit", [None])[0] - if limit is not None: - limit = min(maxsize, int(limit)) - - position = request.args.get(b"position", [b""])[0].decode("utf-8") - return dumps_utf8( { - "total": len(unblinded_tokens), - "spendable": self._store.count_unblinded_tokens(), - "unblinded-tokens": list( - islice( - (token for token in unblinded_tokens if token > position), limit - ) - ), - "lease-maintenance-spending": self._lease_maintenance_activity(), + "total": self._store.count_unblinded_tokens(), + "spending": self._lease_maintenance_activity(), } ) - def render_POST(self, request): - """ - Store some unblinded tokens. - """ - application_json(request) - unblinded_tokens = load(request.content)["unblinded-tokens"] - self._store.insert_unblinded_tokens(unblinded_tokens, group_id=0) - return dumps_utf8({}) - def _lease_maintenance_activity(self): activity = self._store.get_latest_lease_maintenance_activity() if activity is None: diff --git a/src/_zkapauthorizer/storage_common.py b/src/_zkapauthorizer/storage_common.py index db04c1f650eae7b35a95a5eec6c6896103d2e0d3..c8e8786776d4414675848f400e294bdbe805c2ac 100644 --- a/src/_zkapauthorizer/storage_common.py +++ b/src/_zkapauthorizer/storage_common.py @@ -22,6 +22,7 @@ from typing import Callable import attr from pyutil.mathutil import div_ceil +from .api import NAME from .eliot import MUTABLE_PASSES_REQUIRED from .validators import greater_than @@ -110,7 +111,7 @@ def get_configured_pass_value(node_config): value is read from the **pass-value** option of the ZKAPAuthorizer plugin client section. """ - section_name = "storageclient.plugins.privatestorageio-zkapauthz-v1" + section_name = "storageclient.plugins." + NAME return int( node_config.get_config( section=section_name, @@ -124,7 +125,7 @@ def get_configured_allowed_public_keys(node_config): """ Read the set of allowed issuer public keys from the given configuration. """ - section_name = "storageclient.plugins.privatestorageio-zkapauthz-v1" + section_name = "storageclient.plugins." + NAME return set( node_config.get_config( section=section_name, diff --git a/src/_zkapauthorizer/tests/strategies.py b/src/_zkapauthorizer/tests/strategies.py index d468030d111774a4090ad3e544952b4651895f56..45d278012d75fcfcacdd5ec3976476baa6b7d6fe 100644 --- a/src/_zkapauthorizer/tests/strategies.py +++ b/src/_zkapauthorizer/tests/strategies.py @@ -46,6 +46,7 @@ from twisted.internet.task import Clock from twisted.web.test.requesthelper import DummyRequest from zope.interface import implementer +from ..api import NAME from ..configutil import config_string_from_sections from ..lease_maintenance import LeaseMaintenanceConfig, lease_maintenance_config_to_dict from ..model import ( @@ -478,7 +479,7 @@ def client_lease_maintenance_configurations(maint_configs=None): def direct_tahoe_configs( - zkapauthz_v1_configuration=client_dummyredeemer_configurations(), + zkapauthz_v2_configuration=client_dummyredeemer_configurations(), shares=just((None, None, None)), ): """ @@ -492,7 +493,7 @@ def direct_tahoe_configs( """ config_texts = minimal_tahoe_configs( { - "privatestorageio-zkapauthz-v1": zkapauthz_v1_configuration, + NAME: zkapauthz_v2_configuration, }, shares, ) @@ -506,7 +507,7 @@ def direct_tahoe_configs( def tahoe_configs( - zkapauthz_v1_configuration=client_dummyredeemer_configurations(), + zkapauthz_v2_configuration=client_dummyredeemer_configurations(), shares=just((None, None, None)), ): """ @@ -531,7 +532,7 @@ def tahoe_configs( return set_paths - return direct_tahoe_configs(zkapauthz_v1_configuration, shares,).map( + return direct_tahoe_configs(zkapauthz_v2_configuration, shares,).map( path_setter, ) diff --git a/src/_zkapauthorizer/tests/test_client_resource.py b/src/_zkapauthorizer/tests/test_client_resource.py index 2d827a9addca8e9be652385d36fadc2bb04792aa..130294e54becd81e70c206c3dcbefd96f9303d0d 100644 --- a/src/_zkapauthorizer/tests/test_client_resource.py +++ b/src/_zkapauthorizer/tests/test_client_resource.py @@ -47,22 +47,18 @@ from testtools import TestCase from testtools.content import text_content from testtools.matchers import ( AfterPreprocessing, - AllMatch, Always, ContainsDict, Equals, GreaterThan, - HasLength, Is, IsInstance, MatchesAll, MatchesAny, - MatchesPredicate, MatchesStructure, ) from testtools.twistedsupport import CaptureTwistedLogs, succeeded from treq.testing import RequestTraversalAgent -from twisted.internet.defer import Deferred, gatherResults, maybeDeferred from twisted.internet.task import Clock, Cooperator from twisted.python.filepath import FilePath from twisted.web.client import FileBodyProducer, readBody @@ -73,6 +69,7 @@ from twisted.web.resource import IResource, getChildForRequest from .. import __version__ as zkapauthorizer_version from .._base64 import urlsafe_b64decode from .._json import dumps_utf8 +from ..api import NAME from ..configutil import config_string_from_sections from ..model import ( DoubleSpend, @@ -106,7 +103,6 @@ from .strategies import ( requests, share_parameters, tahoe_configs, - unblinded_tokens, vouchers, ) @@ -472,301 +468,6 @@ class UnblindedTokenTests(TestCase): super(UnblindedTokenTests, self).setUp() self.useFixture(CaptureTwistedLogs()) - @given( - tahoe_configs(), - api_auth_tokens(), - vouchers(), - lists(unblinded_tokens(), unique=True, min_size=1, max_size=1000), - ) - def test_post(self, get_config, api_auth_token, voucher, unblinded_tokens): - """ - When the unblinded token collection receives a **POST**, the unblinded - tokens in the request body are inserted into the system and an OK - response is generated. - """ - config = get_config_with_api_token( - self.useFixture(TempDir()), - get_config, - api_auth_token, - ) - root = root_from_config(config, datetime.now) - agent = RequestTraversalAgent(root) - data = BytesIO( - dumps_utf8( - { - "unblinded-tokens": list( - token.unblinded_token.decode("ascii") - for token in unblinded_tokens - ) - } - ) - ) - - requesting = authorized_request( - api_auth_token, - agent, - b"POST", - b"http://127.0.0.1/unblinded-token", - data=data, - ) - self.assertThat( - requesting, - succeeded( - ok_response(headers=application_json()), - ), - ) - - stored_tokens = root.controller.store.backup()["unblinded-tokens"] - - self.assertThat( - stored_tokens, - Equals( - list( - token.unblinded_token.decode("ascii") for token in unblinded_tokens - ) - ), - ) - - @given( - tahoe_configs(), - api_auth_tokens(), - vouchers(), - maybe_extra_tokens(), - ) - def test_get(self, get_config, api_auth_token, voucher, extra_tokens): - """ - When the unblinded token collection receives a **GET**, the response is - the total number of unblinded tokens in the system, the unblinded - tokens themselves, and information about tokens spent on recent lease - maintenance activity. - """ - config = get_config_with_api_token( - self.useFixture(TempDir()), - get_config, - api_auth_token, - ) - root = root_from_config(config, datetime.now) - if extra_tokens is None: - num_tokens = 0 - else: - num_tokens = root.controller.num_redemption_groups + extra_tokens - # Put in a number of tokens with which to test. - redeeming = root.controller.redeem(voucher, num_tokens) - # Make sure the operation completed before proceeding. - self.assertThat( - redeeming, - succeeded(Always()), - ) - - agent = RequestTraversalAgent(root) - requesting = authorized_request( - api_auth_token, - agent, - b"GET", - b"http://127.0.0.1/unblinded-token", - ) - self.addDetail( - "requesting result", - text_content(f"{vars(requesting.result)}"), - ) - self.assertThat( - requesting, - succeeded_with_unblinded_tokens(num_tokens, num_tokens), - ) - - @given( - tahoe_configs(), - api_auth_tokens(), - vouchers(), - maybe_extra_tokens(), - integers(min_value=0), - ) - def test_get_limit(self, get_config, api_auth_token, voucher, extra_tokens, limit): - """ - When the unblinded token collection receives a **GET** with a **limit** - query argument, it returns no more unblinded tokens than indicated by - the limit. - """ - config = get_config_with_api_token( - self.useFixture(TempDir()), - get_config, - api_auth_token, - ) - root = root_from_config(config, datetime.now) - - if extra_tokens is None: - num_tokens = 0 - else: - num_tokens = root.controller.num_redemption_groups + extra_tokens - # Put in a number of tokens with which to test. - redeeming = root.controller.redeem(voucher, num_tokens) - # Make sure the operation completed before proceeding. - self.assertThat( - redeeming, - succeeded(Always()), - ) - - agent = RequestTraversalAgent(root) - requesting = authorized_request( - api_auth_token, - agent, - b"GET", - "http://127.0.0.1/unblinded-token?limit={}".format(limit).encode("utf-8"), - ) - self.addDetail( - "requesting result", - text_content(f"{vars(requesting.result)}"), - ) - self.assertThat( - requesting, - succeeded_with_unblinded_tokens( - num_tokens, - min(num_tokens, limit), - ), - ) - - @given( - tahoe_configs(), - api_auth_tokens(), - vouchers(), - maybe_extra_tokens(), - text(max_size=64), - ) - def test_get_position( - self, get_config, api_auth_token, voucher, extra_tokens, position - ): - """ - When the unblinded token collection receives a **GET** with a **position** - query argument, it returns all unblinded tokens which sort greater - than the position and no others. - """ - config = get_config_with_api_token( - self.useFixture(TempDir()), - get_config, - api_auth_token, - ) - root = root_from_config(config, datetime.now) - - if extra_tokens is None: - num_tokens = 0 - else: - num_tokens = root.controller.num_redemption_groups + extra_tokens - # Put in a number of tokens with which to test. - redeeming = root.controller.redeem(voucher, num_tokens) - # Make sure the operation completed before proceeding. - self.assertThat( - redeeming, - succeeded(Always()), - ) - - agent = RequestTraversalAgent(root) - requesting = authorized_request( - api_auth_token, - agent, - b"GET", - "http://127.0.0.1/unblinded-token?position={}".format( - quote(position.encode("utf-8"), safe=b""), - ).encode("utf-8"), - ) - self.addDetail( - "requesting result", - text_content(f"{vars(requesting.result)}"), - ) - self.assertThat( - requesting, - succeeded_with_unblinded_tokens_with_matcher( - num_tokens, - Equals(num_tokens), - AllMatch( - MatchesAll( - GreaterThan(position), - IsInstance(str), - ), - ), - matches_lease_maintenance_spending(), - ), - ) - - @given( - tahoe_configs(), - api_auth_tokens(), - vouchers(), - integers(min_value=1, max_value=16), - integers(min_value=1, max_value=128), - ) - def test_get_order_matches_use_order( - self, get_config, api_auth_token, voucher, num_redemption_groups, extra_tokens - ): - """ - The first unblinded token returned in a response to a **GET** request is - the first token to be used to authorize a storage request. - """ - - def after(d, f): - new_d = Deferred() - - def f_and_continue(result): - maybeDeferred(f).chainDeferred(new_d) - return result - - d.addCallback(f_and_continue) - return new_d - - def get_tokens(): - d = authorized_request( - api_auth_token, - agent, - b"GET", - b"http://127.0.0.1/unblinded-token", - ) - d.addCallback(readBody) - d.addCallback( - lambda body: loads(body)["unblinded-tokens"], - ) - return d - - def use_a_token(): - root.store.discard_unblinded_tokens( - root.store.get_unblinded_tokens(1), - ) - - config = get_config_with_api_token( - self.useFixture(TempDir()), - get_config, - api_auth_token, - ) - root = root_from_config(config, datetime.now) - - root.controller.num_redemption_groups = num_redemption_groups - num_tokens = root.controller.num_redemption_groups + extra_tokens - - # Put in a number of tokens with which to test. - redeeming = root.controller.redeem(voucher, num_tokens) - # Make sure the operation completed before proceeding. - self.assertThat( - redeeming, - succeeded(Always()), - ) - - agent = RequestTraversalAgent(root) - getting_initial_tokens = get_tokens() - using_a_token = after(getting_initial_tokens, use_a_token) - getting_tokens_after = after(using_a_token, get_tokens) - - def check_tokens(before_and_after): - initial_tokens, tokens_after = before_and_after - return initial_tokens[1:] == tokens_after - - self.assertThat( - gatherResults([getting_initial_tokens, getting_tokens_after]), - succeeded( - MatchesPredicate( - check_tokens, - "initial, after (%s): initial[1:] != after", - ), - ), - ) - @given( tahoe_configs(), api_auth_tokens(), @@ -805,11 +506,11 @@ class UnblindedTokenTests(TestCase): api_auth_token, agent, b"GET", - b"http://127.0.0.1/unblinded-token", + b"http://127.0.0.1/lease-maintenance", ) d.addCallback(readBody) d.addCallback( - lambda body: loads(body)["lease-maintenance-spending"], + lambda body: loads(body)["spending"], ) self.assertThat( d, @@ -824,72 +525,10 @@ class UnblindedTokenTests(TestCase): ) -def succeeded_with_unblinded_tokens_with_matcher( - all_token_count, - match_spendable_token_count, - match_unblinded_tokens, - match_lease_maint_spending, -): - """ - :return: A matcher which matches a Deferred which fires with a response - like the one returned by the **unblinded-tokens** endpoint. - - :param int all_token_count: The expected value in the ``total`` field of - the response. - - :param match_unblinded_tokens: A matcher for the ``unblinded-tokens`` - field of the response. - - :param match_lease_maint_spending: A matcher for the - ``lease-maintenance-spending`` field of the response. - """ - return succeeded( - MatchesAll( - ok_response(headers=application_json()), - AfterPreprocessing( - json_content, - succeeded( - ContainsDict( - { - "total": Equals(all_token_count), - "spendable": match_spendable_token_count, - "unblinded-tokens": match_unblinded_tokens, - "lease-maintenance-spending": match_lease_maint_spending, - } - ), - ), - ), - ), - ) - - -def succeeded_with_unblinded_tokens(all_token_count, returned_token_count): - """ - :return: A matcher which matches a Deferred which fires with a response - like the one returned by the **unblinded-tokens** endpoint. - - :param int all_token_count: The expected value in the ``total`` field of - the response. - - :param int returned_token_count: The expected number of tokens in the - ``unblinded-tokens`` field of the response. - """ - return succeeded_with_unblinded_tokens_with_matcher( - all_token_count, - match_spendable_token_count=Equals(all_token_count), - match_unblinded_tokens=MatchesAll( - HasLength(returned_token_count), - AllMatch(IsInstance(str)), - ), - match_lease_maint_spending=matches_lease_maintenance_spending(), - ) - - def matches_lease_maintenance_spending(): """ - :return: A matcher which matches the value of the - *lease-maintenance-spending* key in the ``unblinded-tokens`` endpoint - response. + :return: A matcher which matches the value of the *spending* key in the + ``lease-maintenance`` endpoint response. """ return MatchesAny( Is(None), @@ -1071,7 +710,7 @@ class VoucherTests(TestCase): those relevant to a voucher which is actively being redeemed, about the voucher are included in a json-encoded response body. """ - count = get_token_count("privatestorageio-zkapauthz-v1", config) + count = get_token_count(NAME, config) return self._test_get_known_voucher( config, api_auth_token, @@ -1103,7 +742,7 @@ class VoucherTests(TestCase): those relevant to a voucher which has been redeemed, about the voucher are included in a json-encoded response body. """ - count = get_token_count("privatestorageio-zkapauthz-v1", config) + count = get_token_count(NAME, config) return self._test_get_known_voucher( config, api_auth_token, @@ -1136,7 +775,7 @@ class VoucherTests(TestCase): already redeemed, about the voucher are included in a json-encoded response body. """ - count = get_token_count("privatestorageio-zkapauthz-v1", config) + count = get_token_count(NAME, config) return self._test_get_known_voucher( config, api_auth_token, @@ -1168,7 +807,7 @@ class VoucherTests(TestCase): not been paid for yet, about the voucher are included in a json-encoded response body. """ - count = get_token_count("privatestorageio-zkapauthz-v1", config) + count = get_token_count(NAME, config) return self._test_get_known_voucher( config, api_auth_token, @@ -1200,7 +839,7 @@ class VoucherTests(TestCase): kind of transient conditions, about the voucher are included in a json-encoded response body. """ - count = get_token_count("privatestorageio-zkapauthz-v1", config) + count = get_token_count(NAME, config) return self._test_get_known_voucher( config, api_auth_token, @@ -1290,7 +929,7 @@ class VoucherTests(TestCase): A ``GET`` to the ``VoucherCollection`` itself returns a list of existing vouchers. """ - count = get_token_count("privatestorageio-zkapauthz-v1", config) + count = get_token_count(NAME, config) return self._test_list_vouchers( config, api_auth_token, @@ -1327,7 +966,7 @@ class VoucherTests(TestCase): A ``GET`` to the ``VoucherCollection`` itself returns a list of existing vouchers including state information that reflects transient states. """ - count = get_token_count("privatestorageio-zkapauthz-v1", config) + count = get_token_count(NAME, config) return self._test_list_vouchers( config, api_auth_token, @@ -1605,7 +1244,7 @@ class CalculatePriceTests(TestCase): lambda share_and_lease_time: tuples( just(share_and_lease_time), direct_tahoe_configs( - zkapauthz_v1_configuration=client_dummyredeemer_configurations( + zkapauthz_v2_configuration=client_dummyredeemer_configurations( min_times_remaining=just(share_and_lease_time[1]), ), shares=just(share_and_lease_time[0]), diff --git a/src/_zkapauthorizer/tests/test_model.py b/src/_zkapauthorizer/tests/test_model.py index f72c46301f03bee1aca421680b6be758e52f536a..494e0af7940f1ecca5ae5fef42044a5069cfe1bf 100644 --- a/src/_zkapauthorizer/tests/test_model.py +++ b/src/_zkapauthorizer/tests/test_model.py @@ -311,88 +311,6 @@ class VoucherStoreTests(TestCase): raises(StoreOpenError), ) - @given(tahoe_configs(), vouchers(), dummy_ristretto_keys(), datetimes(), data()) - def test_spend_order_equals_backup_order( - self, get_config, voucher_value, public_key, now, data - ): - """ - Unblinded tokens returned by ``VoucherStore.backup`` appear in the same - order as they are returned by ``VoucherStore.get_unblinded_tokens``. - """ - backed_up_tokens, spent_tokens, inserted_tokens = self._spend_order_test( - get_config, voucher_value, public_key, now, data - ) - self.assertThat( - backed_up_tokens, - Equals(spent_tokens), - ) - - @given(tahoe_configs(), vouchers(), dummy_ristretto_keys(), datetimes(), data()) - def test_spend_order_equals_insert_order( - self, get_config, voucher_value, public_key, now, data - ): - """ - Unblinded tokens returned by ``VoucherStore.get_unblinded_tokens`` - appear in the same order as they were inserted. - """ - backed_up_tokens, spent_tokens, inserted_tokens = self._spend_order_test( - get_config, voucher_value, public_key, now, data - ) - self.assertThat( - spent_tokens, - Equals(inserted_tokens), - ) - - def _spend_order_test(self, get_config, voucher_value, public_key, now, data): - """ - Insert, backup, and extract some tokens. - - :param get_config: See ``tahoe_configs`` - :param unicode voucher_value: A voucher value to associate with the tokens. - :param unicode public_key: A public key to associate with inserted unblinded tokens. - :param datetime now: A time to pretend is current. - :param data: A Hypothesis data for drawing values from strategies. - - :return: A three-tuple of (backed up tokens, extracted tokens, inserted tokens). - """ - tempdir = self.useFixture(TempDir()) - nodedir = tempdir.join("node") - - config = get_config(nodedir, "tub.port") - - # Create the underlying database file. - store = VoucherStore.from_node_config(config, lambda: now) - - # Put some tokens in it that we can backup and extract - random_tokens, unblinded_tokens = paired_tokens( - data, integers(min_value=1, max_value=5) - ) - store.add(voucher_value, len(random_tokens), 0, lambda: random_tokens) - store.insert_unblinded_tokens_for_voucher( - voucher_value, - public_key, - unblinded_tokens, - completed=data.draw(booleans()), - spendable=True, - ) - - backed_up_tokens = store.backup()["unblinded-tokens"] - extracted_tokens = [] - tokens_remaining = len(unblinded_tokens) - while tokens_remaining > 0: - to_spend = data.draw(integers(min_value=1, max_value=tokens_remaining)) - extracted_tokens.extend( - token.unblinded_token.decode("ascii") - for token in store.get_unblinded_tokens(to_spend) - ) - tokens_remaining -= to_spend - - return ( - backed_up_tokens, - extracted_tokens, - list(token.unblinded_token.decode("ascii") for token in unblinded_tokens), - ) - class UnblindedTokenStateMachine(RuleBasedStateMachine): """ @@ -534,6 +452,17 @@ class UnblindedTokenStateMachine(RuleBasedStateMachine): self.available += len(self.using) del self.using[:] + @invariant() + def unblinded_token_count(self): + """ + ``VoucherStore.count_unblinded_tokens`` returns the number of tokens + available to be spent. + """ + self.case.assertThat( + self.configless.store.count_unblinded_tokens(), + Equals(self.available), + ) + @invariant() def report_state(self): note( diff --git a/src/_zkapauthorizer/tests/test_plugin.py b/src/_zkapauthorizer/tests/test_plugin.py index d2badccfa8927ff0d4e83301f5d91671f59a1baa..f1fc566d211f985b965583ea91ede11d9046e48b 100644 --- a/src/_zkapauthorizer/tests/test_plugin.py +++ b/src/_zkapauthorizer/tests/test_plugin.py @@ -69,6 +69,7 @@ from twisted.plugins.zkapauthorizer import storage_server from .._plugin import get_root_nodes, load_signing_key from .._storage_client import IncorrectStorageServerReference +from ..api import NAME from ..controller import DummyRedeemer, IssuerConfigurationMismatch, PaymentController from ..foolscap import RIPrivacyPassAuthorizedStorageServer from ..lease_maintenance import SERVICE_NAME, LeaseMaintenanceConfig @@ -339,7 +340,7 @@ tahoe_configs_with_dummy_redeemer = tahoe_configs(client_dummyredeemer_configura tahoe_configs_with_mismatched_issuer = minimal_tahoe_configs( { - "privatestorageio-zkapauthz-v1": just( + NAME: just( {"ristretto-issuer-root-url": "https://another-issuer.example.invalid/"} ), } @@ -561,26 +562,30 @@ class ClientResourceTests(TestCase): ) -SERVERS_YAML = b""" +SERVERS_YAML = """ storage: v0-aaaaaaaa: ann: anonymous-storage-FURL: pb://@tcp:/ nickname: 10.0.0.2 storage-options: - - name: privatestorageio-zkapauthz-v1 + - name: {name} ristretto-issuer-root-url: https://payments.example.com/ storage-server-FURL: pb://bbbbbbbb@tcp:10.0.0.2:1234/cccccccc -""" +""".format( + name=NAME +).encode( + "ascii" +) -TWO_SERVERS_YAML = b""" +TWO_SERVERS_YAML = """ storage: v0-aaaaaaaa: ann: anonymous-storage-FURL: pb://@tcp:/ nickname: 10.0.0.2 storage-options: - - name: privatestorageio-zkapauthz-v1 + - name: {name} ristretto-issuer-root-url: https://payments.example.com/ storage-server-FURL: pb://bbbbbbbb@tcp:10.0.0.2:1234/cccccccc v0-dddddddd: @@ -588,10 +593,14 @@ storage: anonymous-storage-FURL: pb://@tcp:/ nickname: 10.0.0.3 storage-options: - - name: privatestorageio-zkapauthz-v1 + - name: {name} ristretto-issuer-root-url: https://payments.example.com/ storage-server-FURL: pb://eeeeeeee@tcp:10.0.0.3:1234/ffffffff -""" +""".format( + name=NAME +).encode( + "ascii" +) class LeaseMaintenanceServiceTests(TestCase): @@ -708,7 +717,7 @@ class LeaseMaintenanceServiceTests(TestCase): # Then build a function that will get us a Tahoe configuration # that includes at least that lease maintenance configuration. lambda lease_maint_config: tahoe_configs( - zkapauthz_v1_configuration=client_lease_maintenance_configurations( + zkapauthz_v2_configuration=client_lease_maintenance_configurations( just(lease_maint_config), ), ).map( diff --git a/src/_zkapauthorizer/tests/test_spending.py b/src/_zkapauthorizer/tests/test_spending.py index 06f6c6d6701f18a80f591a33dbb255b4447b87c1..3a42678128782aa8b87ddcff9deec484fbde21a7 100644 --- a/src/_zkapauthorizer/tests/test_spending.py +++ b/src/_zkapauthorizer/tests/test_spending.py @@ -76,6 +76,7 @@ class PassGroupTests(TestCase): def _test_token_group_operation( self, operation, + rest_operation, matches_tokens, voucher, num_passes, @@ -108,7 +109,10 @@ class PassGroupTests(TestCase): ) group = pass_factory.get(b"message", num_passes) spent, rest = group.split(spent_indices) + + # Perform the test-specified operations on the two groups. operation(spent) + rest_operation(rest) # Verify the expected outcome of the operation using the supplied # matcher factory. @@ -126,14 +130,14 @@ class PassGroupTests(TestCase): def matches_tokens(num_passes, group): return AfterPreprocessing( - # The use of `backup` here to check is questionable. TODO: - # Straight-up query interface for tokens in different states. - lambda store: store.backup()["unblinded-tokens"], - HasLength(num_passes - len(group.passes)), + lambda store: store.count_unblinded_tokens(), + Equals(num_passes - len(group.passes)), ) return self._test_token_group_operation( lambda group: group.mark_spent(), + # Reset the other group so its tokens are counted above. + lambda group: group.reset(), matches_tokens, voucher, num_passes, @@ -150,15 +154,16 @@ class PassGroupTests(TestCase): """ def matches_tokens(num_passes, group): + expected = num_passes - len(group.passes) return AfterPreprocessing( - # The use of `backup` here to check is questionable. TODO: - # Straight-up query interface for tokens in different states. - lambda store: store.backup()["unblinded-tokens"], - HasLength(num_passes - len(group.passes)), + lambda store: store.count_unblinded_tokens(), + Equals(expected), ) return self._test_token_group_operation( lambda group: group.mark_invalid("reason"), + # Reset the rest so we can count them in our matcher. + lambda group: group.reset(), matches_tokens, voucher, num_passes, @@ -183,6 +188,9 @@ class PassGroupTests(TestCase): return self._test_token_group_operation( lambda group: group.reset(), + # Leave the other group alone so we can see what the effect of the + # above reset was. + lambda group: None, matches_tokens, voucher, num_passes, diff --git a/src/_zkapauthorizer/tests/test_storage_client.py b/src/_zkapauthorizer/tests/test_storage_client.py index 9f26eb6ee98d9d58630ff4cb7fd6b80edb2d0cb8..7b56840cbd5f175ff96debb3836dbe51fa5f1ef1 100644 --- a/src/_zkapauthorizer/tests/test_storage_client.py +++ b/src/_zkapauthorizer/tests/test_storage_client.py @@ -38,7 +38,7 @@ from twisted.internet.defer import fail, succeed from .._storage_client import call_with_passes from .._storage_server import _ValidationResult -from ..api import MorePassesRequired +from ..api import NAME, MorePassesRequired from ..model import NotEnoughTokens from ..storage_common import ( get_configured_allowed_public_keys, @@ -108,7 +108,7 @@ shares.total = {} def test_get_configured_pass_value(self, expected): """ ``get_configured_pass_value`` reads the ``pass-value`` value from the - ``storageclient.plugins.privatestorageio-zkapauthz-v1`` section as an + ``storageclient.plugins.privatestorageio-zkapauthz-v2`` section as an integer. """ config = config_from_string( @@ -120,10 +120,10 @@ shares.needed = 3 shares.happy = 5 shares.total = 10 -[storageclient.plugins.privatestorageio-zkapauthz-v1] -pass-value={} +[storageclient.plugins.{name}] +pass-value={pass_value} """.format( - expected + name=NAME, pass_value=expected ), ) @@ -136,7 +136,7 @@ pass-value={} def test_get_configured_allowed_public_keys(self, expected): """ ``get_configured_pass_value`` reads the ``pass-value`` value from the - ``storageclient.plugins.privatestorageio-zkapauthz-v1`` section as an + ``storageclient.plugins.privatestorageio-zkapauthz-v2`` section as an integer. """ config = config_from_string( @@ -148,10 +148,11 @@ shares.needed = 3 shares.happy = 5 shares.total = 10 -[storageclient.plugins.privatestorageio-zkapauthz-v1] -allowed-public-keys = {} +[storageclient.plugins.{name}] +allowed-public-keys = {allowed_public_keys} """.format( - ",".join(expected) + name=NAME, + allowed_public_keys=",".join(expected), ), ) diff --git a/src/twisted/plugins/zkapauthorizer.py b/src/twisted/plugins/zkapauthorizer.py index 85448fe604e6c787c906a878cacd4f6524d6fbc7..ed116120a6cab7542701c2a8895e27a40d4deabc 100644 --- a/src/twisted/plugins/zkapauthorizer.py +++ b/src/twisted/plugins/zkapauthorizer.py @@ -16,6 +16,6 @@ A drop-in to supply plugins to the Twisted plugin system. """ -from _zkapauthorizer.api import ZKAPAuthorizer +from _zkapauthorizer.api import NAME, ZKAPAuthorizer -storage_server = ZKAPAuthorizer() +storage_server = ZKAPAuthorizer(name=NAME)