mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-05-20 10:34:30 +00:00
Removed santiago.py.
This commit is contained in:
parent
f6b4f0cc07
commit
c8e269faaf
@ -1,308 +0,0 @@
|
|||||||
#! /usr/bin/python # -*- fill-column: 80 -*-
|
|
||||||
|
|
||||||
"""The Santiago service.
|
|
||||||
|
|
||||||
It runs on a port (the Port of Santiago) with sending and receiving services
|
|
||||||
(the Santiagi) with a simple authentication mechanism ("the Dance of the
|
|
||||||
Santiagi" or "Santiago's Dance"). The idea is that systems can share
|
|
||||||
identification information without ever revealing their location (where in the
|
|
||||||
world is Santiago?).
|
|
||||||
|
|
||||||
This *is* signed identity statements, just on a service per person level.
|
|
||||||
|
|
||||||
Santiago, he
|
|
||||||
smiles like a Buddah, 'neath
|
|
||||||
his red sombrero.
|
|
||||||
|
|
||||||
This file is distributed under the GNU Affero General Public License, Version 3
|
|
||||||
or later. A copy of GPLv3 is available [from the Free Software Foundation]
|
|
||||||
<http://www.gnu.org/licenses/gpl.html>.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# debug hacks
|
|
||||||
# ===========
|
|
||||||
|
|
||||||
DEBUG = 1
|
|
||||||
|
|
||||||
if DEBUG:
|
|
||||||
"""A few hacks to make testing easier."""
|
|
||||||
|
|
||||||
def cfg_hack():
|
|
||||||
import sys
|
|
||||||
sys.path.append("../../")
|
|
||||||
import cfg
|
|
||||||
|
|
||||||
def ohnoes():
|
|
||||||
for y in range(0, 3):
|
|
||||||
for x in range(0, 7):
|
|
||||||
print "WARNING",
|
|
||||||
print ""
|
|
||||||
print "You're in DEBUG MODE! You are surprisingly vulnerable! Raar!"
|
|
||||||
|
|
||||||
ohnoes()
|
|
||||||
cfg_hack()
|
|
||||||
|
|
||||||
|
|
||||||
# normal imports
|
|
||||||
# ==============
|
|
||||||
|
|
||||||
from collections import defaultdict as DefaultDict
|
|
||||||
import util
|
|
||||||
|
|
||||||
|
|
||||||
class Santiago(object):
|
|
||||||
"""Santiago's base class, containing listener and sender defaults."""
|
|
||||||
|
|
||||||
def __init__(self, instance):
|
|
||||||
"""Initializes the Santiago service.
|
|
||||||
|
|
||||||
instance is the PGP key this Santiago service is responsible for.
|
|
||||||
|
|
||||||
Each service contains one or more senders and listeners, primarily
|
|
||||||
divided by protocol, all pulling from and adding to the same pool of
|
|
||||||
services.
|
|
||||||
|
|
||||||
Each Santiago keeps track of the services it hosts, and other servers'
|
|
||||||
Santiago services. A Santiago has no idea of and is not responsible for
|
|
||||||
anybody else's services.
|
|
||||||
|
|
||||||
Serving-related variables:
|
|
||||||
|
|
||||||
:hosting: service to location mappings: This dictionary maps service
|
|
||||||
names to service locations.
|
|
||||||
|
|
||||||
:keys: key to service mappings: This dictionary maps keys to service
|
|
||||||
names.
|
|
||||||
|
|
||||||
Between the two, we can provide services for keys at particular
|
|
||||||
locations. These aren't necessarily services owned by this box, it
|
|
||||||
merely points the way and acts as a directory service.
|
|
||||||
|
|
||||||
Client-related variables:
|
|
||||||
|
|
||||||
:servers: This dual-key dictionary stores service: key: location
|
|
||||||
mappings, allowing for fast service-based lookup when I'm seeking
|
|
||||||
somebody to perform a specific service for me.
|
|
||||||
|
|
||||||
Both the client and server dictionaries can contain one another's data.
|
|
||||||
I'm not sure whether they should or not, yet. The data separation seems
|
|
||||||
valuable but perhaps highly over-engineered.
|
|
||||||
|
|
||||||
"""
|
|
||||||
self.instance = instance
|
|
||||||
# what I host where
|
|
||||||
self.hosting = self.load_dict("hosting")
|
|
||||||
# who I host for
|
|
||||||
self.keys = self.load_dict("keys")
|
|
||||||
# other folks
|
|
||||||
self.servers = self.load_dict("servers")
|
|
||||||
|
|
||||||
self.listeners = list()
|
|
||||||
self.senders = list()
|
|
||||||
|
|
||||||
# load settings by name
|
|
||||||
settings = self.load_dict("settings")
|
|
||||||
for key in ("socket_port", "max_hops", "proxy_list"):
|
|
||||||
setattr(self, key, settings[key] if key in settings else None)
|
|
||||||
|
|
||||||
def load_dict(self, name):
|
|
||||||
"""Loads a dictionary from file."""
|
|
||||||
|
|
||||||
# FIXME: figure out the threading issue.
|
|
||||||
#return util.filedict_con("%s_%s " % (cfg.santiago, self.instance), name)
|
|
||||||
return {
|
|
||||||
"hosting": DefaultDict(list),
|
|
||||||
"keys": DefaultDict(list),
|
|
||||||
"servers": DefaultDict(lambda: DefaultDict(list)),
|
|
||||||
"settings": DefaultDict(None)
|
|
||||||
}[name]
|
|
||||||
|
|
||||||
def am_i(self, server):
|
|
||||||
"""Hello? Is it me you're looking for?"""
|
|
||||||
|
|
||||||
return self.instance == server
|
|
||||||
|
|
||||||
# Server-related tags
|
|
||||||
# -------------------
|
|
||||||
|
|
||||||
def provide_service(self, key, service, location):
|
|
||||||
"""Serve a service for user at location.
|
|
||||||
|
|
||||||
post::
|
|
||||||
|
|
||||||
location in self.hosting[service]
|
|
||||||
service in self.keys[key]
|
|
||||||
|
|
||||||
"""
|
|
||||||
self.keys[key].append(service)
|
|
||||||
self.hosting[service].append(location)
|
|
||||||
|
|
||||||
# client-related tags
|
|
||||||
# -------------------
|
|
||||||
|
|
||||||
def learn_service(self, key, service, locations):
|
|
||||||
"""Learn a service to use, as a client.
|
|
||||||
|
|
||||||
post::
|
|
||||||
|
|
||||||
forall(locations, lambda x: x in self.servers[service][key])
|
|
||||||
|
|
||||||
"""
|
|
||||||
self.servers[service][key].extend(locations)
|
|
||||||
|
|
||||||
def consume_service(self, service, key):
|
|
||||||
return self.servers[service][key]
|
|
||||||
|
|
||||||
def add_listener(self, listener):
|
|
||||||
"""Registers a protocol-specific listener."""
|
|
||||||
|
|
||||||
self.listeners.append(listener)
|
|
||||||
|
|
||||||
def add_sender(self, sender):
|
|
||||||
"""Registers a sender."""
|
|
||||||
|
|
||||||
self.senders.append(sender)
|
|
||||||
|
|
||||||
# processing related tags
|
|
||||||
# -----------------------
|
|
||||||
|
|
||||||
def serve(self, key, service, server, hops, santiagi, listener):
|
|
||||||
"""Provide a requested service to a client."""
|
|
||||||
|
|
||||||
if santiagi is not None:
|
|
||||||
self.learn_service(key, "santiago", santiagi)
|
|
||||||
|
|
||||||
if not self.am_i(server):
|
|
||||||
self.proxy(key, service, server, hops=hops)
|
|
||||||
|
|
||||||
if service in self.keys[key]:
|
|
||||||
# TODO pick the senders more intelligently.
|
|
||||||
self.senders[0].ack(key, self.hosting[service], listener)
|
|
||||||
|
|
||||||
def proxy(self, key, service, server, hops=3):
|
|
||||||
"""Passes a Santiago request off to another known host.
|
|
||||||
|
|
||||||
We're trying to search the friend list for the target server.
|
|
||||||
|
|
||||||
"""
|
|
||||||
# handle crap input.
|
|
||||||
if (hops > self.max_hops):
|
|
||||||
hops = self.max_hops
|
|
||||||
if (hops < 1):
|
|
||||||
return
|
|
||||||
|
|
||||||
hops -= 1
|
|
||||||
|
|
||||||
# TODO pick the senders more intelligently.
|
|
||||||
return self.senders[0].proxy(key, service, server, hops)
|
|
||||||
|
|
||||||
|
|
||||||
class SantiagoListener(object):
|
|
||||||
"""Listens for requests on the santiago port."""
|
|
||||||
|
|
||||||
def __init__(self, santiago, location):
|
|
||||||
self.santiago = santiago
|
|
||||||
self.location = location
|
|
||||||
|
|
||||||
def serve(self, key, service, server, hops, santiagi):
|
|
||||||
return self.santiago.serve(key, service, server, hops, santiagi, self.location)
|
|
||||||
|
|
||||||
|
|
||||||
class SantiagoSender(object):
|
|
||||||
"""Sends the Santiago request to a Santiago service."""
|
|
||||||
|
|
||||||
def __init__(self, santiago):
|
|
||||||
self.santiago = santiago
|
|
||||||
self.messages = list()
|
|
||||||
|
|
||||||
def send(self):
|
|
||||||
"""Sends all messages on the queue."""
|
|
||||||
|
|
||||||
answer = self.messages
|
|
||||||
self.messages = list()
|
|
||||||
return answer
|
|
||||||
|
|
||||||
def request(self, destination, resource):
|
|
||||||
"""Sends a request for a resource to a known Santiago.
|
|
||||||
|
|
||||||
The request MUST include the following:
|
|
||||||
|
|
||||||
- A service.
|
|
||||||
- A server.
|
|
||||||
|
|
||||||
The request MAY include the following:
|
|
||||||
|
|
||||||
- Other Santiago listeners.
|
|
||||||
- An action.
|
|
||||||
|
|
||||||
post::
|
|
||||||
|
|
||||||
not (__return__["destination"] is None)
|
|
||||||
not (__return__["service"] is None)
|
|
||||||
# TODO my request is signed with my GPG key, recipient encrypted.
|
|
||||||
|
|
||||||
"""
|
|
||||||
pass # TODO: queue a request message.
|
|
||||||
|
|
||||||
def nak(self):
|
|
||||||
"""Denies a requested resource to a Santiago.
|
|
||||||
|
|
||||||
No reason is given. All the recipient knows is that the host did not
|
|
||||||
have that resource for that client.
|
|
||||||
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def ack(self, key, location, listener=None):
|
|
||||||
"""A successful reply to a Santiago request.
|
|
||||||
|
|
||||||
The response must include:
|
|
||||||
|
|
||||||
- A server.
|
|
||||||
|
|
||||||
The response may include:
|
|
||||||
|
|
||||||
- The Santiago listener that received and accepted the request.
|
|
||||||
|
|
||||||
"""
|
|
||||||
self.messages.append({
|
|
||||||
"to": key,
|
|
||||||
"location": location,
|
|
||||||
"reply-to": listener,})
|
|
||||||
|
|
||||||
def end(self):
|
|
||||||
"""Sent by the original requester, when it receives the server's
|
|
||||||
response, telling the server it needs to send no more responses.
|
|
||||||
|
|
||||||
Sent to the Santiago that first received the request.
|
|
||||||
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def proxy(self, key, service, server, hops):
|
|
||||||
"""Sends the request to another server."""
|
|
||||||
|
|
||||||
# TODO pull this off, another day.
|
|
||||||
return ("%(key)s is requesting the %(service)s from %(server)s. " +
|
|
||||||
self.santiago.instance + " is not %(server)s. " +
|
|
||||||
"proxying request. %(hops)d hops remain.") % locals()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import cherrypy
|
|
||||||
import sys
|
|
||||||
sys.path.append(".")
|
|
||||||
from protocols.http import SantiagoHttpListener, SantiagoHttpSender
|
|
||||||
|
|
||||||
# build the Santiago
|
|
||||||
santiago = Santiago("nick")
|
|
||||||
http_listener = SantiagoHttpListener(santiago)
|
|
||||||
http_sender = SantiagoHttpSender(santiago)
|
|
||||||
santiago.add_listener(http_listener)
|
|
||||||
santiago.add_sender(http_sender)
|
|
||||||
|
|
||||||
# TODO move this into the table loading.
|
|
||||||
santiago.provide_service("james", "wiki", "192.168.0.13")
|
|
||||||
|
|
||||||
cherrypy.quickstart(http_listener)
|
|
||||||
Loading…
x
Reference in New Issue
Block a user