tahoe-lafs/src/allmydata/web/introweb.py

150 lines
5.2 KiB
Python
Raw Normal View History

import time, os
from pkg_resources import resource_filename
from twisted.web.template import Element, XMLFile, renderElement, renderer
from twisted.python.filepath import FilePath
2019-08-14 19:35:15 +00:00
from twisted.web import static
import allmydata
2017-01-19 22:39:53 +00:00
import json
from allmydata.version_checks import get_package_versions_string
from allmydata.util import idlib
from allmydata.web.common import (
render_time,
MultiFormatResource,
SlotsSequenceElement,
)
class IntroducerRoot(MultiFormatResource):
"""
A ``Resource`` intended as the root resource for introducers.
:param _IntroducerNode introducer_node: The introducer node to template
information about.
"""
def __init__(self, introducer_node):
super(IntroducerRoot, self).__init__()
self.introducer_node = introducer_node
self.introducer_service = introducer_node.getServiceNamed("introducer")
# necessary as a root Resource
self.putChild("", self)
static_dir = resource_filename("allmydata.web", "static")
for filen in os.listdir(static_dir):
self.putChild(filen, static.File(os.path.join(static_dir, filen)))
def _create_element(self):
"""
Create a ``IntroducerRootElement`` which can be flattened into an HTML
response.
"""
return IntroducerRootElement(
self.introducer_node, self.introducer_service)
def render_HTML(self, req):
"""
Render an HTML template describing this introducer node.
"""
return renderElement(req, self._create_element())
def render_JSON(self, req):
"""
Render JSON describing this introducer node.
"""
res = {}
new introducer: signed extensible dictionary-based messages! refs #466 This introduces new client and server halves to the Introducer (renaming the old one with a _V1 suffix). Both have fallbacks to accomodate talking to a different version: the publishing client switches on whether the server's .get_version() advertises V2 support, the server switches on which subscription method was invoked by the subscribing client. The V2 protocol sends a three-tuple of (serialized announcement dictionary, signature, pubkey) for each announcement. The V2 server dispatches messages to subscribers according to the service-name, and throws errors for invalid signatures, but does not otherwise examine the messages. The V2 receiver's subscription callback will receive a (serverid, ann_dict) pair. The 'serverid' will be equal to the pubkey if all of the following are true: the originating client is V2, and was told a privkey to use the announcement went through a V2 server the signature is valid If not, 'serverid' will be equal to the tubid portion of the announced FURL, as was the case for V1 receivers. Servers will create a keypair if one does not exist yet, stored in private/server.privkey . The signed announcement dictionary puts the server FURL in a key named "anonymous-storage-FURL", which anticipates upcoming Accounting-related changes in the server advertisements. It also provides a key named "permutation-seed-base32" to tell clients what permutation seed to use. This is computed at startup, using tubid if there are existing shares, otherwise the pubkey, to retain share-order compatibility for existing servers.
2011-11-20 10:21:32 +00:00
counts = {}
for s in self.introducer_service.get_subscribers():
if s.service_name not in counts:
counts[s.service_name] = 0
counts[s.service_name] += 1
res[u"subscription_summary"] = counts
announcement_summary = {}
for ad in self.introducer_service.get_announcements():
service_name = ad.service_name
if service_name not in announcement_summary:
announcement_summary[service_name] = 0
announcement_summary[service_name] += 1
res[u"announcement_summary"] = announcement_summary
return json.dumps(res, indent=1) + b"\n"
class IntroducerRootElement(Element):
2019-08-16 16:49:39 +00:00
"""
An ``Element`` HTML template which can be flattened to describe this
introducer node.
:param _IntroducerNode introducer_node: The introducer node to describe.
:param IntroducerService introducer_service: The introducer service created
by the node.
"""
loader = XMLFile(FilePath(__file__).sibling("introducer.xhtml"))
def __init__(self, introducer_node, introducer_service):
super(IntroducerRootElement, self).__init__()
self.introducer_node = introducer_node
self.introducer_service = introducer_service
self.node_data_dict = {
"my_nodeid": idlib.nodeid_b2a(self.introducer_node.nodeid),
"version": get_package_versions_string(),
"import_path": str(allmydata).replace("/", "/ "), # XXX kludge for wrapping
"rendered_at": render_time(time.time()),
}
@renderer
def node_data(self, req, tag):
return tag.fillSlots(**self.node_data_dict)
@renderer
def announcement_summary(self, req, tag):
services = {}
for ad in self.introducer_service.get_announcements():
if ad.service_name not in services:
services[ad.service_name] = 0
services[ad.service_name] += 1
service_names = services.keys()
service_names.sort()
return u", ".join(u"{}: {}".format(service_name, services[service_name])
for service_name in service_names)
@renderer
def client_summary(self, req, tag):
new introducer: signed extensible dictionary-based messages! refs #466 This introduces new client and server halves to the Introducer (renaming the old one with a _V1 suffix). Both have fallbacks to accomodate talking to a different version: the publishing client switches on whether the server's .get_version() advertises V2 support, the server switches on which subscription method was invoked by the subscribing client. The V2 protocol sends a three-tuple of (serialized announcement dictionary, signature, pubkey) for each announcement. The V2 server dispatches messages to subscribers according to the service-name, and throws errors for invalid signatures, but does not otherwise examine the messages. The V2 receiver's subscription callback will receive a (serverid, ann_dict) pair. The 'serverid' will be equal to the pubkey if all of the following are true: the originating client is V2, and was told a privkey to use the announcement went through a V2 server the signature is valid If not, 'serverid' will be equal to the tubid portion of the announced FURL, as was the case for V1 receivers. Servers will create a keypair if one does not exist yet, stored in private/server.privkey . The signed announcement dictionary puts the server FURL in a key named "anonymous-storage-FURL", which anticipates upcoming Accounting-related changes in the server advertisements. It also provides a key named "permutation-seed-base32" to tell clients what permutation seed to use. This is computed at startup, using tubid if there are existing shares, otherwise the pubkey, to retain share-order compatibility for existing servers.
2011-11-20 10:21:32 +00:00
counts = {}
for s in self.introducer_service.get_subscribers():
if s.service_name not in counts:
counts[s.service_name] = 0
counts[s.service_name] += 1
return u", ".join(u"{}: {}".format(name, counts[name])
for name in sorted(counts.keys()))
@renderer
def services(self, req, tag):
services = self.introducer_service.get_announcements()
services.sort(key=lambda ad: (ad.service_name, ad.nickname))
services = [{
"serverid": ad.serverid,
"nickname": ad.nickname,
"connection-hints":
u"connection hints: " + u" ".join(ad.connection_hints),
"connected": u"?",
"announced": render_time(ad.when),
"version": ad.version,
"service_name": ad.service_name,
} for ad in services]
return SlotsSequenceElement(tag, services)
@renderer
def subscribers(self, req, tag):
subscribers = [{
"nickname": s.nickname,
"tubid": s.tubid,
"connected": s.remote_address,
"since": render_time(s.when),
"version": s.version,
"service_name": s.service_name,
} for s in self.introducer_service.get_subscribers()]
return SlotsSequenceElement(tag, subscribers)