Fixing public key lookup

master
Bob Mottram 2019-07-04 15:36:29 +01:00
parent 08134954bc
commit e12f0994cf
7 changed files with 50 additions and 61 deletions

View File

@ -16,7 +16,6 @@ from webfinger import webfingerMeta
from webfinger import webfingerLookup
from webfinger import webfingerHandle
from person import personLookup
from person import personKeyLookup
from person import personOutboxJson
from posts import getPersonPubKey
from posts import outboxMessageCreateWrap
@ -24,6 +23,7 @@ from posts import savePostToOutbox
from inbox import inboxPermittedMessage
from inbox import inboxMessageHasParams
from inbox import runInboxQueue
from inbox import savePostToInboxQueue
from follow import getFollowingFeed
from auth import authorize
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'))
def _webfinger(self) -> bool:
if self.server.debug:
print('DEBUG: WEBFINGER well-known')
if not self.path.startswith('/.well-known'):
return False
if self.server.debug:
print('DEBUG: WEBFINGER well-known')
if self.server.debug:
print('DEBUG: WEBFINGER host-meta')
@ -216,7 +216,7 @@ class PubServer(BaseHTTPRequestHandler):
self._set_headers('application/json')
self.wfile.write(json.dumps(followers).encode('utf-8'))
self.server.GETbusy=False
return
return
# look up a person
getPerson = personLookup(self.server.domain,self.path, \
self.server.baseDir)
@ -225,13 +225,6 @@ class PubServer(BaseHTTPRequestHandler):
self.wfile.write(json.dumps(getPerson).encode('utf-8'))
self.server.GETbusy=False
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
if not self.path.endswith('.json'):
if self.server.debug:
@ -366,13 +359,14 @@ class PubServer(BaseHTTPRequestHandler):
pprint(messageJson)
if not headers.get('keyId'):
if self.server.debug:
print('DEBUG: POST to inbox has no keyId in header')
self.send_response(403)
self.end_headers()
self.server.POSTbusy=False
return
if not self.headers.get('signature'):
if 'keyId=' not in self.headers['signature']:
if self.server.debug:
print('DEBUG: POST to inbox has no keyId in header signature parameter')
self.send_response(403)
self.end_headers()
self.server.POSTbusy=False
return
if self.server.debug:
print('DEBUG: POST saving to inbox cache')
@ -387,11 +381,10 @@ class PubServer(BaseHTTPRequestHandler):
cacheFilename = \
savePostToInboxQueue(self.server.baseDir, \
self.server.httpPrefix, \
headers['keyId'], \
self.postToNickname, \
self.server.domain, \
messageJson,
self.headers)
self.headers['signature'])
if cacheFilename:
if cacheFilename not in self.server.inboxQueue:
self.server.inboxQueue.append(cacheFilename)
@ -410,7 +403,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.POSTbusy=False
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:
domain='localhost'
if '.' not in domain:

View File

@ -133,4 +133,4 @@ if not os.path.isdir(baseDir+'/accounts/'+nickname+'@'+domain):
print('Creating default admin account '+nickname+'@'+domain)
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)

View File

@ -24,7 +24,7 @@ def signPostHeaders(privateKeyPem: str, nickname: str, domain: str, \
if port!=80 and port!=443:
domain=domain+':'+str(port)
keyID = httpPrefix+'://'+domain+'/users/'+nickname+'/main-key'
keyID = httpPrefix+'://'+domain+'/users/'+nickname+'#main-key'
if not messageBodyJson:
headers = {'host': domain}
else:

View File

@ -18,6 +18,7 @@ from posts import getPersonPubKey
from httpsig import verifyPostHeaders
from session import createSession
from follow import receiveFollowRequest
from pprint import pprint
def inboxMessageHasParams(messageJson: {}) -> bool:
"""Checks whether an incoming message contains expected parameters
@ -58,14 +59,12 @@ def validPublishedDate(published) -> bool:
return False
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
keyId specifies the actor sending the post
"""
if ':' in domain:
domain=domain.split(':')[0]
if not keyId:
return None
if not postJson.get('id'):
return None
postId=postJson['id'].replace('/activity','')
@ -82,9 +81,8 @@ def savePostToInboxQueue(baseDir: str,httpPrefix: str,keyId: str,nickname: str,
return None
filename=inboxQueueDir+'/'+postId.replace('/','#')+'.json'
newBufferItem = {
newQueueItem = {
'published': published,
'keyId': keyid,
'headers': headers,
'post': postJson,
'filename': filename,
@ -128,7 +126,21 @@ def runInboxQueue(baseDir: str,httpPrefix: str,personCache: {},queue: [],domain:
# Try a few times to obtain teh public key
pubKey=None
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 debug:
print('DEBUG: Retry '+str(tries+1)+' obtaining public key for '+queueJson['keyId'])

View File

@ -71,7 +71,7 @@ def createPerson(baseDir: str,nickname: str,domain: str,port: int, \
'name': nickname,
'outbox': httpPrefix+'://'+domain+'/users/'+nickname+'/outbox',
'preferredUsername': ''+nickname,
'publicKey': {'id': httpPrefix+'://'+domain+'/users/'+nickname+'/main-key',
'publicKey': {'id': httpPrefix+'://'+domain+'/users/'+nickname+'#main-key',
'owner': httpPrefix+'://'+domain+'/users/'+nickname,
'publicKeyPem': publicKeyPem,
'summary': '',
@ -118,38 +118,13 @@ def validNickname(nickname: str) -> bool:
if c in nickname:
return False
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) -> {}:
"""Lookup the person for an given nickname
"""
notPersonLookup=['/inbox','/outbox','/outboxarchive', \
'/followers','/following','/featured', \
'.png','.jpg','.gif','.mpv', \
'#main-key','/main-key']
'.png','.jpg','.gif','.mpv']
for ending in notPersonLookup:
if path.endswith(ending):
return None

View File

@ -103,18 +103,27 @@ def getPersonBox(session,wfRequest: {},personCache: {},boxName='inbox') -> (str,
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"'}
if not personUrl:
return None
personUrl=personUrl.replace('#main-key','')
personJson = getPersonFromCache(personUrl,personCache)
if not personJson:
print('************Obtaining public key for '+personUrl)
if debug:
print('DEBUG: Obtaining public key for '+personUrl)
personJson = getJson(session,personUrl,asHeader,None)
pubKey=None
if personJson.get('publicKey'):
if personJson['publicKey'].get('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)
return pubKey

View File

@ -111,7 +111,7 @@ def createServerAlice(path: str,domain: str,port: int,federationList: []):
os.mkdir(path)
os.chdir(path)
nickname='alice'
httpPrefix=False
httpPrefix='http'
useTor=False
clientToServer=False
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
testServerAliceRunning = True
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: []):
print('Creating test server: Bob on port '+str(port))
@ -146,7 +146,7 @@ def createServerBob(path: str,domain: str,port: int,federationList: []):
global testServerBobRunning
testServerBobRunning = True
print('Server running: Bob')
runDaemon(domain,port,httpPrefix,federationList,useTor,True)
runDaemon(path,domain,port,httpPrefix,federationList,useTor,True)
def testPostMessageBetweenServers():
print('Testing sending message from one server to the inbox of another')