Test for follow request

master
Bob Mottram 2019-07-06 14:49:25 +01:00
parent 31ccb80830
commit ef3a951452
7 changed files with 226 additions and 71 deletions

View File

@ -36,8 +36,8 @@ def createAcceptReject(baseDir: str,federationList: [],capsList: [],nickname: st
newAccept['cc']=ccUrl newAccept['cc']=ccUrl
return newAccept return newAccept
def createAccept(baseDir: str,federationList: [],nickname: str,domain: str,port: int,toUrl: str,ccUrl: str,httpPrefix: str,objectUrl: str) -> {}: def createAccept(baseDir: str,federationList: [],capsList: [],nickname: str,domain: str,port: int,toUrl: str,ccUrl: str,httpPrefix: str,objectUrl: str) -> {}:
return createAcceptReject(baseDir,federationList,nickname,domain,port,toUrl,ccUrl,httpPrefix,objectUrl,'Accept') return createAcceptReject(baseDir,federationList,capsList,nickname,domain,port,toUrl,ccUrl,httpPrefix,objectUrl,'Accept')
def createReject(baseDir: str,federationList: [],nickname: str,domain: str,port: int,toUrl: str,ccUrl: str,httpPrefix: str,objectUrl: str) -> {}: def createReject(baseDir: str,federationList: [],capsList: [],nickname: str,domain: str,port: int,toUrl: str,ccUrl: str,httpPrefix: str,objectUrl: str) -> {}:
return createAcceptReject(baseDir,federationList,nickname,domain,port,toUrl,ccUrl,httpPrefix,objectUrl,'Reject') return createAcceptReject(baseDir,federationList,capsList,nickname,domain,port,toUrl,ccUrl,httpPrefix,objectUrl,'Reject')

View File

@ -147,7 +147,7 @@ class PubServer(BaseHTTPRequestHandler):
def _updateInboxQueue(self,nickname: str,messageJson: {}) -> bool: def _updateInboxQueue(self,nickname: str,messageJson: {}) -> bool:
"""Update the inbox queue """Update the inbox queue
""" """
cacheFilename = \ queueFilename = \
savePostToInboxQueue(self.server.baseDir, \ savePostToInboxQueue(self.server.baseDir, \
self.server.httpPrefix, \ self.server.httpPrefix, \
nickname, \ nickname, \
@ -155,10 +155,12 @@ class PubServer(BaseHTTPRequestHandler):
messageJson, messageJson,
self.headers['host'], self.headers['host'],
self.headers['signature'], self.headers['signature'],
'/'+self.path.split('/')[-1]) '/'+self.path.split('/')[-1],
if cacheFilename: self.server.debug)
if cacheFilename not in self.server.inboxQueue: if queueFilename:
self.server.inboxQueue.append(cacheFilename) print('**************************************')
if queueFilename not in self.server.inboxQueue:
self.server.inboxQueue.append(queueFilename)
self.send_response(201) self.send_response(201)
self.end_headers() self.end_headers()
self.server.POSTbusy=False self.server.POSTbusy=False
@ -370,6 +372,8 @@ class PubServer(BaseHTTPRequestHandler):
if self.path.endswith('/inbox') or \ if self.path.endswith('/inbox') or \
self.path=='/sharedInbox': self.path=='/sharedInbox':
if not inboxMessageHasParams(messageJson): if not inboxMessageHasParams(messageJson):
if self.server.debug:
print("DEBUG: inbox message doesn't have the required parameters")
self.send_response(403) self.send_response(403)
self.end_headers() self.end_headers()
self.server.POSTbusy=False self.server.POSTbusy=False
@ -397,7 +401,7 @@ class PubServer(BaseHTTPRequestHandler):
return return
if self.server.debug: if self.server.debug:
print('DEBUG: POST saving to inbox cache') print('DEBUG: POST saving to inbox queue')
if '/users/' in self.path: if '/users/' in self.path:
pathUsersSection=self.path.split('/users/')[1] pathUsersSection=self.path.split('/users/')[1]
if '/' not in pathUsersSection: if '/' not in pathUsersSection:
@ -452,6 +456,6 @@ def runDaemon(baseDir: str,domain: str,port=80,httpPrefix='https',fedList=[],cap
httpd.sendThreads=[] httpd.sendThreads=[]
httpd.postLog=[] httpd.postLog=[]
print('Running ActivityPub daemon on ' + domain + ' port ' + str(port)) print('Running ActivityPub daemon on ' + domain + ' port ' + str(port))
httpd.thrInboxQueue=threadWithTrace(target=runInboxQueue,args=(baseDir,httpPrefix,httpd.sendThreads,httpd.postLog,httpd.cachedWebfingers,httpd.personCache,httpd.inboxQueue,domain,port,useTor,httpd.federationList,debug),daemon=True) httpd.thrInboxQueue=threadWithTrace(target=runInboxQueue,args=(baseDir,httpPrefix,httpd.sendThreads,httpd.postLog,httpd.cachedWebfingers,httpd.personCache,httpd.inboxQueue,domain,port,useTor,httpd.federationList,httpd.capsList,debug),daemon=True)
httpd.thrInboxQueue.start() httpd.thrInboxQueue.start()
httpd.serve_forever() httpd.serve_forever()

View File

@ -40,6 +40,7 @@ from follow import unfollowPerson
from follow import unfollowerOfPerson from follow import unfollowerOfPerson
from follow import getFollowersOfPerson from follow import getFollowersOfPerson
from tests import testPostMessageBetweenServers from tests import testPostMessageBetweenServers
from tests import testFollowBetweenServers
from tests import runAllTests from tests import runAllTests
from config import setConfigParam from config import setConfigParam
from config import getConfigParam from config import getConfigParam
@ -111,7 +112,8 @@ if args.tests:
if args.testsnetwork: if args.testsnetwork:
print('Network Tests') print('Network Tests')
testPostMessageBetweenServers() testFollowBetweenServers()
#testPostMessageBetweenServers()
sys.exit() sys.exit()
if args.posts: if args.posts:

View File

@ -14,6 +14,7 @@ from person import validNickname
from utils import domainPermitted from utils import domainPermitted
from posts import sendSignedJson from posts import sendSignedJson
from capabilities import isCapable from capabilities import isCapable
from acceptreject import createAccept
def getFollowersOfPerson(baseDir: str,nickname: str,domain: str,followFile='following.txt') -> []: def getFollowersOfPerson(baseDir: str,nickname: str,domain: str,followFile='following.txt') -> []:
"""Returns a list containing the followers of the given person """Returns a list containing the followers of the given person
@ -230,58 +231,82 @@ def getFollowingFeed(baseDir: str,domain: str,port: int,path: str,httpPrefix: st
following['next']=httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page='+str(lastPage) following['next']=httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page='+str(lastPage)
return following return following
def receiveFollowRequest(session,baseDir: str,httpPrefix: str,port: int,sendThreads: [],postLog: [],cachedWebfingers: {},personCache: {},messageJson: {},federationList: []) -> bool: def receiveFollowRequest(session,baseDir: str,httpPrefix: str,port: int,sendThreads: [],postLog: [],cachedWebfingers: {},personCache: {},messageJson: {},federationList: [],capsList: [],debug : bool) -> bool:
"""Receives a follow request within the POST section of HTTPServer """Receives a follow request within the POST section of HTTPServer
""" """
if not messageJson.get('actor'):
return False
if not messageJson['type'].startswith('Follow'): if not messageJson['type'].startswith('Follow'):
return False return False
if not messageJson.get('actor'):
if debug:
print('DEBUG: follow request has no actor')
return False
if '/users/' not in messageJson['actor']: if '/users/' not in messageJson['actor']:
if debug:
print('DEBUG: "users" missing from actor')
return False return False
domain=messageJson['actor'].split('/users/')[0].replace('https://','').replace('http://','').replace('dat://','') domain=messageJson['actor'].split('/users/')[0].replace('https://','').replace('http://','').replace('dat://','')
if ':' in domain:
domain=domain.split(':')[0]
if not domainPermitted(domain,federationList): if not domainPermitted(domain,federationList):
if debug:
print('DEBUG: follower from domain not permitted - '+domain)
return False return False
nickname=messageJson['actor'].split('/users/')[1].replace('@','') nickname=messageJson['actor'].split('/users/')[1].replace('@','')
handle=nickname.lower()+'@'+domain.lower() handle=nickname.lower()+'@'+domain.lower()
if not os.path.isdir(baseDir+'/accounts/'+handle):
return False
if '/users/' not in messageJson['object']: if '/users/' not in messageJson['object']:
if debug:
print('DEBUG: "users" not found within object')
return False return False
domainToFollow=messageJson['object'].split('/users/')[0].replace('https://','').replace('http://','').replace('dat://','') domainToFollow=messageJson['object'].split('/users/')[0].replace('https://','').replace('http://','').replace('dat://','')
toPort=port
if ':' in domainToFollow:
toPort=domainToFollow.split(':')[1]
domainToFollow=domainToFollow.split(':')[0]
if not domainPermitted(domainToFollow,federationList): if not domainPermitted(domainToFollow,federationList):
if debug:
print('DEBUG: follow domain not permitted '+domainToFollow)
return False return False
nicknameToFollow=messageJson['object'].split('/users/')[1].replace('@','') nicknameToFollow=messageJson['object'].split('/users/')[1].replace('@','')
handleToFollow=nicknameToFollow.lower()+'@'+domainToFollow.lower() handleToFollow=nicknameToFollow.lower()+'@'+domainToFollow.lower()
if domainToFollow==domain: if domainToFollow==domain:
if not os.path.isdir(baseDir+'/accounts/'+handleToFollow): if not os.path.isdir(baseDir+'/accounts/'+handleToFollow):
if debug:
print('DEBUG: followed account not found - '+baseDir+'/accounts/'+handleToFollow)
return False return False
if not followerOfPerson(baseDir,nickname,domain,nicknameToFollow,domainToFollow,federationList): if not followerOfPerson(baseDir,nickname,domain,nicknameToFollow,domainToFollow,federationList):
if debug:
print('DEBUG: '+nickname+'@'+domain+' is already a follower of '+nicknameToFollow+'@'+domainToFollow)
return False return False
# send accept back # send accept back
if debug:
print('DEBUG: sending Accept from '+nickname+'@'+domain+' for follow request')
personUrl=messageJson['actor'] personUrl=messageJson['actor']
acceptJson=createAccept(baseDir,federationList,nickname,domain,port, \ acceptJson=createAccept(baseDir,federationList,capsList,nickname,domain,port, \
personUrl,'',httpPrefix,messageJson['object']) personUrl,'',httpPrefix,messageJson['object'])
clientToServer=False
sendSignedJson(acceptJson,session,baseDir,nickname,domain,port, \ sendSignedJson(acceptJson,session,baseDir,nickname,domain,port, \
nicknameToFollow,domainToFollow,toPort, '', \ nicknameToFollow,domainToFollow,toPort, '', \
httpPrefix,saveToFile,clientToServer,federationList, \ httpPrefix,True,clientToServer, \
sendThreads,postLog,cachedWebfingers,personCache) federationList, capsList, \
sendThreads,postLog,cachedWebfingers,personCache,debug)
def sendFollowRequest(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ def sendFollowRequest(session,baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \
followNickname: str,followDomain: str,followPort: bool,followHttpPrefix: str, \ followNickname: str,followDomain: str,followPort: bool,followHttpPrefix: str, \
federationList: [],capsList: []) -> {}: clientToServer: bool,federationList: [],capsList: [], \
sendThreads: [],postLog: [],cachedWebfingers: {},personCache: {},
debug : bool) -> {}:
"""Gets the json object for sending a follow request """Gets the json object for sending a follow request
""" """
if not domainPermitted(followDomain,federationList): if not domainPermitted(followDomain,federationList):
return None return None
followActor=httpPrefix+'://'+domain+'/users/'+nickname
if port!=80 and port!=443: if port!=80 and port!=443:
domain=domain+':'+str(port) followActor=httpPrefix+'://'+domain+':'+str(port)+'/users/'+nickname
requestDomain=followDomain
if followPort!=80 and followPort!=443: if followPort!=80 and followPort!=443:
followDomain=followDomain+':'+str(followPort) requestDomain=followDomain+':'+str(followPort)
followActor=httpPrefix+'://'+domain+'/users/'+nickname
# check that we are capable # check that we are capable
if capsList: if capsList:
@ -291,12 +316,13 @@ def sendFollowRequest(baseDir: str,nickname: str,domain: str,port: int,httpPrefi
newFollowJson = { newFollowJson = {
'type': 'Follow', 'type': 'Follow',
'actor': followActor, 'actor': followActor,
'object': followHttpPrefix+'://'+followDomain+'/users/'+followNickname 'object': followHttpPrefix+'://'+requestDomain+'/users/'+followNickname
} }
sendSignedJson(newFollowJson,session,baseDir,nickname,domain,port, \ sendSignedJson(newFollowJson,session,baseDir,nickname,domain,port, \
nicknameToFollow,domainToFollow,toPort, '', \ followNickname,followDomain,followPort, '', \
httpPrefix,saveToFile,clientToServer,federationList, \ httpPrefix,True,clientToServer, \
sendThreads,postLog,cachedWebfingers,personCache) federationList, capsList, \
sendThreads,postLog,cachedWebfingers,personCache, debug)
return newFollowJson return newFollowJson

View File

@ -15,6 +15,7 @@ import commentjson
from shutil import copyfile from shutil import copyfile
from utils import urlPermitted from utils import urlPermitted
from utils import createInboxQueueDir from utils import createInboxQueueDir
from utils import getStatusNumber
from httpsig import verifyPostHeaders from httpsig import verifyPostHeaders
from session import createSession from session import createSession
from session import getJson from session import getJson
@ -53,10 +54,14 @@ def getPersonPubKey(session,personUrl: str,personCache: {},debug: bool) -> str:
def inboxMessageHasParams(messageJson: {}) -> bool: def inboxMessageHasParams(messageJson: {}) -> bool:
"""Checks whether an incoming message contains expected parameters """Checks whether an incoming message contains expected parameters
""" """
expectedParams=['type','to','actor','object'] expectedParams=['type','actor','object']
for param in expectedParams: for param in expectedParams:
if not messageJson.get(param): if not messageJson.get(param):
return False return False
if not messageJson.get('to'):
allowedWithoutToParam=['Follow','Request','Capability']
if messageJson['type'] not in allowedWithoutToParam:
return False
return True return True
def inboxPermittedMessage(domain: str,messageJson: {},federationList: [],capsList: []) -> bool: def inboxPermittedMessage(domain: str,messageJson: {},federationList: [],capsList: []) -> bool:
@ -73,11 +78,12 @@ def inboxPermittedMessage(domain: str,messageJson: {},federationList: [],capsLis
if not urlPermitted(actor,federationList,capsList,"inbox:write"): if not urlPermitted(actor,federationList,capsList,"inbox:write"):
return False return False
if messageJson.get('object'): if messageJson['type']!='Follow':
if messageJson['object'].get('inReplyTo'): if messageJson.get('object'):
inReplyTo=messageJson['object']['inReplyTo'] if messageJson['object'].get('inReplyTo'):
if not urlPermitted(inReplyTo,federationList,capsList): inReplyTo=messageJson['object']['inReplyTo']
return False if not urlPermitted(inReplyTo,federationList,capsList):
return False
return True return True
@ -89,16 +95,18 @@ def validPublishedDate(published) -> bool:
return False return False
return True return True
def savePostToInboxQueue(baseDir: str,httpPrefix: str,nickname: str, domain: str,postJson: {},host: str,headers: str,postPath: str) -> str: def savePostToInboxQueue(baseDir: str,httpPrefix: str,nickname: str, domain: str,postJson: {},host: str,headers: str,postPath: str,debug: bool) -> 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 postJson.get('id'): if postJson.get('id'):
return None postId=postJson['id'].replace('/activity','')
postId=postJson['id'].replace('/activity','') else:
statusNumber,published = getStatusNumber()
postId=httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber
currTime=datetime.datetime.utcnow() currTime=datetime.datetime.utcnow()
published=currTime.strftime("%Y-%m-%dT%H:%M:%SZ") published=currTime.strftime("%Y-%m-%dT%H:%M:%SZ")
@ -107,7 +115,8 @@ def savePostToInboxQueue(baseDir: str,httpPrefix: str,nickname: str, domain: str
handle=nickname+'@'+domain handle=nickname+'@'+domain
destination=baseDir+'/accounts/'+handle+'/inbox/'+postId.replace('/','#')+'.json' destination=baseDir+'/accounts/'+handle+'/inbox/'+postId.replace('/','#')+'.json'
if os.path.isfile(destination): if os.path.isfile(destination):
# inbox item already exists if debug:
print('DEBUG: inbox item already exists')
return None return None
filename=inboxQueueDir+'/'+postId.replace('/','#')+'.json' filename=inboxQueueDir+'/'+postId.replace('/','#')+'.json'
@ -125,12 +134,16 @@ def savePostToInboxQueue(baseDir: str,httpPrefix: str,nickname: str, domain: str
'filename': filename, 'filename': filename,
'destination': destination 'destination': destination
} }
if debug:
print('Inbox queue item created')
pprint(newQueueItem)
with open(filename, 'w') as fp: with open(filename, 'w') as fp:
commentjson.dump(newQueueItem, fp, indent=4, sort_keys=False) commentjson.dump(newQueueItem, fp, indent=4, sort_keys=False)
return filename return filename
def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cachedWebfingers: {},personCache: {},queue: [],domain: str,port: int,useTor: bool,federationList: [],debug: bool) -> None: def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cachedWebfingers: {},personCache: {},queue: [],domain: str,port: int,useTor: bool,federationList: [],capsList: [],debug: bool) -> None:
"""Processes received items and moves them to """Processes received items and moves them to
the appropriate directories the appropriate directories
""" """
@ -218,7 +231,8 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache
cachedWebfingers, cachedWebfingers,
personCache, personCache,
queueJson['post'], \ queueJson['post'], \
federationList): federationList,capsList, \
debug):
if debug: if debug:
print('DEBUG: Follow accepted from '+keyId) print('DEBUG: Follow accepted from '+keyId)
os.remove(queueFilename) os.remove(queueFilename)

View File

@ -52,17 +52,21 @@ def noOfFollowersOnDomain(baseDir: str,handle: str, domain: str, followFile='fol
ctr+=1 ctr+=1
return ctr return ctr
def getPersonKey(nickname: str,domain: str,baseDir: str,keyType='public'): def getPersonKey(nickname: str,domain: str,baseDir: str,keyType='public',debug=False):
"""Returns the public or private key of a person """Returns the public or private key of a person
""" """
handle=nickname+'@'+domain handle=nickname+'@'+domain
keyFilename=baseDir+'/keys/'+keyType+'/'+handle.lower()+'.key' keyFilename=baseDir+'/keys/'+keyType+'/'+handle.lower()+'.key'
if not os.path.isfile(keyFilename): if not os.path.isfile(keyFilename):
if debug:
print('DEBUG: private key file not found: '+keyFilename)
return '' return ''
keyPem='' keyPem=''
with open(keyFilename, "r") as pemFile: with open(keyFilename, "r") as pemFile:
keyPem=pemFile.read() keyPem=pemFile.read()
if len(keyPem)<20: if len(keyPem)<20:
if debug:
print('DEBUG: private key was too short: '+keyPem)
return '' return ''
return keyPem return keyPem
@ -453,7 +457,8 @@ def createPublicPost(baseDir: str,
inReplyTo, inReplyToAtomUri, subject) inReplyTo, inReplyToAtomUri, subject)
def threadSendPost(session,postJsonObject: {},federationList: [],capsList: [],\ def threadSendPost(session,postJsonObject: {},federationList: [],capsList: [],\
inboxUrl: str, baseDir: str,signatureHeaderJson: {},postLog: []) -> None: inboxUrl: str, baseDir: str,signatureHeaderJson: {},postLog: [],
debug :bool) -> None:
"""Sends a post with exponential backoff """Sends a post with exponential backoff
""" """
tries=0 tries=0
@ -463,6 +468,8 @@ def threadSendPost(session,postJsonObject: {},federationList: [],capsList: [],\
capsList,inboxUrl,signatureHeaderJson, \ capsList,inboxUrl,signatureHeaderJson, \
"inbox:write") "inbox:write")
if postResult: if postResult:
if debug:
print('DEBUG: json post to '+inboxUrl+' succeeded')
postLog.append(postJsonObject['published']+' '+postResult+'\n') postLog.append(postJsonObject['published']+' '+postResult+'\n')
# keep the length of the log finite # keep the length of the log finite
# Don't accumulate massive files on systems with limited resources # Don't accumulate massive files on systems with limited resources
@ -475,6 +482,8 @@ def threadSendPost(session,postJsonObject: {},federationList: [],capsList: [],\
print(line, file=logFile) print(line, file=logFile)
# our work here is done # our work here is done
break break
if debug:
print('DEBUG: json post to '+inboxUrl+' failed. Waiting for '+str(backoffTime)+' seconds.')
time.sleep(backoffTime) time.sleep(backoffTime)
backoffTime *= 2 backoffTime *= 2
@ -484,13 +493,14 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \
saveToFile: bool, clientToServer: bool, \ saveToFile: bool, clientToServer: bool, \
federationList: [], capsList: [],\ federationList: [], capsList: [],\
sendThreads: [], postLog: [], cachedWebfingers: {},personCache: {}, \ sendThreads: [], postLog: [], cachedWebfingers: {},personCache: {}, \
inReplyTo=None, inReplyToAtomUri=None, subject=None) -> int: debug=False,inReplyTo=None,inReplyToAtomUri=None,subject=None) -> int:
"""Post to another inbox """Post to another inbox
""" """
withDigest=True withDigest=True
if toPort!=80 and toPort!=443: if toPort!=80 and toPort!=443:
toDomain=toDomain+':'+str(toPort) if ':' not in toDomain:
toDomain=toDomain+':'+str(toPort)
handle=httpPrefix+'://'+toDomain+'/@'+toNickname handle=httpPrefix+'://'+toDomain+'/@'+toNickname
@ -557,7 +567,8 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \
capsList, \ capsList, \
inboxUrl,baseDir, \ inboxUrl,baseDir, \
signatureHeaderJson.copy(), \ signatureHeaderJson.copy(), \
postLog),daemon=True) postLog,
debug),daemon=True)
sendThreads.append(thr) sendThreads.append(thr)
thr.start() thr.start()
return 0 return 0
@ -566,19 +577,23 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str,nickname: str, domain
toNickname: str, toDomain: str, toPort: int, cc: str, \ toNickname: str, toDomain: str, toPort: int, cc: str, \
httpPrefix: str, saveToFile: bool, clientToServer: bool, \ httpPrefix: str, saveToFile: bool, clientToServer: bool, \
federationList: [], capsList: [], \ federationList: [], capsList: [], \
sendThreads: [], postLog: [], cachedWebfingers: {},personCache: {}) -> int: sendThreads: [], postLog: [], cachedWebfingers: {},personCache: {}, \
debug: bool) -> int:
"""Sends a signed json object to an inbox/outbox """Sends a signed json object to an inbox/outbox
""" """
withDigest=True withDigest=True
if toPort!=80 and toPort!=443: if toPort!=80 and toPort!=443:
toDomain=toDomain+':'+str(toPort) if ':' not in toDomain:
toDomain=toDomain+':'+str(toPort)
handle=httpPrefix+'://'+toDomain+'/@'+toNickname handle=httpPrefix+'://'+toDomain+'/@'+toNickname
# lookup the inbox for the To handle # lookup the inbox for the To handle
wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers) wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers)
if not wfRequest: if not wfRequest:
if debug:
print('DEBUG: webfinger for '+handle+' failed')
return 1 return 1
if not clientToServer: if not clientToServer:
@ -599,6 +614,9 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str,nickname: str, domain
else: else:
if noOfFollowersOnDomain(baseDir,handle,toDomain)>1 and sharedInbox: if noOfFollowersOnDomain(baseDir,handle,toDomain)>1 and sharedInbox:
inboxUrl=sharedInbox inboxUrl=sharedInbox
if debug:
print('DEBUG: Sending to endpoint '+inboxUrl)
if not inboxUrl: if not inboxUrl:
return 3 return 3
@ -609,8 +627,10 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str,nickname: str, domain
# sharedInbox and capabilities are optional # sharedInbox and capabilities are optional
# get the senders private key # get the senders private key
privateKeyPem=getPersonKey(nickname,domain,baseDir,'private') privateKeyPem=getPersonKey(nickname,domain,baseDir,'private',debug)
if len(privateKeyPem)==0: if len(privateKeyPem)==0:
if debug:
print('DEBUG: Private key not found for '+nickname+'@'+domain+' in '+baseDir+'/keys/private')
return 6 return 6
if toDomain not in inboxUrl: if toDomain not in inboxUrl:
@ -632,7 +652,8 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str,nickname: str, domain
capsList, \ capsList, \
inboxUrl,baseDir, \ inboxUrl,baseDir, \
signatureHeaderJson.copy(), \ signatureHeaderJson.copy(), \
postLog),daemon=True) postLog,
debug),daemon=True)
sendThreads.append(thr) sendThreads.append(thr)
thr.start() thr.start()
return 0 return 0

120
tests.py
View File

@ -31,6 +31,7 @@ from follow import followerOfPerson
from follow import unfollowPerson from follow import unfollowPerson
from follow import unfollowerOfPerson from follow import unfollowerOfPerson
from follow import getFollowersOfPerson from follow import getFollowersOfPerson
from follow import sendFollowRequest
from person import createPerson from person import createPerson
from person import setPreferredNickname from person import setPreferredNickname
from person import setBio from person import setBio
@ -107,7 +108,7 @@ def testThreads():
thr.join() thr.join()
assert thr.isAlive()==False assert thr.isAlive()==False
def createServerAlice(path: str,domain: str,port: int,federationList: [],capsList: []): def createServerAlice(path: str,domain: str,port: int,federationList: [],capsList: [],hasFollows: bool,hasPosts :bool):
print('Creating test server: Alice on port '+str(port)) print('Creating test server: Alice on port '+str(port))
if os.path.isdir(path): if os.path.isdir(path):
shutil.rmtree(path) shutil.rmtree(path)
@ -121,17 +122,19 @@ def createServerAlice(path: str,domain: str,port: int,federationList: [],capsLis
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True,password) privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True,password)
deleteAllPosts(path,nickname,domain,'inbox') deleteAllPosts(path,nickname,domain,'inbox')
deleteAllPosts(path,nickname,domain,'outbox') deleteAllPosts(path,nickname,domain,'outbox')
followPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList) if hasFollows:
followerOfPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList) followPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList)
createPublicPost(path,nickname, domain, port,httpPrefix, "No wise fish would go anywhere without a porpoise", False, True, clientToServer,capsList) followerOfPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList)
createPublicPost(path,nickname, domain, port,httpPrefix, "Curiouser and curiouser!", False, True, clientToServer,capsList) if hasPosts:
createPublicPost(path,nickname, domain, port,httpPrefix, "In the gardens of memory, in the palace of dreams, that is where you and I shall meet", False, True, clientToServer,capsList) createPublicPost(path,nickname, domain, port,httpPrefix, "No wise fish would go anywhere without a porpoise", False, True, clientToServer,capsList)
createPublicPost(path,nickname, domain, port,httpPrefix, "Curiouser and curiouser!", False, True, clientToServer,capsList)
createPublicPost(path,nickname, domain, port,httpPrefix, "In the gardens of memory, in the palace of dreams, that is where you and I shall meet", False, True, clientToServer,capsList)
global testServerAliceRunning global testServerAliceRunning
testServerAliceRunning = True testServerAliceRunning = True
print('Server running: Alice') print('Server running: Alice')
runDaemon(path,domain,port,httpPrefix,federationList,capsList,useTor,True) runDaemon(path,domain,port,httpPrefix,federationList,capsList,useTor,True)
def createServerBob(path: str,domain: str,port: int,federationList: [],capsList: []): def createServerBob(path: str,domain: str,port: int,federationList: [],capsList: [],hasFollows: bool,hasPosts :bool):
print('Creating test server: Bob on port '+str(port)) print('Creating test server: Bob on port '+str(port))
if os.path.isdir(path): if os.path.isdir(path):
shutil.rmtree(path) shutil.rmtree(path)
@ -145,11 +148,13 @@ def createServerBob(path: str,domain: str,port: int,federationList: [],capsList:
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True,password) privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True,password)
deleteAllPosts(path,nickname,domain,'inbox') deleteAllPosts(path,nickname,domain,'inbox')
deleteAllPosts(path,nickname,domain,'outbox') deleteAllPosts(path,nickname,domain,'outbox')
followPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList) if hasFollows:
followerOfPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList) followPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList)
createPublicPost(path,nickname, domain, port,httpPrefix, "It's your life, live it your way.", False, True, clientToServer,capsList) followerOfPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList)
createPublicPost(path,nickname, domain, port,httpPrefix, "One of the things I've realised is that I am very simple", False, True, clientToServer,capsList) if hasPosts:
createPublicPost(path,nickname, domain, port,httpPrefix, "Quantum physics is a bit of a passion of mine", False, True, clientToServer,capsList) createPublicPost(path,nickname, domain, port,httpPrefix, "It's your life, live it your way.", False, True, clientToServer,capsList)
createPublicPost(path,nickname, domain, port,httpPrefix, "One of the things I've realised is that I am very simple", False, True, clientToServer,capsList)
createPublicPost(path,nickname, domain, port,httpPrefix, "Quantum physics is a bit of a passion of mine", False, True, clientToServer,capsList)
global testServerBobRunning global testServerBobRunning
testServerBobRunning = True testServerBobRunning = True
print('Server running: Bob') print('Server running: Bob')
@ -169,19 +174,20 @@ def testPostMessageBetweenServers():
capsList=[] capsList=[]
baseDir=os.getcwd() baseDir=os.getcwd()
if not os.path.isdir(baseDir+'/.tests'): if os.path.isdir(baseDir+'/.tests'):
os.mkdir(baseDir+'/.tests') shutil.rmtree(baseDir+'/.tests')
os.mkdir(baseDir+'/.tests')
# create the servers # create the servers
aliceDir=baseDir+'/.tests/alice' aliceDir=baseDir+'/.tests/alice'
aliceDomain='127.0.0.50' aliceDomain='127.0.0.50'
alicePort=61935 alicePort=61935
thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,capsList),daemon=True) thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,capsList,True,True),daemon=True)
bobDir=baseDir+'/.tests/bob' bobDir=baseDir+'/.tests/bob'
bobDomain='127.0.0.100' bobDomain='127.0.0.100'
bobPort=61936 bobPort=61936
thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,capsList),daemon=True) thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,capsList,True,True),daemon=True)
thrAlice.start() thrAlice.start()
thrBob.start() thrBob.start()
@ -237,6 +243,88 @@ def testPostMessageBetweenServers():
shutil.rmtree(aliceDir) shutil.rmtree(aliceDir)
shutil.rmtree(bobDir) shutil.rmtree(bobDir)
def testFollowBetweenServers():
print('Testing sending a follow request from one server to another')
global testServerAliceRunning
global testServerBobRunning
testServerAliceRunning = False
testServerBobRunning = False
httpPrefix='http'
useTor=False
federationList=['127.0.0.42','127.0.0.64']
capsList=[]
baseDir=os.getcwd()
if os.path.isdir(baseDir+'/.tests'):
shutil.rmtree(baseDir+'/.tests')
os.mkdir(baseDir+'/.tests')
# create the servers
aliceDir=baseDir+'/.tests/alice'
aliceDomain='127.0.0.42'
alicePort=61935
thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,capsList,False,False),daemon=True)
bobDir=baseDir+'/.tests/bob'
bobDomain='127.0.0.64'
bobPort=61936
thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,capsList,False,False),daemon=True)
thrAlice.start()
thrBob.start()
assert thrAlice.isAlive()==True
assert thrBob.isAlive()==True
# wait for both servers to be running
while not (testServerAliceRunning and testServerBobRunning):
time.sleep(1)
time.sleep(1)
# In the beginning all was calm and there were no follows
print('Alice sends a follow request to Bob')
os.chdir(aliceDir)
sessionAlice = createSession(aliceDomain,alicePort,useTor)
inReplyTo=None
inReplyToAtomUri=None
subject=None
aliceSendThreads = []
alicePostLog = []
followersOnly=False
saveToFile=True
clientToServer=False
ccUrl=None
alicePersonCache={}
aliceCachedWebfingers={}
aliceSendThreads=[]
alicePostLog=[]
sendResult = \
sendFollowRequest(sessionAlice,aliceDir, \
'alice',aliceDomain,alicePort,httpPrefix, \
'bob',bobDomain,bobPort,httpPrefix, \
clientToServer,federationList,capsList,
aliceSendThreads,alicePostLog, \
aliceCachedWebfingers,alicePersonCache,True)
print('sendResult: '+str(sendResult))
time.sleep(10)
# stop the servers
thrAlice.kill()
thrAlice.join()
assert thrAlice.isAlive()==False
thrBob.kill()
thrBob.join()
assert thrBob.isAlive()==False
os.chdir(baseDir)
#shutil.rmtree(baseDir+'/.tests')
def testFollowersOfPerson(): def testFollowersOfPerson():
print('testFollowersOfPerson') print('testFollowersOfPerson')
currDir=os.getcwd() currDir=os.getcwd()