Updating of capabilities

master
Bob Mottram 2019-07-09 15:20:23 +01:00
parent 1b10d7ef4b
commit 76e8167ce7
12 changed files with 346 additions and 88 deletions

View File

@ -18,7 +18,7 @@ from utils import getNicknameFromActor
from utils import domainPermitted from utils import domainPermitted
from utils import followPerson from utils import followPerson
def createAcceptReject(baseDir: str,federationList: [],ocapGranted: {}, \ def createAcceptReject(baseDir: str,federationList: [], \
nickname: str,domain: str,port: int, \ nickname: str,domain: str,port: int, \
toUrl: str,ccUrl: str,httpPrefix: str, \ toUrl: str,ccUrl: str,httpPrefix: str, \
objectJson: {},ocapJson,acceptType: str) -> {}: objectJson: {},ocapJson,acceptType: str) -> {}:
@ -31,7 +31,7 @@ def createAcceptReject(baseDir: str,federationList: [],ocapGranted: {}, \
if not objectJson.get('actor'): if not objectJson.get('actor'):
return None return None
if not urlPermitted(objectJson['actor'],federationList,ocapGranted,"inbox:write"): if not urlPermitted(objectJson['actor'],federationList,"inbox:write"):
return None return None
if port!=80 and port!=443: if port!=80 and port!=443:
@ -52,28 +52,28 @@ def createAcceptReject(baseDir: str,federationList: [],ocapGranted: {}, \
newAccept['capabilities']=ocapJson newAccept['capabilities']=ocapJson
return newAccept return newAccept
def createAccept(baseDir: str,federationList: [],ocapGranted: {}, \ def createAccept(baseDir: str,federationList: [], \
nickname: str,domain: str,port: int, \ nickname: str,domain: str,port: int, \
toUrl: str,ccUrl: str,httpPrefix: str, \ toUrl: str,ccUrl: str,httpPrefix: str, \
objectJson: {}) -> {}: objectJson: {}) -> {}:
# create capabilities accept # create capabilities accept
ocapNew=capabilitiesAccept(baseDir,httpPrefix,nickname,domain,port,toUrl,True) ocapNew=capabilitiesAccept(baseDir,httpPrefix,nickname,domain,port,toUrl,True)
return createAcceptReject(baseDir,federationList,ocapGranted, \ return createAcceptReject(baseDir,federationList, \
nickname,domain,port, \ nickname,domain,port, \
toUrl,ccUrl,httpPrefix, \ toUrl,ccUrl,httpPrefix, \
objectJson,ocapNew,'Accept') objectJson,ocapNew,'Accept')
def createReject(baseDir: str,federationList: [],ocapGranted: {}, \ def createReject(baseDir: str,federationList: [], \
nickname: str,domain: str,port: int, \ nickname: str,domain: str,port: int, \
toUrl: str,ccUrl: str,httpPrefix: str, \ toUrl: str,ccUrl: str,httpPrefix: str, \
objectJson: {}) -> {}: objectJson: {}) -> {}:
return createAcceptReject(baseDir,federationList,ocapGranted, \ return createAcceptReject(baseDir,federationList, \
nickname,domain,port, \ nickname,domain,port, \
toUrl,ccUrl, \ toUrl,ccUrl, \
httpPrefix,objectJson,None,'Reject') httpPrefix,objectJson,None,'Reject')
def acceptFollow(baseDir: str,domain : str,messageJson: {}, \ def acceptFollow(baseDir: str,domain : str,messageJson: {}, \
federationList: [],ocapGranted: {},debug : bool) -> None: federationList: [],debug : bool) -> None:
if not messageJson.get('object'): if not messageJson.get('object'):
return return
if not messageJson['object'].get('type'): if not messageJson['object'].get('type'):
@ -161,7 +161,7 @@ def receiveAcceptReject(session,baseDir: str, \
httpPrefix: str,domain :str,port: int, \ httpPrefix: str,domain :str,port: int, \
sendThreads: [],postLog: [],cachedWebfingers: {}, \ sendThreads: [],postLog: [],cachedWebfingers: {}, \
personCache: {},messageJson: {},federationList: [], \ personCache: {},messageJson: {},federationList: [], \
ocapGranted: {},debug : bool) -> bool: debug : bool) -> bool:
"""Receives an Accept or Reject within the POST section of HTTPServer """Receives an Accept or Reject within the POST section of HTTPServer
""" """
if messageJson['type']!='Accept' and messageJson['type']!='Reject': if messageJson['type']!='Accept' and messageJson['type']!='Reject':
@ -185,7 +185,7 @@ def receiveAcceptReject(session,baseDir: str, \
print('DEBUG: '+messageJson['type']+' does not contain a nickname') print('DEBUG: '+messageJson['type']+' does not contain a nickname')
return False return False
handle=nickname.lower()+'@'+domain.lower() handle=nickname.lower()+'@'+domain.lower()
acceptFollow(baseDir,domain,messageJson,federationList,ocapGranted,debug) acceptFollow(baseDir,domain,messageJson,federationList,debug)
if debug: if debug:
print('DEBUG: Uh, '+messageJson['type']+', I guess') print('DEBUG: Uh, '+messageJson['type']+', I guess')
return True return True

View File

@ -12,7 +12,7 @@ from utils import getStatusNumber
from utils import createOutboxDir from utils import createOutboxDir
from utils import urlPermitted from utils import urlPermitted
def createAnnounce(baseDir: str,federationList: [], ocapGranted: {}, \ def createAnnounce(baseDir: str,federationList: [], \
nickname: str, domain: str, port: int, \ nickname: str, domain: str, port: int, \
toUrl: str, ccUrl: str, httpPrefix: str, \ toUrl: str, ccUrl: str, httpPrefix: str, \
objectUrl: str, saveToFile: bool) -> {}: objectUrl: str, saveToFile: bool) -> {}:
@ -22,7 +22,7 @@ def createAnnounce(baseDir: str,federationList: [], ocapGranted: {}, \
followers url objectUrl is typically the url of the message, followers url objectUrl is typically the url of the message,
corresponding to url or atomUri in createPostBase corresponding to url or atomUri in createPostBase
""" """
if not urlPermitted(objectUrl,federationList,ocapGranted,"inbox:write"): if not urlPermitted(objectUrl,federationList,"inbox:write"):
return None return None
if port!=80 and port!=443: if port!=80 and port!=443:

View File

@ -16,6 +16,9 @@ from utils import getNicknameFromActor
from utils import getDomainFromActor from utils import getDomainFromActor
def getOcapFilename(baseDir :str,nickname: str,domain: str,actor :str,subdir: str) -> str: def getOcapFilename(baseDir :str,nickname: str,domain: str,actor :str,subdir: str) -> str:
"""Returns the filename for a particular capability accepted or granted
Also creates directories as needed
"""
if ':' in domain: if ':' in domain:
domain=domain.split(':')[0] domain=domain.split(':')[0]
@ -151,3 +154,84 @@ def capabilitiesGrantedSave(baseDir :str,nickname :str,domain :str,ocap: {}) ->
with open(ocapFilename, 'w') as fp: with open(ocapFilename, 'w') as fp:
commentjson.dump(ocap, fp, indent=4, sort_keys=False) commentjson.dump(ocap, fp, indent=4, sort_keys=False)
return True return True
def capabilitiesUpdate(baseDir: str,httpPrefix: str, \
nickname: str,domain: str, port: int, \
updateActor: str, \
updateCaps: []) -> {}:
"""Used to sends an update for a change of object capabilities
Note that the capability id gets changed with a new random token
so that the old capabilities can't continue to be used
"""
# reject excessively long actors
if len(updateActor)>256:
return None
fullDomain=domain
if port!=80 and port !=443:
fullDomain=domain+':'+str(port)
# Get the filename of the capability
ocapFilename=getOcapFilename(baseDir,nickname,fullDomain,updateActor,'accept')
# The capability should already exist for it to be updated
if not os.path.isfile(ocapFilename):
return None
# create an update activity
ocapUpdate = {
'type': 'Update',
'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname,
'to': [updateActor],
'cc': [],
'object': {}
}
# read the existing capability
with open(ocapFilename, 'r') as fp:
ocapJson=commentjson.load(fp)
# set the new capabilities list. eg. ["inbox:write","objects:read"]
ocapJson['capability']=updateCaps
# change the id, so that the old capabilities can't continue to be used
updateActorNickname=getNicknameFromActor(updateActor)
updateActorDomain,updateActorPort=getDomainFromActor(updateActor)
if updateActorPort:
ocapId=updateActorNickname+'@'+updateActorDomain+':'+str(updateActorPort)+'#'+createPassword(32)
else:
ocapId=updateActorNickname+'@'+updateActorDomain+'#'+createPassword(32)
ocapJson['id']=httpPrefix+"://"+fullDomain+"/caps/"+ocapId
ocapUpdate['object']=ocapJson
# save it again
with open(ocapFilename, 'w') as fp:
commentjson.dump(ocapJson, fp, indent=4, sort_keys=False)
return ocapUpdate
def capabilitiesReceiveUpdate(baseDir :str, \
nickname :str,domain :str,port :int, \
actor :str, \
newCapabilitiesId :str, \
capabilityList :[], debug :bool) -> bool:
"""An update for a capability or the given actor has arrived
"""
ocapFilename= \
getOcapFilename(baseDir,nickname,domain,actor,'granted')
if not os.path.isfile(ocapFilename):
if debug:
print('DEBUG: capabilities file not found during update')
print(ocapFilename)
return False
with open(ocapFilename, 'r') as fp:
ocapJson=commentjson.load(fp)
ocapJson['id']=newCapabilitiesId
ocapJson['capability']=capabilityList
with open(ocapFilename, 'w') as fp:
commentjson.dump(ocapJson, fp, indent=4, sort_keys=False)
return True
return False

View File

@ -450,8 +450,7 @@ class PubServer(BaseHTTPRequestHandler):
if not inboxPermittedMessage(self.server.domain, \ if not inboxPermittedMessage(self.server.domain, \
messageJson, \ messageJson, \
self.server.federationList, \ self.server.federationList):
self.server.ocapGranted):
if self.server.debug: if self.server.debug:
# https://www.youtube.com/watch?v=K3PrSj9XEu4 # https://www.youtube.com/watch?v=K3PrSj9XEu4
print('DEBUG: Ah Ah Ah') print('DEBUG: Ah Ah Ah')
@ -498,7 +497,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.POSTbusy=False self.server.POSTbusy=False
def runDaemon(baseDir: str,domain: str,port=80,httpPrefix='https', \ def runDaemon(baseDir: str,domain: str,port=80,httpPrefix='https', \
fedList=[],ocapAlways=False,ocapGranted={}, \ fedList=[],ocapAlways=False, \
useTor=False,debug=False) -> None: useTor=False,debug=False) -> None:
if len(domain)==0: if len(domain)==0:
domain='localhost' domain='localhost'
@ -514,7 +513,6 @@ def runDaemon(baseDir: str,domain: str,port=80,httpPrefix='https', \
httpd.httpPrefix=httpPrefix httpd.httpPrefix=httpPrefix
httpd.debug=debug httpd.debug=debug
httpd.federationList=fedList.copy() httpd.federationList=fedList.copy()
httpd.ocapGranted=ocapGranted.copy()
httpd.baseDir=baseDir httpd.baseDir=baseDir
httpd.personCache={} httpd.personCache={}
httpd.cachedWebfingers={} httpd.cachedWebfingers={}
@ -538,6 +536,6 @@ def runDaemon(baseDir: str,domain: str,port=80,httpPrefix='https', \
httpd.personCache,httpd.inboxQueue, \ httpd.personCache,httpd.inboxQueue, \
domain,port,useTor,httpd.federationList, \ domain,port,useTor,httpd.federationList, \
httpd.ocapAlways, \ httpd.ocapAlways, \
httpd.ocapGranted,debug),daemon=True) debug),daemon=True)
httpd.thrInboxQueue.start() httpd.thrInboxQueue.start()
httpd.serve_forever() httpd.serve_forever()

View File

@ -320,8 +320,6 @@ else:
if configFederationList: if configFederationList:
federationList=configFederationList federationList=configFederationList
ocapGranted={}
if federationList: if federationList:
print('Federating with: '+str(federationList)) print('Federating with: '+str(federationList))
@ -348,13 +346,13 @@ if args.testdata:
deleteAllPosts(baseDir,nickname,domain,'outbox') deleteAllPosts(baseDir,nickname,domain,'outbox')
followPerson(baseDir,nickname,domain,'admin',domain,federationList,True) followPerson(baseDir,nickname,domain,'admin',domain,federationList,True)
followerOfPerson(baseDir,nickname,domain,'admin',domain,federationList,True) followerOfPerson(baseDir,nickname,domain,'admin',domain,federationList,True)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"like, this is totally just a test, man",False,True,False,ocapGranted) createPublicPost(baseDir,nickname,domain,port,httpPrefix,"like, this is totally just a test, man",False,True,False)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Zoiks!!!",False,True,False,ocapGranted) createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Zoiks!!!",False,True,False)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Hey scoob we need like a hundred more milkshakes",False,True,False,ocapGranted) createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Hey scoob we need like a hundred more milkshakes",False,True,False)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Getting kinda spooky around here",False,True,False,ocapGranted) createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Getting kinda spooky around here",False,True,False)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"And they would have gotten away with it too if it wasn't for those pesky hackers",False,True,False,ocapGranted) createPublicPost(baseDir,nickname,domain,port,httpPrefix,"And they would have gotten away with it too if it wasn't for those pesky hackers",False,True,False)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"man, these centralized sites are, like, the worst!",False,True,False,ocapGranted) createPublicPost(baseDir,nickname,domain,port,httpPrefix,"man, these centralized sites are, like, the worst!",False,True,False)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"another mystery solved hey",False,True,False,ocapGranted) createPublicPost(baseDir,nickname,domain,port,httpPrefix,"another mystery solved hey",False,True,False)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"let's go bowling",False,True,False,ocapGranted) createPublicPost(baseDir,nickname,domain,port,httpPrefix,"let's go bowling",False,True,False)
runDaemon(baseDir,domain,port,httpPrefix,federationList,ocapAlways,ocapGranted,useTor,debug) runDaemon(baseDir,domain,port,httpPrefix,federationList,ocapAlways,useTor,debug)

