Add capabilities to follow requests

master
Bob Mottram 2019-07-07 12:53:32 +01:00
parent 03a43f64f1
commit e9cf9dfc27
12 changed files with 246 additions and 112 deletions

View File

@ -8,6 +8,8 @@ __status__ = "Production"
import json
import commentjson
from capabilities import capabilitiesAccept
from capabilities import capabilitiesGrantedSave
from utils import getStatusNumber
from utils import createOutboxDir
from utils import urlPermitted
@ -16,10 +18,10 @@ from utils import getNicknameFromActor
from utils import domainPermitted
from utils import followPerson
def createAcceptReject(baseDir: str,federationList: [],capsList: [], \
def createAcceptReject(baseDir: str,federationList: [],ocapGranted: {}, \
nickname: str,domain: str,port: int, \
toUrl: str,ccUrl: str,httpPrefix: str, \
objectJson: {},acceptType: str) -> {}:
objectJson: {},ocapJson,acceptType: str) -> {}:
"""Accepts or rejects something (eg. a follow request or offer)
Typically toUrl will be https://www.w3.org/ns/activitystreams#Public
and ccUrl might be a specific person favorited or repeated and
@ -29,7 +31,7 @@ def createAcceptReject(baseDir: str,federationList: [],capsList: [], \
if not objectJson.get('actor'):
return None
if not urlPermitted(objectJson['actor'],federationList,capsList,"inbox:write"):
if not urlPermitted(objectJson['actor'],federationList,ocapGranted,"inbox:write"):
return None
if port!=80 and port!=443:
@ -45,28 +47,33 @@ def createAcceptReject(baseDir: str,federationList: [],capsList: [], \
if ccUrl:
if len(ccUrl)>0:
newAccept['cc']=ccUrl
# attach capabilities for follow accept
if ocapJson:
newAccept['capabilities']=ocapJson
return newAccept
def createAccept(baseDir: str,federationList: [],capsList: [], \
def createAccept(baseDir: str,federationList: [],ocapGranted: {}, \
nickname: str,domain: str,port: int, \
toUrl: str,ccUrl: str,httpPrefix: str, \
objectJson: {}) -> {}:
return createAcceptReject(baseDir,federationList,capsList, \
# create capabilities accept
ocapNew=capabilitiesAccept(baseDir,httpPrefix,nickname,domain, toUrl, True)
return createAcceptReject(baseDir,federationList,ocapGranted, \
nickname,domain,port, \
toUrl,ccUrl,httpPrefix, \
objectJson,'Accept')
objectJson,ocapNew,'Accept')
def createReject(baseDir: str,federationList: [],capsList: [], \
def createReject(baseDir: str,federationList: [],ocapGranted: {}, \
nickname: str,domain: str,port: int, \
toUrl: str,ccUrl: str,httpPrefix: str, \
objectJson: {}) -> {}:
return createAcceptReject(baseDir,federationList,capsList, \
return createAcceptReject(baseDir,federationList,ocapGranted, \
nickname,domain,port, \
toUrl,ccUrl, \
httpPrefix,objectJson,'Reject')
httpPrefix,objectJson,None,'Reject')
def acceptFollow(baseDir: str,domain : str,messageJson: {}, \
federationList: [],capsList: [],debug : bool) -> None:
federationList: [],ocapGranted: {},debug : bool) -> None:
if not messageJson.get('object'):
return
if not messageJson['object'].get('type'):
@ -115,6 +122,12 @@ def acceptFollow(baseDir: str,domain : str,messageJson: {}, \
followedNickname=getNicknameFromActor(followedActor)
if not followedNickname:
return
# are capabilities attached? If so then store them
if messageJson['object'].get('capabilities'):
if isinstance(messageJson['object']['capabilities'], dict):
capabilitiesGrantedSave(baseDir,messageJson['object']['capabilities'])
if followPerson(baseDir,nickname,domain, \
followedNickname,followedDomain, \
federationList,debug):
@ -128,7 +141,7 @@ def receiveAcceptReject(session,baseDir: str, \
httpPrefix: str,domain :str,port: int, \
sendThreads: [],postLog: [],cachedWebfingers: {}, \
personCache: {},messageJson: {},federationList: [], \
capsList: [],debug : bool) -> bool:
ocapGranted: {},debug : bool) -> bool:
"""Receives an Accept or Reject within the POST section of HTTPServer
"""
if messageJson['type']!='Accept' and messageJson['type']!='Reject':
@ -152,7 +165,7 @@ def receiveAcceptReject(session,baseDir: str, \
print('DEBUG: '+messageJson['type']+' does not contain a nickname')
return False
handle=nickname.lower()+'@'+domain.lower()
acceptFollow(baseDir,domain,messageJson,federationList,capsList,debug)
acceptFollow(baseDir,domain,messageJson,federationList,ocapGranted,debug)
if debug:
print('DEBUG: Uh, '+messageJson['type']+', I guess')
return True

View File

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

View File

@ -7,42 +7,99 @@ __email__ = "bob@freedombone.net"
__status__ = "Production"
import os
import datetime
import time
import json
import commentjson
from auth import createPassword
def sendCapabilitiesRequest(baseDir: str,httpPrefix: str,domain: str, \
requestedActor: str, \
requestedCaps=["inbox:write","objects:read"]) -> None:
def capabilitiesMakeDirs(baseDir: str):
if not os.path.isdir(baseDir+'/ocap'):
os.mkdir(baseDir+'/ocap')
# for capabilities accepted by this instance
if not os.path.isdir(baseDir+'/ocap/accept'):
os.mkdir(baseDir+'/ocap/accept')
# for capabilities granted to this instance
if not os.path.isdir(baseDir+'/ocap/granted'):
os.mkdir(baseDir+'/ocap/granted')
def capabilitiesRequest(baseDir: str,httpPrefix: str,domain: str, \
requestedActor: str, \
requestedCaps=["inbox:write","objects:read"]) -> {}:
# This is sent to the capabilities endpoint /caps/new
# which could be instance wide or for a particular person
capId=createPassword(32)
capRequest = {
"id": httpPrefix+"://"+requestedDomain+"/caps/request/"+capId,
# This could also be added to a follow activity
capabilitiesMakeDirs(baseDir)
ocapId=createPassword(32)
ocapRequest = {
"id": httpPrefix+"://"+requestedDomain+"/caps/request/"+ocapId,
"type": "Request",
"capability": requestedCaps,
"actor": requestedActor
}
#TODO
return ocapRequest
def sendCapabilitiesAccept(baseDir: str,httpPrefix: str,nickname: str,domain: str, \
acceptedActor: str, \
acceptedCaps=["inbox:write","objects:read"]) -> None:
def capabilitiesAccept(baseDir: str,httpPrefix: str,nickname: str,domain: str, \
acceptedActor: str, saveToFile: bool, \
acceptedCaps=["inbox:write","objects:read"]) -> {}:
# This gets returned to capabilities requester
capId=createPassword(32)
capAccept = {
"id": httpPrefix+"://"+domain+"/caps/"+capId,
"type": "Capability",
"capability": acceptedCaps,
"scope": acceptedActor,
"actor": httpPrefix+"://"+domain
}
if nickname:
capAccept['actor']=httpPrefix+"://"+domain+'/users/'+nickname
#TODO
# This could also be added to a follow Accept activity
def isCapable(actor: str,capsJson: [],capability: str) -> bool:
# reject excessively long actors
if len(acceptedActor)>256:
return None
# make directories to store capabilities
capabilitiesMakeDirs(baseDir)
filename=baseDir+'/ocap/accept/'+acceptedActor.replace('/','#')+'.json'
ocapAccept=None
# if the capability already exists then load it from file
if os.path.isfile(filename):
with open(filename, 'r') as fp:
ocapAccept=commentjson.load(fp)
# otherwise create a new capability
if not ocapAccept:
ocapId=createPassword(32)
ocapAccept = {
"id": httpPrefix+"://"+domain+"/caps/"+ocapId,
"type": "Capability",
"capability": acceptedCaps,
"scope": acceptedActor,
"actor": httpPrefix+"://"+domain
}
if nickname:
ocapAccept['actor']=httpPrefix+"://"+domain+'/users/'+nickname
if saveToFile:
with open(filename, 'w') as fp:
commentjson.dump(ocapAccept, fp, indent=4, sort_keys=False)
return ocapAccept
def capabilitiesGrantedSave(baseDir :str,ocap: {}) -> bool:
"""A capabilities accept is received, so stor it for
reference when sending to the actor
"""
if not ocap.get('actor'):
return False
filename=baseDir+'/ocap/granted/'+ocap['actor'].replace('/','#')+'.json'
with open(filename, 'w') as fp:
commentjson.dump(ocap, fp, indent=4, sort_keys=False)
return True
def isCapable(actor: str,ocapGranted: {},capability: str) -> bool:
# is the given actor capable of using the current resource?
for cap in capsJson:
if cap['scope'] in actor:
if capability in cap['capability']:
for id,ocap in ocapGranted.items():
if ocap['scope'] in actor:
if capability in ocap['capability']:
return True
return False
def isCapableId(id: str,ocapGranted: {},capability: str) -> bool:
# is the given id capable of using the current resource?
if ocapGranted.get(id):
if ocapGranted['id']['scope'] in actor:
if capability in ocapGranted['id']['capability']:
return True
return False

View File

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

View File

@ -6,6 +6,7 @@ __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
from capabilities import capabilitiesMakeDirs
from person import createPerson
from person import createSharedInbox
from person import createCapabilitiesInbox
@ -314,7 +315,7 @@ else:
if configFederationList:
federationList=configFederationList
capsList=[]
ocapGranted={}
if federationList:
print('Federating with: '+str(federationList))
@ -334,6 +335,8 @@ if not os.path.isdir(baseDir+'/accounts/capabilities@'+domain):
print('Creating capabilities account which can sign requests')
createCapabilitiesInbox(baseDir,'capabilities',domain,port,httpPrefix)
capabilitiesMakeDirs(baseDir)
if args.testdata:
nickname='testuser567'
print('Generating some test data for user: '+nickname)
@ -342,13 +345,13 @@ if args.testdata:
deleteAllPosts(baseDir,nickname,domain,'outbox')
followPerson(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,capsList)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Zoiks!!!",False,True,False,capsList)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Hey scoob we need like a hundred more milkshakes",False,True,False,capsList)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Getting kinda spooky around here",False,True,False,capsList)
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,capsList)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"man, these centralized sites are, like, the worst!",False,True,False,capsList)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"another mystery solved hey",False,True,False,capsList)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"let's go bowling",False,True,False,capsList)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"like, this is totally just a test, man",False,True,False,ocapGranted)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"Zoiks!!!",False,True,False,ocapGranted)
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,"Getting kinda spooky around here",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,ocapGranted)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"man, these centralized sites are, like, the worst!",False,True,False,ocapGranted)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"another mystery solved hey",False,True,False,ocapGranted)
createPublicPost(baseDir,nickname,domain,port,httpPrefix,"let's go bowling",False,True,False,ocapGranted)
runDaemon(baseDir,domain,port,httpPrefix,federationList,capsList,useTor,debug)
runDaemon(baseDir,domain,port,httpPrefix,federationList,ocapGranted,useTor,debug)

View File

@ -221,7 +221,7 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \
port: int,sendThreads: [],postLog: [], \
cachedWebfingers: {},personCache: {}, \
messageJson: {},federationList: [], \
capsList: [],debug : bool) -> bool:
ocapGranted: {},debug : bool) -> bool:
"""Receives a follow request within the POST section of HTTPServer
"""
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 '+ \
nicknameToFollow+'@'+domainToFollow+' back to '+nickname+'@'+domain)
personUrl=messageJson['actor']
acceptJson=createAccept(baseDir,federationList,capsList, \
acceptJson=createAccept(baseDir,federationList,ocapGranted, \
nickname,domain,port, \
personUrl,'',httpPrefix,messageJson)
if debug:
@ -295,7 +295,7 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \
nicknameToFollow,domainToFollow,port, \
nickname,domain,fromPort, '', \
httpPrefix,True,clientToServer, \
federationList, capsList, \
federationList, ocapGranted, \
sendThreads,postLog,cachedWebfingers, \
personCache,debug)
@ -303,7 +303,7 @@ def sendFollowRequest(session,baseDir: str, \
nickname: str,domain: str,port: int,httpPrefix: str, \
followNickname: str,followDomain: str, \
followPort: bool,followHttpPrefix: str, \
clientToServer: bool,federationList: [],capsList: [], \
clientToServer: bool,federationList: [],ocapGranted: {}, \
sendThreads: [],postLog: [],cachedWebfingers: {}, \
personCache: {},debug : bool) -> {}:
"""Gets the json object for sending a follow request
@ -320,8 +320,8 @@ def sendFollowRequest(session,baseDir: str, \
requestDomain=followDomain+':'+str(followPort)
# check that we are capable
if capsList:
if not isCapable(followActor,capsList,'inbox:write'):
if ocapGranted:
if not isCapable(followActor,ocapGranted,'inbox:write'):
return None
statusNumber,published = getStatusNumber()
@ -342,7 +342,7 @@ def sendFollowRequest(session,baseDir: str, \
followNickname,followDomain,followPort, \
'https://www.w3.org/ns/activitystreams#Public', \
httpPrefix,True,clientToServer, \
federationList, capsList, \
federationList, ocapGranted, \
sendThreads,postLog,cachedWebfingers,personCache, debug)
return newFollowJson

View File

@ -65,7 +65,7 @@ def inboxMessageHasParams(messageJson: {}) -> bool:
return False
return True
def inboxPermittedMessage(domain: str,messageJson: {},federationList: [],capsList: []) -> bool:
def inboxPermittedMessage(domain: str,messageJson: {},federationList: [],ocapGranted: {}) -> bool:
""" check that we are receiving from a permitted domain
"""
testParam='actor'
@ -76,14 +76,14 @@ def inboxPermittedMessage(domain: str,messageJson: {},federationList: [],capsLis
if domain in actor:
return True
if not urlPermitted(actor,federationList,capsList,"inbox:write"):
if not urlPermitted(actor,federationList,ocapGranted,"inbox:write"):
return False
if messageJson['type']!='Follow':
if messageJson.get('object'):
if messageJson['object'].get('inReplyTo'):
inReplyTo=messageJson['object']['inReplyTo']
if not urlPermitted(inReplyTo,federationList,capsList):
if not urlPermitted(inReplyTo,federationList,ocapGranted):
return False
return True
@ -144,7 +144,7 @@ def savePostToInboxQueue(baseDir: str,httpPrefix: str,nickname: str, domain: str
commentjson.dump(newQueueItem, fp, indent=4, sort_keys=False)
return filename
def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cachedWebfingers: {},personCache: {},queue: [],domain: str,port: int,useTor: bool,federationList: [],capsList: [],debug: bool) -> None:
def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cachedWebfingers: {},personCache: {},queue: [],domain: str,port: int,useTor: bool,federationList: [],ocapGranted: {},debug: bool) -> None:
"""Processes received items and moves them to
the appropriate directories
"""
@ -174,6 +174,57 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache
with open(queueFilename, 'r') as fp:
queueJson=commentjson.load(fp)
# check that capabilities are accepted
if queueJson['post'].get('capabilities'):
if queueJson['post']['type']!='Accept':
if isinstance(queueJson['post']['capabilities'], dict):
if debug:
print('DEBUG: received post capabilities should be a string, not a dict')
pprint(queueJson['post'])
os.remove(queueFilename)
queue.pop(0)
continue
if not queueJson['post'].get('actor'):
if debug:
print('DEBUG: post should have an actor')
os.remove(queueFilename)
queue.pop(0)
continue
ocapFilename=baseDir+'/ocap/accept/'+queueJson['post']['actor'].replace('/','#')+'.json'
if not os.path.isfile(ocapFilename):
if debug:
print('DEBUG: capabilities for '+queueJson['post']['actor']+' do not exist')
os.remove(queueFilename)
queue.pop(0)
continue
with open(ocapFilename, 'r') as fp:
oc=commentjson.load(fp)
if not oc.get('id'):
if debug:
print('DEBUG: capabilities for '+queueJson['post']['actor']+' do not contain an id')
os.remove(queueFilename)
queue.pop(0)
continue
if oc['id']!=queueJson['post']['capabilities']:
if debug:
print('DEBUG: capabilities id mismatch')
os.remove(queueFilename)
queue.pop(0)
continue
if not queueJson['post']['capabilities'].get('capability'):
if debug:
print('DEBUG: missing capability list')
os.remove(queueFilename)
queue.pop(0)
continue
if 'inbox:write' not in queueJson['post']['capabilities']['capability']:
if debug:
print('DEBUG: insufficient capabilities to write to inbox from '+ \
queueJson['post']['actor'])
os.remove(queueFilename)
queue.pop(0)
continue
# Try a few times to obtain the public key
pubKey=None
keyId=None
@ -232,7 +283,7 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache
cachedWebfingers,
personCache,
queueJson['post'], \
federationList,capsList, \
federationList,ocapGranted, \
debug):
if debug:
print('DEBUG: Follow accepted from '+keyId)
@ -246,7 +297,7 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache
cachedWebfingers,
personCache,
queueJson['post'], \
federationList,capsList, \
federationList,ocapGranted, \
debug):
if debug:
print('DEBUG: Accept/Reject received from '+keyId)

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
objectUrl is typically the url of the message, corresponding to url or atomUri in createPostBase
"""
if not urlPermitted(objectUrl,federationList,capsList,"inbox:write"):
if not urlPermitted(objectUrl,federationList,ocapGranted,"inbox:write"):
return None
if port!=80 and port!=443:

View File

@ -153,7 +153,7 @@ def getPersonBox(session,wfRequest: {},personCache: {}, \
def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \
maxEmoji: int,maxAttachments: int, \
federationList: [], capsList: [],\
federationList: [], ocapGranted: {},\
personCache: {},raw: bool,simple: bool) -> {}:
personPosts={}
if not outboxUrl:
@ -193,7 +193,7 @@ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \
if tagItem['icon'].get('url'):
# No emoji from non-permitted domains
if urlPermitted(tagItem['icon']['url'], \
federationList,capsList, \
federationList,ocapGranted, \
"objects:read"):
emojiName=tagItem['name']
emojiIcon=tagItem['icon']['url']
@ -217,7 +217,7 @@ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \
if item['object']['inReplyTo']:
# No replies to non-permitted domains
if not urlPermitted(item['object']['inReplyTo'], \
federationList,capsList, \
federationList,ocapGranted, \
"objects:read"):
continue
inReplyTo = item['object']['inReplyTo']
@ -227,7 +227,7 @@ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \
if item['object']['conversation']:
# no conversations originated in non-permitted domains
if urlPermitted(item['object']['conversation'], \
federationList,capsList,"objects:read"):
federationList,ocapGranted,"objects:read"):
conversation = item['object']['conversation']
attachment = []
@ -237,7 +237,7 @@ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \
if attach.get('name') and attach.get('url'):
# no attachments from non-permitted domains
if urlPermitted(attach['url'], \
federationList,capsList, \
federationList,ocapGranted, \
"objects:read"):
attachment.append([attach['name'],attach['url']])
@ -319,7 +319,7 @@ def savePostToBox(baseDir: str,httpPrefix: str,postId: str, \
def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \
toUrl: str, ccUrl: str, httpPrefix: str, content: str, \
followersOnly: bool, saveToFile: bool, clientToServer: bool, \
capsList: [], \
ocapGranted: {}, \
inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
"""Creates a message
"""
@ -335,8 +335,7 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \
postTo=postCC
postCC=''
newPostId=httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber
# TODO
capabilityUrl=''
sensitive=False
summary=None
if subject:
@ -344,13 +343,24 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \
sensitive=True
if not clientToServer:
actorUrl=httpPrefix+'://'+domain+'/users/'+nickname
if capsList:
if not isCapable(actorUrl,capsList,'inbox:write'):
if ocapGranted:
if not isCapable(actorUrl,ocapGranted,'inbox:write'):
return None
# if capabilities have been granted for this actor
# then get the corresponding id
capabilityId=None
ocapFilename= \
baseDir+'/ocap/granted/'+actorUrl.replace('/','#')+'.json'
if os.path.isfile(ocapFilename):
with open(ocapFilename, 'r') as fp:
oc=commentjson.load(fp)
if oc.get('id'):
capabilityId=oc['id']
newPost = {
'id': newPostId+'/activity',
'capability': capabilityUrl,
'capability': capabilityId,
'type': 'Create',
'actor': actorUrl,
'published': published,
@ -454,7 +464,7 @@ def outboxMessageCreateWrap(httpPrefix: str,nickname: str,domain: str, \
def createPublicPost(baseDir: str,
nickname: str, domain: str, port: int,httpPrefix: str, \
content: str, followersOnly: bool, saveToFile: bool,
clientToServer: bool, capsList: [],\
clientToServer: bool, ocapGranted: {},\
inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
"""Public post to the outbox
"""
@ -462,10 +472,10 @@ def createPublicPost(baseDir: str,
'https://www.w3.org/ns/activitystreams#Public', \
httpPrefix+'://'+domain+'/users/'+nickname+'/followers', \
httpPrefix, content, followersOnly, saveToFile, \
clientToServer, capsList, \
clientToServer, ocapGranted, \
inReplyTo, inReplyToAtomUri, subject)
def threadSendPost(session,postJsonObject: {},federationList: [],capsList: [],\
def threadSendPost(session,postJsonObject: {},federationList: [],ocapGranted: {},\
inboxUrl: str, baseDir: str,signatureHeaderJson: {},postLog: [],
debug :bool) -> None:
"""Sends a post with exponential backoff
@ -474,7 +484,7 @@ def threadSendPost(session,postJsonObject: {},federationList: [],capsList: [],\
backoffTime=60
for attempt in range(20):
postResult = postJson(session,postJsonObject,federationList, \
capsList,inboxUrl,signatureHeaderJson, \
ocapGranted,inboxUrl,signatureHeaderJson, \
"inbox:write")
if postResult:
if debug:
@ -501,7 +511,7 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \
toNickname: str, toDomain: str, toPort: int, cc: str, \
httpPrefix: str, content: str, followersOnly: bool, \
saveToFile: bool, clientToServer: bool, \
federationList: [], capsList: [],\
federationList: [], ocapGranted: {},\
sendThreads: [], postLog: [], cachedWebfingers: {},personCache: {}, \
debug=False,inReplyTo=None,inReplyToAtomUri=None,subject=None) -> int:
"""Post to another inbox
@ -550,7 +560,7 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \
createPostBase(baseDir,nickname,domain,port, \
toPersonId,cc,httpPrefix,content, \
followersOnly,saveToFile,clientToServer, \
capsList, \
ocapGranted, \
inReplyTo,inReplyToAtomUri,subject)
# get the senders private key
@ -574,7 +584,7 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \
thr = threadWithTrace(target=threadSendPost,args=(session, \
postJsonObject.copy(), \
federationList, \
capsList, \
ocapGranted, \
inboxUrl,baseDir, \
signatureHeaderJson.copy(), \
postLog,
@ -587,7 +597,7 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \
nickname: str, domain: str, port: int, \
toNickname: str, toDomain: str, toPort: int, cc: str, \
httpPrefix: str, saveToFile: bool, clientToServer: bool, \
federationList: [], capsList: [], \
federationList: [], ocapGranted: {}, \
sendThreads: [], postLog: [], cachedWebfingers: {}, \
personCache: {}, debug: bool) -> int:
"""Sends a signed json object to an inbox/outbox
@ -661,7 +671,7 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \
args=(session, \
postJsonObject.copy(), \
federationList, \
capsList, \
ocapGranted, \
inboxUrl,baseDir, \
signatureHeaderJson.copy(), \
postLog,
@ -812,7 +822,7 @@ def getPublicPostsOfPerson(nickname: str,domain: str, \
personCache={}
cachedWebfingers={}
federationList=[]
capsList=[]
ocapGranted={}
httpPrefix='https'
handle=httpPrefix+"://"+domain+"/@"+nickname
@ -829,6 +839,6 @@ def getPublicPostsOfPerson(nickname: str,domain: str, \
maxEmoji=10
maxAttachments=5
userPosts = getPosts(session,personUrl,30,maxMentions,maxEmoji, \
maxAttachments,federationList,capsList, \
maxAttachments,federationList,ocapGranted, \
personCache,raw,simple)
#print(str(userPosts))

View File

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

View File

@ -108,7 +108,7 @@ def testThreads():
thr.join()
assert thr.isAlive()==False
def createServerAlice(path: str,domain: str,port: int,federationList: [],capsList: [],hasFollows: bool,hasPosts :bool):
def createServerAlice(path: str,domain: str,port: int,federationList: [],ocapGranted: {},hasFollows: bool,hasPosts :bool):
print('Creating test server: Alice on port '+str(port))
if os.path.isdir(path):
shutil.rmtree(path)
@ -126,15 +126,15 @@ def createServerAlice(path: str,domain: str,port: int,federationList: [],capsLis
followPerson(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:
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)
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, "Curiouser and curiouser!", 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,ocapGranted)
global testServerAliceRunning
testServerAliceRunning = True
print('Server running: Alice')
runDaemon(path,domain,port,httpPrefix,federationList,capsList,useTor,True)
runDaemon(path,domain,port,httpPrefix,federationList,ocapGranted,useTor,True)
def createServerBob(path: str,domain: str,port: int,federationList: [],capsList: [],hasFollows: bool,hasPosts :bool):
def createServerBob(path: str,domain: str,port: int,federationList: [],ocapGranted: {},hasFollows: bool,hasPosts :bool):
print('Creating test server: Bob on port '+str(port))
if os.path.isdir(path):
shutil.rmtree(path)
@ -152,13 +152,13 @@ def createServerBob(path: str,domain: str,port: int,federationList: [],capsList:
followPerson(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:
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)
createPublicPost(path,nickname, domain, port,httpPrefix, "It's your life, live it your way.", 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,ocapGranted)
createPublicPost(path,nickname, domain, port,httpPrefix, "Quantum physics is a bit of a passion of mine", False, True, clientToServer,ocapGranted)
global testServerBobRunning
testServerBobRunning = True
print('Server running: Bob')
runDaemon(path,domain,port,httpPrefix,federationList,capsList,useTor,True)
runDaemon(path,domain,port,httpPrefix,federationList,ocapGranted,useTor,True)
def testPostMessageBetweenServers():
print('Testing sending message from one server to the inbox of another')
@ -171,7 +171,7 @@ def testPostMessageBetweenServers():
httpPrefix='http'
useTor=False
federationList=['127.0.0.50','127.0.0.100']
capsList=[]
ocapGranted={}
baseDir=os.getcwd()
if os.path.isdir(baseDir+'/.tests'):
@ -182,12 +182,12 @@ def testPostMessageBetweenServers():
aliceDir=baseDir+'/.tests/alice'
aliceDomain='127.0.0.50'
alicePort=61935
thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,capsList,True,True),daemon=True)
thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,ocapGranted,True,True),daemon=True)
bobDir=baseDir+'/.tests/bob'
bobDomain='127.0.0.100'
bobPort=61936
thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,capsList,True,True),daemon=True)
thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,ocapGranted,True,True),daemon=True)
thrAlice.start()
thrBob.start()
@ -214,7 +214,7 @@ def testPostMessageBetweenServers():
ccUrl=None
alicePersonCache={}
aliceCachedWebfingers={}
sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Why is a mouse when it spins?', followersOnly, saveToFile, clientToServer, federationList, capsList, 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, ocapGranted, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject)
print('sendResult: '+str(sendResult))
queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
@ -254,7 +254,7 @@ def testFollowBetweenServers():
httpPrefix='http'
useTor=False
federationList=['127.0.0.42','127.0.0.64']
capsList=[]
ocapGranted={}
baseDir=os.getcwd()
if os.path.isdir(baseDir+'/.tests'):
@ -265,12 +265,12 @@ def testFollowBetweenServers():
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)
thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,ocapGranted,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)
thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,ocapGranted,False,False),daemon=True)
thrAlice.start()
thrBob.start()
@ -305,7 +305,7 @@ def testFollowBetweenServers():
sendFollowRequest(sessionAlice,aliceDir, \
'alice',aliceDomain,alicePort,httpPrefix, \
'bob',bobDomain,bobPort,httpPrefix, \
clientToServer,federationList,capsList,
clientToServer,federationList,ocapGranted,
aliceSendThreads,alicePostLog, \
aliceCachedWebfingers,alicePersonCache,True)
print('sendResult: '+str(sendResult))

View File

@ -49,9 +49,9 @@ def domainPermitted(domain: str, federationList: []):
return True
return False
def urlPermitted(url: str, federationList: [],capsList: [],capability: str):
if capsList:
if not isCapable(url,capsList,capability):
def urlPermitted(url: str, federationList: [],ocapGranted: {},capability: str):
if ocapGranted:
if not isCapable(url,ocapGranted,capability):
return False
if len(federationList)==0:
return True