Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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"]