View File

@ -221,7 +221,7 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \
port: int,sendThreads: [],postLog: [], \ port: int,sendThreads: [],postLog: [], \
cachedWebfingers: {},personCache: {}, \ cachedWebfingers: {},personCache: {}, \
messageJson: {},federationList: [], \ messageJson: {},federationList: [], \
ocapGranted: {},debug : bool) -> bool: 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['type'].startswith('Follow'): if not messageJson['type'].startswith('Follow'):
@ -281,7 +281,7 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \
print('DEBUG: sending Accept for follow request which arrived at '+ \ print('DEBUG: sending Accept for follow request which arrived at '+ \
nicknameToFollow+'@'+domainToFollow+' back to '+nickname+'@'+domain) nicknameToFollow+'@'+domainToFollow+' back to '+nickname+'@'+domain)
personUrl=messageJson['actor'] personUrl=messageJson['actor']
acceptJson=createAccept(baseDir,federationList,ocapGranted, \ acceptJson=createAccept(baseDir,federationList, \
nicknameToFollow,domainToFollow,port, \ nicknameToFollow,domainToFollow,port, \
personUrl,'',httpPrefix,messageJson) personUrl,'',httpPrefix,messageJson)
if debug: if debug:
@ -295,7 +295,7 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \
nicknameToFollow,domainToFollow,port, \ nicknameToFollow,domainToFollow,port, \
nickname,domain,fromPort, '', \ nickname,domain,fromPort, '', \
httpPrefix,True,clientToServer, \ httpPrefix,True,clientToServer, \
federationList, ocapGranted, \ federationList, \
sendThreads,postLog,cachedWebfingers, \ sendThreads,postLog,cachedWebfingers, \
personCache,debug) personCache,debug)
@ -303,7 +303,7 @@ def sendFollowRequest(session,baseDir: str, \
nickname: str,domain: str,port: int,httpPrefix: str, \ nickname: str,domain: str,port: int,httpPrefix: str, \
followNickname: str,followDomain: str, \ followNickname: str,followDomain: str, \
followPort: bool,followHttpPrefix: str, \ followPort: bool,followHttpPrefix: str, \
clientToServer: bool,federationList: [],ocapGranted: {}, \ clientToServer: bool,federationList: [], \
sendThreads: [],postLog: [],cachedWebfingers: {}, \ sendThreads: [],postLog: [],cachedWebfingers: {}, \
personCache: {},debug : bool) -> {}: personCache: {},debug : bool) -> {}:
"""Gets the json object for sending a follow request """Gets the json object for sending a follow request
@ -339,7 +339,7 @@ def sendFollowRequest(session,baseDir: str, \
followNickname,followDomain,followPort, \ followNickname,followDomain,followPort, \
'https://www.w3.org/ns/activitystreams#Public', \ 'https://www.w3.org/ns/activitystreams#Public', \
httpPrefix,True,clientToServer, \ httpPrefix,True,clientToServer, \
federationList, ocapGranted, \ federationList, \
sendThreads,postLog,cachedWebfingers,personCache, debug) sendThreads,postLog,cachedWebfingers,personCache, debug)
return newFollowJson return newFollowJson

View File

@ -16,6 +16,9 @@ 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 utils import getStatusNumber
from utils import getDomainFromActor
from utils import getNicknameFromActor
from utils import domainPermitted
from httpsig import verifyPostHeaders from httpsig import verifyPostHeaders
from session import createSession from session import createSession
from session import getJson from session import getJson
@ -27,6 +30,7 @@ from cache import storePersonInCache
from acceptreject import receiveAcceptReject from acceptreject import receiveAcceptReject
from capabilities import getOcapFilename from capabilities import getOcapFilename
from capabilities import CapablePost from capabilities import CapablePost
from capabilities import capabilitiesReceiveUpdate
def getPersonPubKey(session,personUrl: str,personCache: {},debug: bool) -> str: def getPersonPubKey(session,personUrl: str,personCache: {},debug: bool) -> str:
if not personUrl: if not personUrl:
@ -68,7 +72,7 @@ def inboxMessageHasParams(messageJson: {}) -> bool:
return False return False
return True return True
def inboxPermittedMessage(domain: str,messageJson: {},federationList: [],ocapGranted: {}) -> bool: def inboxPermittedMessage(domain: str,messageJson: {},federationList: []) -> bool:
""" check that we are receiving from a permitted domain """ check that we are receiving from a permitted domain
""" """
testParam='actor' testParam='actor'
@ -79,14 +83,14 @@ def inboxPermittedMessage(domain: str,messageJson: {},federationList: [],ocapGra
if domain in actor: if domain in actor:
return True return True
if not urlPermitted(actor,federationList,ocapGranted,"inbox:write"): if not urlPermitted(actor,federationList,"inbox:write"):
return False return False
if messageJson['type']!='Follow': if messageJson['type']!='Follow':
if messageJson.get('object'): if messageJson.get('object'):
if messageJson['object'].get('inReplyTo'): if messageJson['object'].get('inReplyTo'):
inReplyTo=messageJson['object']['inReplyTo'] inReplyTo=messageJson['object']['inReplyTo']
if not urlPermitted(inReplyTo,federationList,ocapGranted): if not urlPermitted(inReplyTo,federationList):
return False return False
return True return True
@ -299,7 +303,62 @@ def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain :
return recipientsDict return recipientsDict
def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cachedWebfingers: {},personCache: {},queue: [],domain: str,port: int,useTor: bool,federationList: [],ocapAlways: bool,ocapGranted: {},debug: bool) -> None: def receiveUpdate(session,baseDir: str, \
httpPrefix: str,domain :str,port: int, \
sendThreads: [],postLog: [],cachedWebfingers: {}, \
personCache: {},messageJson: {},federationList: [], \
debug : bool) -> bool:
"""Receives an Update activity within the POST section of HTTPServer
"""
if messageJson['type']!='Update':
return False
if not messageJson.get('actor'):
if debug:
print('DEBUG: '+messageJson['type']+' has no actor')
return False
if not messageJson.get('object'):
if debug:
print('DEBUG: '+messageJson['type']+' has no object')
return False
if not isinstance(messageJson['object'], dict):
if debug:
print('DEBUG: '+messageJson['type']+' object is not a dict')
return False
if not messageJson['object'].get('type'):
if debug:
print('DEBUG: '+messageJson['type']+' object has no type')
return False
if '/users/' not in messageJson['actor']:
if debug:
print('DEBUG: "users" missing from actor in '+messageJson['type'])
return False
domain,tempPort=getDomainFromActor(messageJson['actor'])
if not domainPermitted(domain,federationList):
if debug:
print('DEBUG: '+messageJson['type']+' from domain not permitted - '+domain)
return False
nickname=getNicknameFromActor(messageJson['actor'])
if not nickname:
if debug:
print('DEBUG: '+messageJson['type']+' does not contain a nickname')
return False
handle=nickname.lower()+'@'+domain.lower()
if messageJson['object'].get('capability') and messageJson['object'].get('scope'):
domain,tempPort=getDomainFromActor(messageJson['object']['scope'])
nickname=getNicknameFromActor(messageJson['object']['scope'])
if messageJson['object']['type']=='Capability':
if capabilitiesReceiveUpdate(baseDir,nickname,domain,port,
messageJson['actor'], \
messageJson['object']['id'], \
messageJson['object']['capability'], \
debug):
if debug:
print('DEBUG: An update was received')
return True
return False
def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cachedWebfingers: {},personCache: {},queue: [],domain: str,port: int,useTor: bool,federationList: [],ocapAlways: bool,debug: bool) -> None:
"""Processes received items and moves them to """Processes received items and moves them to
the appropriate directories the appropriate directories
""" """
@ -389,7 +448,7 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache
cachedWebfingers, cachedWebfingers,
personCache, personCache,
queueJson['post'], \ queueJson['post'], \
federationList,ocapGranted, \ federationList, \
debug): debug):
if debug: if debug:
print('DEBUG: Follow accepted from '+keyId) print('DEBUG: Follow accepted from '+keyId)
@ -403,7 +462,7 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache
cachedWebfingers, cachedWebfingers,
personCache, personCache,
queueJson['post'], \ queueJson['post'], \
federationList,ocapGranted, \ federationList, \
debug): debug):
if debug: if debug:
print('DEBUG: Accept/Reject received from '+keyId) print('DEBUG: Accept/Reject received from '+keyId)
@ -411,6 +470,21 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache
queue.pop(0) queue.pop(0)
continue continue
if receiveUpdate(session, \
baseDir,httpPrefix, \
domain,port, \
sendThreads,postLog, \
cachedWebfingers,
personCache,
queueJson['post'], \
federationList, \
debug):
if debug:
print('DEBUG: Update accepted from '+keyId)
os.remove(queueFilename)
queue.pop(0)
continue
# get recipients list # get recipients list
recipientsDict=inboxPostRecipients(baseDir,queueJson['post'],httpPrefix,domain,port) recipientsDict=inboxPostRecipients(baseDir,queueJson['post'],httpPrefix,domain,port)

View File

@ -17,7 +17,7 @@ def like(baseDir: str,federationList: [],nickname: str,domain: str,port: int, \
and ccUrl might be a specific person whose post was liked and ccUrl might be a specific person whose post was liked
objectUrl is typically the url of the message, corresponding to url or atomUri in createPostBase objectUrl is typically the url of the message, corresponding to url or atomUri in createPostBase
""" """
if not urlPermitted(objectUrl,federationList,ocapGranted,"inbox:write"): if not urlPermitted(objectUrl,federationList,"inbox:write"):
return None return None
if port!=80 and port!=443: if port!=80 and port!=443:

View File

@ -30,7 +30,10 @@ from httpsig import createSignedHeader
from utils import getStatusNumber from utils import getStatusNumber
from utils import createPersonDir from utils import createPersonDir
from utils import urlPermitted from utils import urlPermitted
from utils import getNicknameFromActor
from utils import getDomainFromActor
from capabilities import getOcapFilename from capabilities import getOcapFilename
from capabilities import capabilitiesUpdate
try: try:
from BeautifulSoup import BeautifulSoup from BeautifulSoup import BeautifulSoup
except ImportError: except ImportError:
@ -153,7 +156,7 @@ def getPersonBox(session,wfRequest: {},personCache: {}, \
def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \
maxEmoji: int,maxAttachments: int, \ maxEmoji: int,maxAttachments: int, \
federationList: [], ocapGranted: {},\ federationList: [],\
personCache: {},raw: bool,simple: bool) -> {}: personCache: {},raw: bool,simple: bool) -> {}:
personPosts={} personPosts={}
if not outboxUrl: if not outboxUrl:
@ -193,7 +196,7 @@ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \
if tagItem['icon'].get('url'): if tagItem['icon'].get('url'):
# No emoji from non-permitted domains # No emoji from non-permitted domains
if urlPermitted(tagItem['icon']['url'], \ if urlPermitted(tagItem['icon']['url'], \
federationList,ocapGranted, \ federationList, \
"objects:read"): "objects:read"):
emojiName=tagItem['name'] emojiName=tagItem['name']
emojiIcon=tagItem['icon']['url'] emojiIcon=tagItem['icon']['url']
@ -217,7 +220,7 @@ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \
if item['object']['inReplyTo']: if item['object']['inReplyTo']:
# No replies to non-permitted domains # No replies to non-permitted domains
if not urlPermitted(item['object']['inReplyTo'], \ if not urlPermitted(item['object']['inReplyTo'], \
federationList,ocapGranted, \ federationList, \
"objects:read"): "objects:read"):
continue continue
inReplyTo = item['object']['inReplyTo'] inReplyTo = item['object']['inReplyTo']
@ -227,7 +230,7 @@ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \
if item['object']['conversation']: if item['object']['conversation']:
# no conversations originated in non-permitted domains # no conversations originated in non-permitted domains
if urlPermitted(item['object']['conversation'], \ if urlPermitted(item['object']['conversation'], \
federationList,ocapGranted,"objects:read"): federationList,"objects:read"):
conversation = item['object']['conversation'] conversation = item['object']['conversation']
attachment = [] attachment = []
@ -237,7 +240,7 @@ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \
if attach.get('name') and attach.get('url'): if attach.get('name') and attach.get('url'):
# no attachments from non-permitted domains # no attachments from non-permitted domains
if urlPermitted(attach['url'], \ if urlPermitted(attach['url'], \
federationList,ocapGranted, \ federationList, \
"objects:read"): "objects:read"):
attachment.append([attach['name'],attach['url']]) attachment.append([attach['name'],attach['url']])
@ -319,7 +322,6 @@ def savePostToBox(baseDir: str,httpPrefix: str,postId: str, \
def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \
toUrl: str, ccUrl: str, httpPrefix: str, content: str, \ toUrl: str, ccUrl: str, httpPrefix: str, content: str, \
followersOnly: bool, saveToFile: bool, clientToServer: bool, \ followersOnly: bool, saveToFile: bool, clientToServer: bool, \
ocapGranted: {}, \
inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}: inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
"""Creates a message """Creates a message
""" """
@ -487,7 +489,7 @@ def postIsAddressedToFollowers(baseDir: str,
def createPublicPost(baseDir: str, def createPublicPost(baseDir: str,
nickname: str, domain: str, port: int,httpPrefix: str, \ nickname: str, domain: str, port: int,httpPrefix: str, \
content: str, followersOnly: bool, saveToFile: bool, content: str, followersOnly: bool, saveToFile: bool,
clientToServer: bool, ocapGranted: {},\ clientToServer: bool,\
inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}: inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
"""Public post to the outbox """Public post to the outbox
""" """
@ -495,10 +497,10 @@ def createPublicPost(baseDir: str,
'https://www.w3.org/ns/activitystreams#Public', \ 'https://www.w3.org/ns/activitystreams#Public', \
httpPrefix+'://'+domain+'/users/'+nickname+'/followers', \ httpPrefix+'://'+domain+'/users/'+nickname+'/followers', \
httpPrefix, content, followersOnly, saveToFile, \ httpPrefix, content, followersOnly, saveToFile, \
clientToServer, ocapGranted, \ clientToServer, \
inReplyTo, inReplyToAtomUri, subject) inReplyTo, inReplyToAtomUri, subject)
def threadSendPost(session,postJsonObject: {},federationList: [],ocapGranted: {},\ def threadSendPost(session,postJsonObject: {},federationList: [],\
inboxUrl: str, baseDir: str,signatureHeaderJson: {},postLog: [], inboxUrl: str, baseDir: str,signatureHeaderJson: {},postLog: [],
debug :bool) -> None: debug :bool) -> None:
"""Sends a post with exponential backoff """Sends a post with exponential backoff
@ -507,7 +509,7 @@ def threadSendPost(session,postJsonObject: {},federationList: [],ocapGranted: {}
backoffTime=60 backoffTime=60
for attempt in range(20): for attempt in range(20):
postResult = postJson(session,postJsonObject,federationList, \ postResult = postJson(session,postJsonObject,federationList, \
ocapGranted,inboxUrl,signatureHeaderJson, \ inboxUrl,signatureHeaderJson, \
"inbox:write") "inbox:write")
if postResult: if postResult:
if debug: if debug:
@ -534,7 +536,7 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \
toNickname: str, toDomain: str, toPort: int, cc: str, \ toNickname: str, toDomain: str, toPort: int, cc: str, \
httpPrefix: str, content: str, followersOnly: bool, \ httpPrefix: str, content: str, followersOnly: bool, \
saveToFile: bool, clientToServer: bool, \ saveToFile: bool, clientToServer: bool, \
federationList: [], ocapGranted: {},\ federationList: [],\
sendThreads: [], postLog: [], cachedWebfingers: {},personCache: {}, \ sendThreads: [], postLog: [], cachedWebfingers: {},personCache: {}, \
debug=False,inReplyTo=None,inReplyToAtomUri=None,subject=None) -> int: debug=False,inReplyTo=None,inReplyToAtomUri=None,subject=None) -> int:
"""Post to another inbox """Post to another inbox
@ -583,7 +585,6 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \
createPostBase(baseDir,nickname,domain,port, \ createPostBase(baseDir,nickname,domain,port, \
toPersonId,cc,httpPrefix,content, \ toPersonId,cc,httpPrefix,content, \
followersOnly,saveToFile,clientToServer, \ followersOnly,saveToFile,clientToServer, \
ocapGranted, \
inReplyTo,inReplyToAtomUri,subject) inReplyTo,inReplyToAtomUri,subject)
# get the senders private key # get the senders private key
@ -607,7 +608,6 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \
thr = threadWithTrace(target=threadSendPost,args=(session, \ thr = threadWithTrace(target=threadSendPost,args=(session, \
postJsonObject.copy(), \ postJsonObject.copy(), \
federationList, \ federationList, \
ocapGranted, \
inboxUrl,baseDir, \ inboxUrl,baseDir, \
signatureHeaderJson.copy(), \ signatureHeaderJson.copy(), \
postLog, postLog,
@ -639,7 +639,7 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \
nickname: str, domain: str, port: int, \ nickname: str, domain: str, port: int, \
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: [], ocapGranted: {}, \ federationList: [], \
sendThreads: [], postLog: [], cachedWebfingers: {}, \ sendThreads: [], postLog: [], cachedWebfingers: {}, \
personCache: {}, debug: bool) -> int: personCache: {}, debug: bool) -> int:
"""Sends a signed json object to an inbox/outbox """Sends a signed json object to an inbox/outbox
@ -715,7 +715,6 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \
args=(session, \ args=(session, \
postJsonObject.copy(), \ postJsonObject.copy(), \
federationList, \ federationList, \
ocapGranted, \
inboxUrl,baseDir, \ inboxUrl,baseDir, \
signatureHeaderJson.copy(), \ signatureHeaderJson.copy(), \
postLog, postLog,
@ -754,7 +753,7 @@ def sendToFollowers(session,baseDir: str,
nickname,domain,port, \ nickname,domain,port, \
toNickname,toDomain,toPort, \ toNickname,toDomain,toPort, \
cc,httpPrefix,True,clientToServer, \ cc,httpPrefix,True,clientToServer, \
federationList,ocapGranted, \ federationList, \
sendThreads,postLog,cachedWebfingers, \ sendThreads,postLog,cachedWebfingers, \
personCache,debug) personCache,debug)
@ -900,7 +899,6 @@ def getPublicPostsOfPerson(nickname: str,domain: str, \
personCache={} personCache={}
cachedWebfingers={} cachedWebfingers={}
federationList=[] federationList=[]
ocapGranted={}
httpPrefix='https' httpPrefix='https'
handle=httpPrefix+"://"+domain+"/@"+nickname handle=httpPrefix+"://"+domain+"/@"+nickname
@ -917,6 +915,40 @@ def getPublicPostsOfPerson(nickname: str,domain: str, \
maxEmoji=10 maxEmoji=10
maxAttachments=5 maxAttachments=5
userPosts = getPosts(session,personUrl,30,maxMentions,maxEmoji, \ userPosts = getPosts(session,personUrl,30,maxMentions,maxEmoji, \
maxAttachments,federationList,ocapGranted, \ maxAttachments,federationList, \
personCache,raw,simple) personCache,raw,simple)
#print(str(userPosts)) #print(str(userPosts))
def sendCapabilitiesUpdate(session,baseDir: str,httpPrefix: str, \
nickname: str,domain: str,port: int, \
followerUrl,updateCaps: [], \
sendThreads: [],postLog: [], \
cachedWebfingers: {},personCache: {}, \
federationList :[],debug :bool) -> int:
"""When the capabilities for a follower are changed this
sends out an update. followerUrl is the actor of the follower.
"""
updateJson=capabilitiesUpdate(baseDir,httpPrefix, \
nickname,domain,port, \
followerUrl, \
updateCaps)
if not updateJson:
return 1
if debug:
pprint(updateJson)
print('DEBUG: sending capabilities update from '+ \
nickname+'@'+domain+' port '+ str(port) + \
' to '+followerUrl)
clientToServer=False
followerNickname=getNicknameFromActor(followerUrl)
followerDomain,followerPort=getDomainFromActor(followerUrl)
return sendSignedJson(updateJson,session,baseDir, \
nickname,domain,port, \
followerNickname,followerDomain,followerPort, '', \
httpPrefix,True,clientToServer, \
federationList, \
sendThreads,postLog,cachedWebfingers, \
personCache,debug)

View File

@ -39,7 +39,7 @@ def getJson(session,url: str,headers: {},params: {}) -> {}:
pass pass
return None return None
def postJson(session,postJsonObject: {},federationList: [],ocapGranted: {},inboxUrl: str,headers: {},capability: str) -> str: def postJson(session,postJsonObject: {},federationList: [],inboxUrl: str,headers: {},capability: str) -> str:
"""Post a json message to the inbox of another person """Post a json message to the inbox of another person
Supplying a capability, such as "inbox:write" Supplying a capability, such as "inbox:write"
""" """
@ -47,7 +47,7 @@ def postJson(session,postJsonObject: {},federationList: [],ocapGranted: {},inbox
# always allow capability requests # always allow capability requests
if not capability.startswith('cap'): if not capability.startswith('cap'):
# check that we are posting to a permitted domain # check that we are posting to a permitted domain
if not urlPermitted(inboxUrl,federationList,ocapGranted,capability): if not urlPermitted(inboxUrl,federationList,capability):
return None return None
postResult = session.post(url = inboxUrl, data = json.dumps(postJsonObject), headers=headers) postResult = session.post(url = inboxUrl, data = json.dumps(postJsonObject), headers=headers)

126
tests.py
View File

@ -10,6 +10,8 @@ import base64
import time import time
import os, os.path import os, os.path
import shutil import shutil
import commentjson
from pprint import pprint
from person import createPerson from person import createPerson
from Crypto.Hash import SHA256 from Crypto.Hash import SHA256
from httpsig import signPostHeaders from httpsig import signPostHeaders
@ -25,6 +27,7 @@ from posts import sendPost
from posts import archivePosts from posts import archivePosts
from posts import noOfFollowersOnDomain from posts import noOfFollowersOnDomain
from posts import groupFollowersByDomain from posts import groupFollowersByDomain
from posts import sendCapabilitiesUpdate
from follow import clearFollows from follow import clearFollows
from follow import clearFollowers from follow import clearFollowers
from utils import followPerson from utils import followPerson
@ -111,7 +114,7 @@ def testThreads():
thr.join() thr.join()
assert thr.isAlive()==False assert thr.isAlive()==False
def createServerAlice(path: str,domain: str,port: int,federationList: [],ocapGranted: {},hasFollows: bool,hasPosts :bool,ocapAlways: bool): def createServerAlice(path: str,domain: str,port: int,federationList: [],hasFollows: bool,hasPosts :bool,ocapAlways: 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)
@ -129,15 +132,15 @@ def createServerAlice(path: str,domain: str,port: int,federationList: [],ocapGra
followPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList,True) followPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList,True)
followerOfPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList,True) followerOfPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList,True)
if hasPosts: if hasPosts:
createPublicPost(path,nickname, domain, port,httpPrefix, "No wise fish would go anywhere without a porpoise", False, True, clientToServer,ocapGranted) createPublicPost(path,nickname, domain, port,httpPrefix, "No wise fish would go anywhere without a porpoise", False, True, clientToServer)
createPublicPost(path,nickname, domain, port,httpPrefix, "Curiouser and curiouser!", False, True, clientToServer,ocapGranted) createPublicPost(path,nickname, domain, port,httpPrefix, "Curiouser and curiouser!", False, True, clientToServer)
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,ocapGranted) 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)
global testServerAliceRunning global testServerAliceRunning
testServerAliceRunning = True testServerAliceRunning = True
print('Server running: Alice') print('Server running: Alice')
runDaemon(path,domain,port,httpPrefix,federationList,ocapAlways,ocapGranted,useTor,True) runDaemon(path,domain,port,httpPrefix,federationList,ocapAlways,useTor,True)
def createServerBob(path: str,domain: str,port: int,federationList: [],ocapGranted: {},hasFollows: bool,hasPosts :bool,ocapAlways :bool): def createServerBob(path: str,domain: str,port: int,federationList: [],hasFollows: bool,hasPosts :bool,ocapAlways :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)
@ -155,15 +158,15 @@ def createServerBob(path: str,domain: str,port: int,federationList: [],ocapGrant
followPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList,True) followPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList,True)
followerOfPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList,True) followerOfPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList,True)
if hasPosts: if hasPosts:
createPublicPost(path,nickname, domain, port,httpPrefix, "It's your life, live it your way.", False, True, clientToServer,ocapGranted) createPublicPost(path,nickname, domain, port,httpPrefix, "It's your life, live it your way.", False, True, clientToServer)
createPublicPost(path,nickname, domain, port,httpPrefix, "One of the things I've realised is that I am very simple", False, True, clientToServer,ocapGranted) createPublicPost(path,nickname, domain, port,httpPrefix, "One of the things I've realised is that I am very simple", False, True, clientToServer)
createPublicPost(path,nickname, domain, port,httpPrefix, "Quantum physics is a bit of a passion of mine", False, True, clientToServer,ocapGranted) createPublicPost(path,nickname, domain, port,httpPrefix, "Quantum physics is a bit of a passion of mine", False, True, clientToServer)
global testServerBobRunning global testServerBobRunning
testServerBobRunning = True testServerBobRunning = True
print('Server running: Bob') print('Server running: Bob')
runDaemon(path,domain,port,httpPrefix,federationList,ocapAlways,ocapGranted,useTor,True) runDaemon(path,domain,port,httpPrefix,federationList,ocapAlways,useTor,True)
def createServerEve(path: str,domain: str,port: int,federationList: [],ocapGranted: {},hasFollows: bool,hasPosts :bool,ocapAlways :bool): def createServerEve(path: str,domain: str,port: int,federationList: [],hasFollows: bool,hasPosts :bool,ocapAlways :bool):
print('Creating test server: Eve on port '+str(port)) print('Creating test server: Eve on port '+str(port))
if os.path.isdir(path): if os.path.isdir(path):
shutil.rmtree(path) shutil.rmtree(path)
@ -180,7 +183,7 @@ def createServerEve(path: str,domain: str,port: int,federationList: [],ocapGrant
global testServerEveRunning global testServerEveRunning
testServerEveRunning = True testServerEveRunning = True
print('Server running: Eve') print('Server running: Eve')
runDaemon(path,domain,port,httpPrefix,federationList,ocapAlways,ocapGranted,useTor,True) runDaemon(path,domain,port,httpPrefix,federationList,ocapAlways,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')
@ -193,7 +196,6 @@ def testPostMessageBetweenServers():
httpPrefix='http' httpPrefix='http'
useTor=False useTor=False
federationList=['127.0.0.50','127.0.0.100'] federationList=['127.0.0.50','127.0.0.100']
ocapGranted={}
baseDir=os.getcwd() baseDir=os.getcwd()
if os.path.isdir(baseDir+'/.tests'): if os.path.isdir(baseDir+'/.tests'):
@ -206,12 +208,12 @@ def testPostMessageBetweenServers():
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,ocapGranted,True,True,ocapAlways),daemon=True) thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,True,True,ocapAlways),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,ocapGranted,True,True,ocapAlways),daemon=True) thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,True,True,ocapAlways),daemon=True)
thrAlice.start() thrAlice.start()
thrBob.start() thrBob.start()
@ -238,7 +240,7 @@ def testPostMessageBetweenServers():
ccUrl=None ccUrl=None
alicePersonCache={} alicePersonCache={}
aliceCachedWebfingers={} aliceCachedWebfingers={}
sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Why is a mouse when it spins?', followersOnly, saveToFile, clientToServer, federationList, ocapGranted, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject) sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Why is a mouse when it spins?', followersOnly, saveToFile, clientToServer, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject)
print('sendResult: '+str(sendResult)) print('sendResult: '+str(sendResult))
queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue' queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
@ -280,7 +282,6 @@ def testFollowBetweenServers():
httpPrefix='http' httpPrefix='http'
useTor=False useTor=False
federationList=[] federationList=[]
ocapGranted={}
baseDir=os.getcwd() baseDir=os.getcwd()
if os.path.isdir(baseDir+'/.tests'): if os.path.isdir(baseDir+'/.tests'):
@ -293,17 +294,17 @@ def testFollowBetweenServers():
aliceDir=baseDir+'/.tests/alice' aliceDir=baseDir+'/.tests/alice'
aliceDomain='127.0.0.42' aliceDomain='127.0.0.42'
alicePort=61935 alicePort=61935
thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,ocapGranted,False,False,ocapAlways),daemon=True) thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,False,False,ocapAlways),daemon=True)
bobDir=baseDir+'/.tests/bob' bobDir=baseDir+'/.tests/bob'
bobDomain='127.0.0.64' bobDomain='127.0.0.64'
bobPort=61936 bobPort=61936
thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,ocapGranted,False,False,ocapAlways),daemon=True) thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,False,False,ocapAlways),daemon=True)
eveDir=baseDir+'/.tests/eve' eveDir=baseDir+'/.tests/eve'
eveDomain='127.0.0.55' eveDomain='127.0.0.55'
evePort=61937 evePort=61937
thrEve = threadWithTrace(target=createServerEve,args=(eveDir,eveDomain,evePort,federationList,ocapGranted,False,False,False),daemon=True) thrEve = threadWithTrace(target=createServerEve,args=(eveDir,eveDomain,evePort,federationList,False,False,False),daemon=True)
thrAlice.start() thrAlice.start()
thrBob.start() thrBob.start()
@ -327,6 +328,7 @@ def testFollowBetweenServers():
# In the beginning all was calm and there were no follows # In the beginning all was calm and there were no follows
print('*********************************************************')
print('Alice sends a follow request to Bob') print('Alice sends a follow request to Bob')
print('Both are strictly enforcing object capabilities') print('Both are strictly enforcing object capabilities')
os.chdir(aliceDir) os.chdir(aliceDir)
@ -348,20 +350,31 @@ def testFollowBetweenServers():
sendFollowRequest(sessionAlice,aliceDir, \ sendFollowRequest(sessionAlice,aliceDir, \
'alice',aliceDomain,alicePort,httpPrefix, \ 'alice',aliceDomain,alicePort,httpPrefix, \
'bob',bobDomain,bobPort,httpPrefix, \ 'bob',bobDomain,bobPort,httpPrefix, \
clientToServer,federationList,ocapGranted, clientToServer,federationList,
aliceSendThreads,alicePostLog, \ aliceSendThreads,alicePostLog, \
aliceCachedWebfingers,alicePersonCache,True) aliceCachedWebfingers,alicePersonCache,True)
print('sendResult: '+str(sendResult)) print('sendResult: '+str(sendResult))
bobCapsFilename=bobDir+'/accounts/bob@'+bobDomain+'/ocap/accept/'+httpPrefix+':##'+aliceDomain+':'+str(alicePort)+'#users#alice.json'
aliceCapsFilename=aliceDir+'/accounts/alice@'+aliceDomain+'/ocap/granted/'+httpPrefix+':##'+bobDomain+':'+str(bobPort)+'#users#bob.json'
for t in range(10): for t in range(10):
if os.path.isfile(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt'): if os.path.isfile(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt'):
if os.path.isfile(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt'): if os.path.isfile(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt'):
if os.path.isfile(bobDir+'/accounts/bob@'+bobDomain+'/ocap/accept/'+httpPrefix+':##'+aliceDomain+':'+str(alicePort)+'#users#alice.json'): if os.path.isfile(bobCapsFilename):
if os.path.isfile(aliceDir+'/accounts/alice@'+aliceDomain+'/ocap/granted/'+httpPrefix+':##'+bobDomain+':'+str(bobPort)+'#users#bob.json'): if os.path.isfile(aliceCapsFilename):
break break
time.sleep(1) time.sleep(1)
print('\n\nEve tries to send to Bob') with open(bobCapsFilename, 'r') as fp:
bobCapsJson=commentjson.load(fp)
if not bobCapsJson.get('capability'):
print("Unexpected format for Bob's capabilities")
pprint(bobCapsJson)
assert False
print('\n\n*********************************************************')
print('Eve tries to send to Bob')
sessionEve = createSession(eveDomain,evePort,useTor) sessionEve = createSession(eveDomain,evePort,useTor)
eveSendThreads = [] eveSendThreads = []
evePostLog = [] evePostLog = []
@ -369,7 +382,7 @@ def testFollowBetweenServers():
eveCachedWebfingers={} eveCachedWebfingers={}
eveSendThreads=[] eveSendThreads=[]
evePostLog=[] evePostLog=[]
sendResult = sendPost(sessionEve,eveDir,'eve', eveDomain, evePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Eve message', followersOnly, saveToFile, clientToServer, federationList, ocapGranted, eveSendThreads, evePostLog, eveCachedWebfingers,evePersonCache,inReplyTo, inReplyToAtomUri, subject) sendResult = sendPost(sessionEve,eveDir,'eve', eveDomain, evePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Eve message', followersOnly, saveToFile, clientToServer, federationList, eveSendThreads, evePostLog, eveCachedWebfingers,evePersonCache,inReplyTo, inReplyToAtomUri, subject)
print('sendResult: '+str(sendResult)) print('sendResult: '+str(sendResult))
queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue' queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
@ -387,14 +400,15 @@ def testFollowBetweenServers():
assert eveMessageArrived==False assert eveMessageArrived==False
print('Message from Eve to Bob was correctly rejected by object capabilities') print('Message from Eve to Bob was correctly rejected by object capabilities')
print('\n\n*********************************************************')
print('Alice sends a message to Bob')
aliceSendThreads = [] aliceSendThreads = []
alicePostLog = [] alicePostLog = []
alicePersonCache={} alicePersonCache={}
aliceCachedWebfingers={} aliceCachedWebfingers={}
aliceSendThreads=[] aliceSendThreads=[]
alicePostLog=[] alicePostLog=[]
sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Alice message', followersOnly, saveToFile, clientToServer, federationList, ocapGranted, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject) sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Alice message', followersOnly, saveToFile, clientToServer, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject)
print('sendResult: '+str(sendResult)) print('sendResult: '+str(sendResult))
queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue' queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
@ -411,6 +425,64 @@ def testFollowBetweenServers():
assert aliceMessageArrived==True assert aliceMessageArrived==True
print('Message from Alice to Bob succeeded, since it was granted capabilities') print('Message from Alice to Bob succeeded, since it was granted capabilities')
print('\n\n*********************************************************')
print("\nBob changes Alice's capabilities so that she can't reply on his posts")
sessionBob = createSession(bobDomain,bobPort,useTor)
bobSendThreads = []
bobPostLog = []
bobPersonCache={}
bobCachedWebfingers={}
print("Bob's capabilities for Alice:")
with open(bobCapsFilename, 'r') as fp:
bobCapsJson=commentjson.load(fp)
pprint(bobCapsJson)
assert "inbox:noreply" not in bobCapsJson['capability']
print("Alice's capabilities granted by Bob")
with open(aliceCapsFilename, 'r') as fp:
aliceCapsJson=commentjson.load(fp)
pprint(aliceCapsJson)
assert "inbox:noreply" not in aliceCapsJson['capability']
newCapabilities=["inbox:write","objects:read","inbox:noreply"]
sendCapabilitiesUpdate(sessionBob,bobDir,httpPrefix, \
'bob',bobDomain,bobPort, \
httpPrefix+'://'+aliceDomain+':'+str(alicePort)+'/users/alice',
newCapabilities, \
bobSendThreads, bobPostLog, \
bobCachedWebfingers,bobPersonCache, \
federationList,True)
bobChanged=False
bobNewCapsJson=None
for i in range(20):
time.sleep(1)
with open(bobCapsFilename, 'r') as fp:
bobNewCapsJson=commentjson.load(fp)
if "inbox:noreply" in bobNewCapsJson['capability']:
print("Bob's capabilities were changed")
pprint(bobNewCapsJson)
bobChanged=True
break
assert bobChanged
aliceChanged=False
aliceNewCapsJson=None
for i in range(20):
time.sleep(1)
with open(aliceCapsFilename, 'r') as fp:
aliceNewCapsJson=commentjson.load(fp)
if "inbox:noreply" in aliceNewCapsJson['capability']:
print("Alice's granted capabilities were changed")
pprint(aliceNewCapsJson)
aliceChanged=True
break
assert aliceChanged
# check that the capabilities id has changed
assert bobNewCapsJson['id']!=bobCapsJson['id']
assert aliceNewCapsJson['id']!=aliceCapsJson['id']
# stop the servers # stop the servers
thrAlice.kill() thrAlice.kill()
thrAlice.join() thrAlice.join()

View File

@ -48,7 +48,7 @@ def domainPermitted(domain: str, federationList: []):
return True return True
return False return False
def urlPermitted(url: str, federationList: [],ocapGranted: {},capability: str): def urlPermitted(url: str, federationList: [],capability: str):
if len(federationList)==0: if len(federationList)==0:
return True return True
for domain in federationList: for domain in federationList: