forked from indymedia/epicyon
Fixing public key lookup
parent
08134954bc
commit
e12f0994cf
23
daemon.py
23
daemon.py
|
@ -16,7 +16,6 @@ from webfinger import webfingerMeta
|
||||||
from webfinger import webfingerLookup
|
from webfinger import webfingerLookup
|
||||||
from webfinger import webfingerHandle
|
from webfinger import webfingerHandle
|
||||||
from person import personLookup
|
from person import personLookup
|
||||||
from person import personKeyLookup
|
|
||||||
from person import personOutboxJson
|
from person import personOutboxJson
|
||||||
from posts import getPersonPubKey
|
from posts import getPersonPubKey
|
||||||
from posts import outboxMessageCreateWrap
|
from posts import outboxMessageCreateWrap
|
||||||
|
@ -24,6 +23,7 @@ from posts import savePostToOutbox
|
||||||
from inbox import inboxPermittedMessage
|
from inbox import inboxPermittedMessage
|
||||||
from inbox import inboxMessageHasParams
|
from inbox import inboxMessageHasParams
|
||||||
from inbox import runInboxQueue
|
from inbox import runInboxQueue
|
||||||
|
from inbox import savePostToInboxQueue
|
||||||
from follow import getFollowingFeed
|
from follow import getFollowingFeed
|
||||||
from auth import authorize
|
from auth import authorize
|
||||||
from threads import threadWithTrace
|
from threads import threadWithTrace
|
||||||
|
@ -67,10 +67,10 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.wfile.write("<html><head></head><body><h1>404 Not Found</h1></body></html>".encode('utf-8'))
|
self.wfile.write("<html><head></head><body><h1>404 Not Found</h1></body></html>".encode('utf-8'))
|
||||||
|
|
||||||
def _webfinger(self) -> bool:
|
def _webfinger(self) -> bool:
|
||||||
if self.server.debug:
|
|
||||||
print('DEBUG: WEBFINGER well-known')
|
|
||||||
if not self.path.startswith('/.well-known'):
|
if not self.path.startswith('/.well-known'):
|
||||||
return False
|
return False
|
||||||
|
if self.server.debug:
|
||||||
|
print('DEBUG: WEBFINGER well-known')
|
||||||
|
|
||||||
if self.server.debug:
|
if self.server.debug:
|
||||||
print('DEBUG: WEBFINGER host-meta')
|
print('DEBUG: WEBFINGER host-meta')
|
||||||
|
@ -225,13 +225,6 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.wfile.write(json.dumps(getPerson).encode('utf-8'))
|
self.wfile.write(json.dumps(getPerson).encode('utf-8'))
|
||||||
self.server.GETbusy=False
|
self.server.GETbusy=False
|
||||||
return
|
return
|
||||||
personKey = personKeyLookup(self.server.domain,self.path, \
|
|
||||||
self.server.baseDir)
|
|
||||||
if personKey:
|
|
||||||
self._set_headers('text/html; charset=utf-8')
|
|
||||||
self.wfile.write(personKey.encode('utf-8'))
|
|
||||||
self.server.GETbusy=False
|
|
||||||
return
|
|
||||||
# check that a json file was requested
|
# check that a json file was requested
|
||||||
if not self.path.endswith('.json'):
|
if not self.path.endswith('.json'):
|
||||||
if self.server.debug:
|
if self.server.debug:
|
||||||
|
@ -366,9 +359,10 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
pprint(messageJson)
|
pprint(messageJson)
|
||||||
|
|
||||||
if not headers.get('keyId'):
|
if not self.headers.get('signature'):
|
||||||
|
if 'keyId=' not in self.headers['signature']:
|
||||||
if self.server.debug:
|
if self.server.debug:
|
||||||
print('DEBUG: POST to inbox has no keyId in header')
|
print('DEBUG: POST to inbox has no keyId in header signature parameter')
|
||||||
self.send_response(403)
|
self.send_response(403)
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
self.server.POSTbusy=False
|
self.server.POSTbusy=False
|
||||||
|
@ -387,11 +381,10 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
cacheFilename = \
|
cacheFilename = \
|
||||||
savePostToInboxQueue(self.server.baseDir, \
|
savePostToInboxQueue(self.server.baseDir, \
|
||||||
self.server.httpPrefix, \
|
self.server.httpPrefix, \
|
||||||
headers['keyId'], \
|
|
||||||
self.postToNickname, \
|
self.postToNickname, \
|
||||||
self.server.domain, \
|
self.server.domain, \
|
||||||
messageJson,
|
messageJson,
|
||||||
self.headers)
|
self.headers['signature'])
|
||||||
if cacheFilename:
|
if cacheFilename:
|
||||||
if cacheFilename not in self.server.inboxQueue:
|
if cacheFilename not in self.server.inboxQueue:
|
||||||
self.server.inboxQueue.append(cacheFilename)
|
self.server.inboxQueue.append(cacheFilename)
|
||||||
|
@ -410,7 +403,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.POSTbusy=False
|
self.server.POSTbusy=False
|
||||||
return
|
return
|
||||||
|
|
||||||
def runDaemon(domain: str,port=80,httpPrefix='https',fedList=[],useTor=False,debug=False) -> None:
|
def runDaemon(baseDir: str,domain: str,port=80,httpPrefix='https',fedList=[],useTor=False,debug=False) -> None:
|
||||||
if len(domain)==0:
|
if len(domain)==0:
|
||||||
domain='localhost'
|
domain='localhost'
|
||||||
if '.' not in domain:
|
if '.' not in domain:
|
||||||
|
|
|
@ -133,4 +133,4 @@ if not os.path.isdir(baseDir+'/accounts/'+nickname+'@'+domain):
|
||||||
print('Creating default admin account '+nickname+'@'+domain)
|
print('Creating default admin account '+nickname+'@'+domain)
|
||||||
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,httpPrefix,True)
|
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,httpPrefix,True)
|
||||||
|
|
||||||
runDaemon(domain,port,httpPrefix,federationList,useTor,debug)
|
runDaemon(baseDir,domain,port,httpPrefix,federationList,useTor,debug)
|
||||||
|
|
|
@ -24,7 +24,7 @@ def signPostHeaders(privateKeyPem: str, nickname: str, domain: str, \
|
||||||
if port!=80 and port!=443:
|
if port!=80 and port!=443:
|
||||||
domain=domain+':'+str(port)
|
domain=domain+':'+str(port)
|
||||||
|
|
||||||
keyID = httpPrefix+'://'+domain+'/users/'+nickname+'/main-key'
|
keyID = httpPrefix+'://'+domain+'/users/'+nickname+'#main-key'
|
||||||
if not messageBodyJson:
|
if not messageBodyJson:
|
||||||
headers = {'host': domain}
|
headers = {'host': domain}
|
||||||
else:
|
else:
|
||||||
|
|
24
inbox.py
24
inbox.py
|
@ -18,6 +18,7 @@ from posts import getPersonPubKey
|
||||||
from httpsig import verifyPostHeaders
|
from httpsig import verifyPostHeaders
|
||||||
from session import createSession
|
from session import createSession
|
||||||
from follow import receiveFollowRequest
|
from follow import receiveFollowRequest
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
def inboxMessageHasParams(messageJson: {}) -> bool:
|
def inboxMessageHasParams(messageJson: {}) -> bool:
|
||||||
"""Checks whether an incoming message contains expected parameters
|
"""Checks whether an incoming message contains expected parameters
|
||||||
|
@ -58,14 +59,12 @@ def validPublishedDate(published) -> bool:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def savePostToInboxQueue(baseDir: str,httpPrefix: str,keyId: str,nickname: str, domain: str,postJson: {},headers: {}) -> str:
|
def savePostToInboxQueue(baseDir: str,httpPrefix: str,nickname: str, domain: str,postJson: {},headers: str) -> str:
|
||||||
"""Saves the give json to the inbox queue for the person
|
"""Saves the give json to the inbox queue for the person
|
||||||
keyId specifies the actor sending the post
|
keyId specifies the actor sending the post
|
||||||
"""
|
"""
|
||||||
if ':' in domain:
|
if ':' in domain:
|
||||||
domain=domain.split(':')[0]
|
domain=domain.split(':')[0]
|
||||||
if not keyId:
|
|
||||||
return None
|
|
||||||
if not postJson.get('id'):
|
if not postJson.get('id'):
|
||||||
return None
|
return None
|
||||||
postId=postJson['id'].replace('/activity','')
|
postId=postJson['id'].replace('/activity','')
|
||||||
|
@ -82,9 +81,8 @@ def savePostToInboxQueue(baseDir: str,httpPrefix: str,keyId: str,nickname: str,
|
||||||
return None
|
return None
|
||||||
filename=inboxQueueDir+'/'+postId.replace('/','#')+'.json'
|
filename=inboxQueueDir+'/'+postId.replace('/','#')+'.json'
|
||||||
|
|
||||||
newBufferItem = {
|
newQueueItem = {
|
||||||
'published': published,
|
'published': published,
|
||||||
'keyId': keyid,
|
|
||||||
'headers': headers,
|
'headers': headers,
|
||||||
'post': postJson,
|
'post': postJson,
|
||||||
'filename': filename,
|
'filename': filename,
|
||||||
|
@ -128,7 +126,21 @@ def runInboxQueue(baseDir: str,httpPrefix: str,personCache: {},queue: [],domain:
|
||||||
# Try a few times to obtain teh public key
|
# Try a few times to obtain teh public key
|
||||||
pubKey=None
|
pubKey=None
|
||||||
for tries in range(5):
|
for tries in range(5):
|
||||||
pubKey=getPersonPubKey(session,queueJson['keyId'],personCache)
|
keyId=None
|
||||||
|
signatureParams=queueJson['headers'].split(',')
|
||||||
|
for signatureItem in signatureParams:
|
||||||
|
if signatureItem.startswith('keyId='):
|
||||||
|
if '"' in signatureItem:
|
||||||
|
keyId=signatureItem.split('"')[1]
|
||||||
|
break
|
||||||
|
if not keyId:
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: No keyId in signature: '+queueJson['headers']['signature'])
|
||||||
|
os.remove(queueFilename)
|
||||||
|
queue.pop(0)
|
||||||
|
continue
|
||||||
|
|
||||||
|
pubKey=getPersonPubKey(session,keyId,personCache,debug)
|
||||||
if not pubKey:
|
if not pubKey:
|
||||||
if debug:
|
if debug:
|
||||||
print('DEBUG: Retry '+str(tries+1)+' obtaining public key for '+queueJson['keyId'])
|
print('DEBUG: Retry '+str(tries+1)+' obtaining public key for '+queueJson['keyId'])
|
||||||
|
|
29
person.py
29
person.py
|
@ -71,7 +71,7 @@ def createPerson(baseDir: str,nickname: str,domain: str,port: int, \
|
||||||
'name': nickname,
|
'name': nickname,
|
||||||
'outbox': httpPrefix+'://'+domain+'/users/'+nickname+'/outbox',
|
'outbox': httpPrefix+'://'+domain+'/users/'+nickname+'/outbox',
|
||||||
'preferredUsername': ''+nickname,
|
'preferredUsername': ''+nickname,
|
||||||
'publicKey': {'id': httpPrefix+'://'+domain+'/users/'+nickname+'/main-key',
|
'publicKey': {'id': httpPrefix+'://'+domain+'/users/'+nickname+'#main-key',
|
||||||
'owner': httpPrefix+'://'+domain+'/users/'+nickname,
|
'owner': httpPrefix+'://'+domain+'/users/'+nickname,
|
||||||
'publicKeyPem': publicKeyPem,
|
'publicKeyPem': publicKeyPem,
|
||||||
'summary': '',
|
'summary': '',
|
||||||
|
@ -119,37 +119,12 @@ def validNickname(nickname: str) -> bool:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def personKeyLookup(domain: str,path: str,baseDir: str) -> str:
|
|
||||||
"""Lookup the public key of the person with a given nickname
|
|
||||||
"""
|
|
||||||
if not path.endswith('/main-key'):
|
|
||||||
return None
|
|
||||||
if not path.startswith('/users/'):
|
|
||||||
return None
|
|
||||||
nickname=path.replace('/users/','',1).replace('/main-key','')
|
|
||||||
if not validNickname(nickname):
|
|
||||||
return None
|
|
||||||
if ':' in domain:
|
|
||||||
domain=domain.split(':')[0]
|
|
||||||
handle=nickname.lower()+'@'+domain.lower()
|
|
||||||
filename=baseDir+'/accounts/'+handle.lower()+'.json'
|
|
||||||
if not os.path.isfile(filename):
|
|
||||||
return None
|
|
||||||
personJson={"user": "unknown"}
|
|
||||||
with open(filename, 'r') as fp:
|
|
||||||
personJson=commentjson.load(fp)
|
|
||||||
if personJson.get('publicKey'):
|
|
||||||
if personJson['publicKey'].get('publicKeyPem'):
|
|
||||||
return personJson['publicKey']['publicKeyPem']
|
|
||||||
return None
|
|
||||||
|
|
||||||
def personLookup(domain: str,path: str,baseDir: str) -> {}:
|
def personLookup(domain: str,path: str,baseDir: str) -> {}:
|
||||||
"""Lookup the person for an given nickname
|
"""Lookup the person for an given nickname
|
||||||
"""
|
"""
|
||||||
notPersonLookup=['/inbox','/outbox','/outboxarchive', \
|
notPersonLookup=['/inbox','/outbox','/outboxarchive', \
|
||||||
'/followers','/following','/featured', \
|
'/followers','/following','/featured', \
|
||||||
'.png','.jpg','.gif','.mpv', \
|
'.png','.jpg','.gif','.mpv']
|
||||||
'#main-key','/main-key']
|
|
||||||
for ending in notPersonLookup:
|
for ending in notPersonLookup:
|
||||||
if path.endswith(ending):
|
if path.endswith(ending):
|
||||||
return None
|
return None
|
||||||
|
|
13
posts.py
13
posts.py
|
@ -103,18 +103,27 @@ def getPersonBox(session,wfRequest: {},personCache: {},boxName='inbox') -> (str,
|
||||||
|
|
||||||
return personJson[boxName],pubKeyId,pubKey,personId
|
return personJson[boxName],pubKeyId,pubKey,personId
|
||||||
|
|
||||||
def getPersonPubKey(session,personUrl: str,personCache: {}) -> str:
|
def getPersonPubKey(session,personUrl: str,personCache: {},debug: bool) -> str:
|
||||||
asHeader = {'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'}
|
asHeader = {'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'}
|
||||||
if not personUrl:
|
if not personUrl:
|
||||||
return None
|
return None
|
||||||
|
personUrl=personUrl.replace('#main-key','')
|
||||||
personJson = getPersonFromCache(personUrl,personCache)
|
personJson = getPersonFromCache(personUrl,personCache)
|
||||||
if not personJson:
|
if not personJson:
|
||||||
print('************Obtaining public key for '+personUrl)
|
if debug:
|
||||||
|
print('DEBUG: Obtaining public key for '+personUrl)
|
||||||
personJson = getJson(session,personUrl,asHeader,None)
|
personJson = getJson(session,personUrl,asHeader,None)
|
||||||
pubKey=None
|
pubKey=None
|
||||||
if personJson.get('publicKey'):
|
if personJson.get('publicKey'):
|
||||||
if personJson['publicKey'].get('publicKeyPem'):
|
if personJson['publicKey'].get('publicKeyPem'):
|
||||||
pubKey=personJson['publicKey']['publicKeyPem']
|
pubKey=personJson['publicKey']['publicKeyPem']
|
||||||
|
else:
|
||||||
|
if personJson.get('publicKeyPem'):
|
||||||
|
pubKey=personJson['publicKeyPem']
|
||||||
|
|
||||||
|
if not pubKey:
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: Public key not found for '+personUrl)
|
||||||
|
|
||||||
storePersonInCache(personUrl,personJson,personCache)
|
storePersonInCache(personUrl,personJson,personCache)
|
||||||
return pubKey
|
return pubKey
|
||||||
|
|
6
tests.py
6
tests.py
|
@ -111,7 +111,7 @@ def createServerAlice(path: str,domain: str,port: int,federationList: []):
|
||||||
os.mkdir(path)
|
os.mkdir(path)
|
||||||
os.chdir(path)
|
os.chdir(path)
|
||||||
nickname='alice'
|
nickname='alice'
|
||||||
httpPrefix=False
|
httpPrefix='http'
|
||||||
useTor=False
|
useTor=False
|
||||||
clientToServer=False
|
clientToServer=False
|
||||||
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True)
|
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True)
|
||||||
|
@ -124,7 +124,7 @@ def createServerAlice(path: str,domain: str,port: int,federationList: []):
|
||||||
global testServerAliceRunning
|
global testServerAliceRunning
|
||||||
testServerAliceRunning = True
|
testServerAliceRunning = True
|
||||||
print('Server running: Alice')
|
print('Server running: Alice')
|
||||||
runDaemon(domain,port,httpPrefix,federationList,useTor,True)
|
runDaemon(path,domain,port,httpPrefix,federationList,useTor,True)
|
||||||
|
|
||||||
def createServerBob(path: str,domain: str,port: int,federationList: []):
|
def createServerBob(path: str,domain: str,port: int,federationList: []):
|
||||||
print('Creating test server: Bob on port '+str(port))
|
print('Creating test server: Bob on port '+str(port))
|
||||||
|
@ -146,7 +146,7 @@ def createServerBob(path: str,domain: str,port: int,federationList: []):
|
||||||
global testServerBobRunning
|
global testServerBobRunning
|
||||||
testServerBobRunning = True
|
testServerBobRunning = True
|
||||||
print('Server running: Bob')
|
print('Server running: Bob')
|
||||||
runDaemon(domain,port,httpPrefix,federationList,useTor,True)
|
runDaemon(path,domain,port,httpPrefix,federationList,useTor,True)
|
||||||
|
|
||||||
def testPostMessageBetweenServers():
|
def testPostMessageBetweenServers():
|
||||||
print('Testing sending message from one server to the inbox of another')
|
print('Testing sending message from one server to the inbox of another')
|
||||||
|
|
Loading…
Reference in New Issue