forked from indymedia/epicyon
Test for follow request
parent
31ccb80830
commit
ef3a951452
|
@ -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')
|
||||||
|
|
18
daemon.py
18
daemon.py
|
@ -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()
|
||||||
|
|
|
@ -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:
|
||||||
|
|
66
follow.py
66
follow.py
|
@ -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
|
||||||
|
|
42
inbox.py
42
inbox.py
|
@ -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)
|
||||||
|
|
39
posts.py
39
posts.py
|
@ -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
120
tests.py
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue