mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-28 08:03:36 +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