tests.py 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351
  1. __filename__ = "tests.py"
  2. __author__ = "Bob Mottram"
  3. __license__ = "AGPL3+"
  4. __version__ = "0.0.1"
  5. __maintainer__ = "Bob Mottram"
  6. __email__ = "bob@freedombone.net"
  7. __status__ = "Production"
  8. import base64
  9. import time
  10. import os, os.path
  11. import shutil
  12. import commentjson
  13. import json
  14. from time import gmtime, strftime
  15. from pprint import pprint
  16. from person import createPerson
  17. from Crypto.Hash import SHA256
  18. from httpsig import signPostHeaders
  19. from httpsig import verifyPostHeaders
  20. from httpsig import messageContentDigest
  21. from cache import storePersonInCache
  22. from cache import getPersonFromCache
  23. from threads import threadWithTrace
  24. from daemon import runDaemon
  25. from session import createSession
  26. from posts import deleteAllPosts
  27. from posts import createPublicPost
  28. from posts import sendPost
  29. from posts import archivePosts
  30. from posts import noOfFollowersOnDomain
  31. from posts import groupFollowersByDomain
  32. from posts import sendCapabilitiesUpdate
  33. from posts import archivePostsForPerson
  34. from posts import sendPostViaServer
  35. from follow import clearFollows
  36. from follow import clearFollowers
  37. from follow import sendFollowRequestViaServer
  38. from follow import sendUnfollowRequestViaServer
  39. from utils import followPerson
  40. from follow import followerOfPerson
  41. from follow import unfollowPerson
  42. from follow import unfollowerOfPerson
  43. from follow import getFollowersOfPerson
  44. from follow import sendFollowRequest
  45. from person import createPerson
  46. from person import setPreferredNickname
  47. from person import setBio
  48. from skills import setSkillLevel
  49. from roles import setRole
  50. from roles import getRoles
  51. from roles import outboxDelegate
  52. from auth import createBasicAuthHeader
  53. from auth import authorizeBasic
  54. from auth import storeBasicCredentials
  55. from like import likePost
  56. from like import sendLikeViaServer
  57. from announce import announcePublic
  58. from announce import sendAnnounceViaServer
  59. from media import getMediaPath
  60. from delete import sendDeleteViaServer
  61. from inbox import validInbox
  62. from inbox import validInboxFilenames
  63. testServerAliceRunning = False
  64. testServerBobRunning = False
  65. testServerEveRunning = False
  66. def testHttpsigBase(withDigest):
  67. print('testHttpsig(' + str(withDigest) + ')')
  68. baseDir=os.getcwd()
  69. path=baseDir+'/.testHttpsigBase'
  70. if os.path.isdir(path):
  71. shutil.rmtree(path)
  72. os.mkdir(path)
  73. os.chdir(path)
  74. contentType='application/activity+json'
  75. nickname='socrates'
  76. domain='argumentative.social'
  77. httpPrefix='https'
  78. port=5576
  79. password='SuperSecretPassword'
  80. privateKeyPem,publicKeyPem,person,wfEndpoint= \
  81. createPerson(path,nickname,domain,port,httpPrefix,False,password)
  82. assert privateKeyPem
  83. messageBodyJson = {"a key": "a value", "another key": "A string","yet another key": "Another string"}
  84. messageBodyJsonStr=json.dumps(messageBodyJson)
  85. headersDomain=domain
  86. if port:
  87. if port!=80 and port !=443:
  88. if ':' not in domain:
  89. headersDomain=domain+':'+str(port)
  90. dateStr=strftime("%a, %d %b %Y %H:%M:%S %Z", gmtime())
  91. boxpath='/inbox'
  92. if not withDigest:
  93. headers = {'host': headersDomain,'date': dateStr,'content-type': 'application/json'}
  94. signatureHeader = \
  95. signPostHeaders(dateStr,privateKeyPem, nickname, \
  96. domain, port, \
  97. domain, port, \
  98. boxpath, httpPrefix, None)
  99. else:
  100. bodyDigest = messageContentDigest(messageBodyJsonStr)
  101. headers = {'host': headersDomain,'date': dateStr,'digest': f'SHA-256={bodyDigest}','content-type': contentType}
  102. signatureHeader = \
  103. signPostHeaders(dateStr,privateKeyPem, nickname, \
  104. domain, port, \
  105. domain, port, \
  106. boxpath, httpPrefix, messageBodyJsonStr)
  107. headers['signature'] = signatureHeader
  108. assert verifyPostHeaders(httpPrefix,publicKeyPem,headers, \
  109. boxpath,False,None, \
  110. messageBodyJsonStr)
  111. assert verifyPostHeaders(httpPrefix,publicKeyPem,headers, \
  112. '/parambulator'+boxpath,False,None, \
  113. messageBodyJsonStr) == False
  114. assert verifyPostHeaders(httpPrefix,publicKeyPem,headers, \
  115. boxpath,True,None, \
  116. messageBodyJsonStr) == False
  117. if not withDigest:
  118. # fake domain
  119. headers = {'host': 'bogon.domain','date': dateStr,'content-type': 'application/json'}
  120. else:
  121. # correct domain but fake message
  122. messageBodyJsonStr = '{"a key": "a value", "another key": "Fake GNUs", "yet another key": "More Fake GNUs"}'
  123. bodyDigest = messageContentDigest(messageBodyJsonStr)
  124. headers = {'host': domain,'date': dateStr,'digest': f'SHA-256={bodyDigest}','content-type': contentType}
  125. headers['signature'] = signatureHeader
  126. assert verifyPostHeaders(httpPrefix,publicKeyPem,headers, \
  127. boxpath,True,None, \
  128. messageBodyJsonStr) == False
  129. os.chdir(baseDir)
  130. shutil.rmtree(path)
  131. def testHttpsig():
  132. testHttpsigBase(True)
  133. testHttpsigBase(False)
  134. def testCache():
  135. print('testCache')
  136. personUrl="cat@cardboard.box"
  137. personJson={ "id": 123456, "test": "This is a test" }
  138. personCache={}
  139. storePersonInCache(personUrl,personJson,personCache)
  140. result=getPersonFromCache(personUrl,personCache)
  141. assert result['id']==123456
  142. assert result['test']=='This is a test'
  143. def testThreadsFunction(param: str):
  144. for i in range(10000):
  145. time.sleep(2)
  146. def testThreads():
  147. print('testThreads')
  148. thr = threadWithTrace(target=testThreadsFunction,args=('test',),daemon=True)
  149. thr.start()
  150. assert thr.isAlive()==True
  151. time.sleep(1)
  152. thr.kill()
  153. thr.join()
  154. assert thr.isAlive()==False
  155. def createServerAlice(path: str,domain: str,port: int,federationList: [], \
  156. hasFollows: bool,hasPosts :bool,ocapAlways: bool):
  157. print('Creating test server: Alice on port '+str(port))
  158. if os.path.isdir(path):
  159. shutil.rmtree(path)
  160. os.mkdir(path)
  161. os.chdir(path)
  162. nickname='alice'
  163. httpPrefix='http'
  164. useTor=False
  165. password='alicepass'
  166. noreply=False
  167. nolike=False
  168. nopics=False
  169. noannounce=False
  170. cw=False
  171. useBlurhash=True
  172. maxReplies=64
  173. domainMaxPostsPerDay=1000
  174. accountMaxPostsPerDay=1000
  175. allowDeletion=True
  176. privateKeyPem,publicKeyPem,person,wfEndpoint= \
  177. createPerson(path,nickname,domain,port,httpPrefix,True,password)
  178. deleteAllPosts(path,nickname,domain,'inbox')
  179. deleteAllPosts(path,nickname,domain,'outbox')
  180. assert setSkillLevel(path,nickname,domain,'hacking',90)
  181. assert setRole(path,nickname,domain,'someproject','guru')
  182. if hasFollows:
  183. followPerson(path,nickname,domain,'bob','127.0.0.100:61936', \
  184. federationList,False)
  185. followerOfPerson(path,nickname,domain,'bob','127.0.0.100:61936', \
  186. federationList,False)
  187. if hasPosts:
  188. createPublicPost(path,nickname, domain, port,httpPrefix, \
  189. "No wise fish would go anywhere without a porpoise", \
  190. False, True, clientToServer,None,None,useBlurhash)
  191. createPublicPost(path,nickname, domain, port,httpPrefix, \
  192. "Curiouser and curiouser!", False, True, \
  193. clientToServer,None,None,useBlurhash)
  194. createPublicPost(path,nickname, domain, port,httpPrefix, \
  195. "In the gardens of memory, in the palace of dreams, that is where you and I shall meet", \
  196. False, True, clientToServer,None,None,useBlurhash)
  197. global testServerAliceRunning
  198. testServerAliceRunning = True
  199. print('Server running: Alice')
  200. runDaemon(__version__,"instanceId",False,path,domain,port,port, \
  201. httpPrefix,federationList, \
  202. noreply,nolike,nopics,noannounce,cw,ocapAlways, \
  203. useTor,maxReplies, \
  204. domainMaxPostsPerDay,accountMaxPostsPerDay, \
  205. allowDeletion,True,True)
  206. def createServerBob(path: str,domain: str,port: int,federationList: [], \
  207. hasFollows: bool,hasPosts :bool,ocapAlways :bool):
  208. print('Creating test server: Bob on port '+str(port))
  209. if os.path.isdir(path):
  210. shutil.rmtree(path)
  211. os.mkdir(path)
  212. os.chdir(path)
  213. nickname='bob'
  214. httpPrefix='http'
  215. useTor=False
  216. clientToServer=False
  217. password='bobpass'
  218. noreply=False
  219. nolike=False
  220. nopics=False
  221. noannounce=False
  222. cw=False
  223. useBlurhash=False
  224. maxReplies=64
  225. domainMaxPostsPerDay=1000
  226. accountMaxPostsPerDay=1000
  227. allowDeletion=True
  228. privateKeyPem,publicKeyPem,person,wfEndpoint= \
  229. createPerson(path,nickname,domain,port,httpPrefix,True,password)
  230. deleteAllPosts(path,nickname,domain,'inbox')
  231. deleteAllPosts(path,nickname,domain,'outbox')
  232. assert setRole(path,nickname,domain,'bandname','bass player')
  233. assert setRole(path,nickname,domain,'bandname','publicist')
  234. if hasFollows:
  235. followPerson(path,nickname,domain, \
  236. 'alice','127.0.0.50:61935',federationList,False)
  237. followerOfPerson(path,nickname,domain, \
  238. 'alice','127.0.0.50:61935',federationList,False)
  239. if hasPosts:
  240. createPublicPost(path,nickname, domain, port,httpPrefix, \
  241. "It's your life, live it your way.", \
  242. False, True, clientToServer,None,None,useBlurhash)
  243. createPublicPost(path,nickname, domain, port,httpPrefix, \
  244. "One of the things I've realised is that I am very simple", \
  245. False, True, clientToServer,None,None,useBlurhash)
  246. createPublicPost(path,nickname, domain, port,httpPrefix, \
  247. "Quantum physics is a bit of a passion of mine", \
  248. False, True, clientToServer,None,None,useBlurhash)
  249. global testServerBobRunning
  250. testServerBobRunning = True
  251. print('Server running: Bob')
  252. runDaemon(__version__,"instanceId",False,path,domain,port,port, \
  253. httpPrefix,federationList, \
  254. noreply,nolike,nopics,noannounce,cw,ocapAlways, \
  255. useTor,maxReplies, \
  256. domainMaxPostsPerDay,accountMaxPostsPerDay, \
  257. allowDeletion,True,True)
  258. def createServerEve(path: str,domain: str,port: int,federationList: [], \
  259. hasFollows: bool,hasPosts :bool,ocapAlways :bool):
  260. print('Creating test server: Eve on port '+str(port))
  261. if os.path.isdir(path):
  262. shutil.rmtree(path)
  263. os.mkdir(path)
  264. os.chdir(path)
  265. nickname='eve'
  266. httpPrefix='http'
  267. useTor=False
  268. clientToServer=False
  269. password='evepass'
  270. noreply=False
  271. nolike=False
  272. nopics=False
  273. noannounce=False
  274. cw=False
  275. maxReplies=64
  276. allowDeletion=True
  277. privateKeyPem,publicKeyPem,person,wfEndpoint= \
  278. createPerson(path,nickname,domain,port,httpPrefix,True,password)
  279. deleteAllPosts(path,nickname,domain,'inbox')
  280. deleteAllPosts(path,nickname,domain,'outbox')
  281. global testServerEveRunning
  282. testServerEveRunning = True
  283. print('Server running: Eve')
  284. runDaemon(__version__,"instanceId",False,path,domain,port,port, \
  285. httpPrefix,federationList, \
  286. noreply,nolike,nopics,noannounce,cw,ocapAlways, \
  287. useTor,maxReplies,allowDeletion,True,True)
  288. def testPostMessageBetweenServers():
  289. print('Testing sending message from one server to the inbox of another')
  290. global testServerAliceRunning
  291. global testServerBobRunning
  292. testServerAliceRunning = False
  293. testServerBobRunning = False
  294. httpPrefix='http'
  295. useTor=False
  296. baseDir=os.getcwd()
  297. if os.path.isdir(baseDir+'/.tests'):
  298. shutil.rmtree(baseDir+'/.tests')
  299. os.mkdir(baseDir+'/.tests')
  300. ocapAlways=False
  301. # create the servers
  302. aliceDir=baseDir+'/.tests/alice'
  303. aliceDomain='127.0.0.50'
  304. alicePort=61935
  305. bobDir=baseDir+'/.tests/bob'
  306. bobDomain='127.0.0.100'
  307. bobPort=61936
  308. federationList=[bobDomain,aliceDomain]
  309. thrAlice = \
  310. threadWithTrace(target=createServerAlice, \
  311. args=(aliceDir,aliceDomain,alicePort, \
  312. federationList,False,False, \
  313. ocapAlways),daemon=True)
  314. thrBob = \
  315. threadWithTrace(target=createServerBob, \
  316. args=(bobDir,bobDomain,bobPort, \
  317. federationList,False,False, \
  318. ocapAlways),daemon=True)
  319. thrAlice.start()
  320. thrBob.start()
  321. assert thrAlice.isAlive()==True
  322. assert thrBob.isAlive()==True
  323. # wait for both servers to be running
  324. while not (testServerAliceRunning and testServerBobRunning):
  325. time.sleep(1)
  326. time.sleep(1)
  327. print('\n\n*******************************************************')
  328. print('Alice sends to Bob')
  329. os.chdir(aliceDir)
  330. sessionAlice = createSession(aliceDomain,alicePort,useTor)
  331. inReplyTo=None
  332. inReplyToAtomUri=None
  333. subject=None
  334. aliceSendThreads = []
  335. alicePostLog = []
  336. followersOnly=False
  337. saveToFile=True
  338. clientToServer=False
  339. ccUrl=None
  340. alicePersonCache={}
  341. aliceCachedWebfingers={}
  342. attachedImageFilename=baseDir+'/img/logo.png'
  343. attachedImageDescription='Logo'
  344. useBlurhash=True
  345. # nothing in Alice's outbox
  346. outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox'
  347. assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==0
  348. sendResult = \
  349. sendPost(__version__, \
  350. sessionAlice,aliceDir,'alice', aliceDomain, alicePort, \
  351. 'bob', bobDomain, bobPort, ccUrl, httpPrefix, \
  352. 'Why is a mouse when it spins? #sillyquestion', followersOnly, \
  353. saveToFile, clientToServer,attachedImageFilename, \
  354. attachedImageDescription,useBlurhash, federationList, \
  355. aliceSendThreads, alicePostLog, aliceCachedWebfingers, \
  356. alicePersonCache,inReplyTo, inReplyToAtomUri, subject)
  357. print('sendResult: '+str(sendResult))
  358. queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
  359. inboxPath=bobDir+'/accounts/bob@'+bobDomain+'/inbox'
  360. mPath=getMediaPath()
  361. mediaPath=aliceDir+'/'+mPath
  362. for i in range(30):
  363. if os.path.isdir(inboxPath):
  364. if len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])>0:
  365. if len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==1:
  366. if len([name for name in os.listdir(mediaPath) if os.path.isfile(os.path.join(mediaPath, name))])>0:
  367. break
  368. time.sleep(1)
  369. # Image attachment created
  370. assert len([name for name in os.listdir(mediaPath) if os.path.isfile(os.path.join(mediaPath, name))])>0
  371. # inbox item created
  372. assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1
  373. # queue item removed
  374. assert len([name for name in os.listdir(queuePath) if os.path.isfile(os.path.join(queuePath, name))])==0
  375. assert validInbox(bobDir,'bob',bobDomain)
  376. assert validInboxFilenames(bobDir,'bob',bobDomain,aliceDomain,alicePort)
  377. print('\n\n*******************************************************')
  378. print("Bob likes Alice's post")
  379. followerOfPerson(bobDir,'bob',bobDomain,'alice', \
  380. aliceDomain+':'+str(alicePort),federationList,False)
  381. followPerson(aliceDir,'alice',aliceDomain,'bob', \
  382. bobDomain+':'+str(bobPort),federationList,False)
  383. sessionBob = createSession(bobDomain,bobPort,useTor)
  384. bobSendThreads = []
  385. bobPostLog = []
  386. bobPersonCache={}
  387. bobCachedWebfingers={}
  388. statusNumber=None
  389. outboxPostFilename=None
  390. outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox'
  391. for name in os.listdir(outboxPath):
  392. if '#statuses#' in name:
  393. statusNumber=int(name.split('#statuses#')[1].replace('.json',''))
  394. outboxPostFilename=outboxPath+'/'+name
  395. assert statusNumber>0
  396. assert outboxPostFilename
  397. assert likePost(sessionBob,bobDir,federationList, \
  398. 'bob',bobDomain,bobPort,httpPrefix, \
  399. 'alice',aliceDomain,alicePort,[], \
  400. statusNumber,False,bobSendThreads,bobPostLog, \
  401. bobPersonCache,bobCachedWebfingers, \
  402. True,__version__)
  403. for i in range(20):
  404. if 'likes' in open(outboxPostFilename).read():
  405. break
  406. time.sleep(1)
  407. with open(outboxPostFilename, 'r') as fp:
  408. alicePostJson=commentjson.load(fp)
  409. pprint(alicePostJson)
  410. assert 'likes' in open(outboxPostFilename).read()
  411. print('\n\n*******************************************************')
  412. print("Bob repeats Alice's post")
  413. objectUrl=httpPrefix+'://'+aliceDomain+':'+str(alicePort)+'/users/alice/statuses/'+str(statusNumber)
  414. inboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/inbox'
  415. outboxPath=bobDir+'/accounts/bob@'+bobDomain+'/outbox'
  416. outboxBeforeAnnounceCount=len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])
  417. beforeAnnounceCount=len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])
  418. assert beforeAnnounceCount==0
  419. print('inbox items before announce: '+str(beforeAnnounceCount))
  420. announcePublic(sessionBob,bobDir,federationList, \
  421. 'bob',bobDomain,bobPort,httpPrefix, \
  422. objectUrl, \
  423. False,bobSendThreads,bobPostLog, \
  424. bobPersonCache,bobCachedWebfingers, \
  425. True,__version__)
  426. announceMessageArrived=False
  427. for i in range(10):
  428. time.sleep(1)
  429. if os.path.isdir(inboxPath):
  430. if len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])>0:
  431. announceMessageArrived=True
  432. print('Announce message sent to Alice!')
  433. break
  434. afterAnnounceCount=len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])
  435. outboxAfterAnnounceCount=len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])
  436. print('inbox items after announce: '+str(afterAnnounceCount))
  437. assert afterAnnounceCount==beforeAnnounceCount+1
  438. assert outboxAfterAnnounceCount==outboxBeforeAnnounceCount+1
  439. # stop the servers
  440. thrAlice.kill()
  441. thrAlice.join()
  442. assert thrAlice.isAlive()==False
  443. thrBob.kill()
  444. thrBob.join()
  445. assert thrBob.isAlive()==False
  446. os.chdir(baseDir)
  447. shutil.rmtree(aliceDir)
  448. shutil.rmtree(bobDir)
  449. def testFollowBetweenServers():
  450. print('Testing sending a follow request from one server to another')
  451. global testServerAliceRunning
  452. global testServerBobRunning
  453. global testServerEveRunning
  454. testServerAliceRunning = False
  455. testServerBobRunning = False
  456. testServerEveRunning = False
  457. httpPrefix='http'
  458. useTor=False
  459. federationList=[]
  460. baseDir=os.getcwd()
  461. if os.path.isdir(baseDir+'/.tests'):
  462. shutil.rmtree(baseDir+'/.tests')
  463. os.mkdir(baseDir+'/.tests')
  464. ocapAlways=True
  465. # create the servers
  466. aliceDir=baseDir+'/.tests/alice'
  467. aliceDomain='127.0.0.42'
  468. alicePort=61935
  469. thrAlice = \
  470. threadWithTrace(target=createServerAlice, \
  471. args=(aliceDir,aliceDomain,alicePort, \
  472. federationList,False,False, \
  473. ocapAlways),daemon=True)
  474. bobDir=baseDir+'/.tests/bob'
  475. bobDomain='127.0.0.64'
  476. bobPort=61936
  477. thrBob = \
  478. threadWithTrace(target=createServerBob, \
  479. args=(bobDir,bobDomain,bobPort, \
  480. federationList,False,False, \
  481. ocapAlways),daemon=True)
  482. eveDir=baseDir+'/.tests/eve'
  483. eveDomain='127.0.0.55'
  484. evePort=61937
  485. thrEve = \
  486. threadWithTrace(target=createServerEve, \
  487. args=(eveDir,eveDomain,evePort, \
  488. federationList,False,False, \
  489. False),daemon=True)
  490. thrAlice.start()
  491. thrBob.start()
  492. thrEve.start()
  493. assert thrAlice.isAlive()==True
  494. assert thrBob.isAlive()==True
  495. assert thrEve.isAlive()==True
  496. # wait for all servers to be running
  497. ctr=0
  498. while not (testServerAliceRunning and testServerBobRunning and testServerEveRunning):
  499. time.sleep(1)
  500. ctr+=1
  501. if ctr>60:
  502. break
  503. print('Alice online: '+str(testServerAliceRunning))
  504. print('Bob online: '+str(testServerBobRunning))
  505. print('Eve online: '+str(testServerEveRunning))
  506. assert ctr<=60
  507. time.sleep(1)
  508. # In the beginning all was calm and there were no follows
  509. print('*********************************************************')
  510. print('Alice sends a follow request to Bob')
  511. print('Both are strictly enforcing object capabilities')
  512. os.chdir(aliceDir)
  513. sessionAlice = createSession(aliceDomain,alicePort,useTor)
  514. inReplyTo=None
  515. inReplyToAtomUri=None
  516. subject=None
  517. aliceSendThreads = []
  518. alicePostLog = []
  519. followersOnly=False
  520. saveToFile=True
  521. clientToServer=False
  522. ccUrl=None
  523. alicePersonCache={}
  524. aliceCachedWebfingers={}
  525. aliceSendThreads=[]
  526. alicePostLog=[]
  527. sendResult = \
  528. sendFollowRequest(sessionAlice,aliceDir, \
  529. 'alice',aliceDomain,alicePort,httpPrefix, \
  530. 'bob',bobDomain,bobPort,httpPrefix, \
  531. clientToServer,federationList, \
  532. aliceSendThreads,alicePostLog, \
  533. aliceCachedWebfingers,alicePersonCache, \
  534. True,__version__)
  535. print('sendResult: '+str(sendResult))
  536. bobCapsFilename=bobDir+'/accounts/bob@'+bobDomain+'/ocap/accept/'+httpPrefix+':##'+aliceDomain+':'+str(alicePort)+'#users#alice.json'
  537. aliceCapsFilename=aliceDir+'/accounts/alice@'+aliceDomain+'/ocap/granted/'+httpPrefix+':##'+bobDomain+':'+str(bobPort)+'#users#bob.json'
  538. for t in range(10):
  539. if os.path.isfile(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt'):
  540. if os.path.isfile(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt'):
  541. if os.path.isfile(bobCapsFilename):
  542. if os.path.isfile(aliceCapsFilename):
  543. break
  544. time.sleep(1)
  545. with open(bobCapsFilename, 'r') as fp:
  546. bobCapsJson=commentjson.load(fp)
  547. if not bobCapsJson.get('capability'):
  548. print("Unexpected format for Bob's capabilities")
  549. pprint(bobCapsJson)
  550. assert False
  551. assert validInbox(bobDir,'bob',bobDomain)
  552. assert validInboxFilenames(bobDir,'bob',bobDomain,aliceDomain,alicePort)
  553. print('\n\n*********************************************************')
  554. print('Eve tries to send to Bob')
  555. sessionEve = createSession(eveDomain,evePort,useTor)
  556. eveSendThreads = []
  557. evePostLog = []
  558. evePersonCache={}
  559. eveCachedWebfingers={}
  560. eveSendThreads=[]
  561. evePostLog=[]
  562. useBlurhash=False
  563. sendResult = \
  564. sendPost(__version__, \
  565. sessionEve,eveDir,'eve', eveDomain, evePort, \
  566. 'bob', bobDomain, bobPort, ccUrl, \
  567. httpPrefix, 'Eve message', followersOnly, \
  568. saveToFile, clientToServer,None,None, \
  569. useBlurhash, federationList, eveSendThreads, \
  570. evePostLog, eveCachedWebfingers, \
  571. evePersonCache,inReplyTo, inReplyToAtomUri, subject)
  572. print('sendResult: '+str(sendResult))
  573. queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
  574. inboxPath=bobDir+'/accounts/bob@'+bobDomain+'/inbox'
  575. eveMessageArrived=False
  576. for i in range(10):
  577. time.sleep(1)
  578. if os.path.isdir(inboxPath):
  579. if len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])>1:
  580. eveMessageArrived=True
  581. print('Eve message sent to Bob!')
  582. break
  583. # capabilities should have prevented delivery
  584. assert eveMessageArrived==False
  585. print('Message from Eve to Bob was correctly rejected by object capabilities')
  586. print('\n\n*********************************************************')
  587. print('Alice sends a message to Bob')
  588. aliceSendThreads = []
  589. alicePostLog = []
  590. alicePersonCache={}
  591. aliceCachedWebfingers={}
  592. aliceSendThreads=[]
  593. alicePostLog=[]
  594. useBlurhash=False
  595. sendResult = \
  596. sendPost(__version__, \
  597. sessionAlice,aliceDir,'alice', aliceDomain, alicePort, \
  598. 'bob', bobDomain, bobPort, ccUrl, \
  599. httpPrefix, 'Alice message', followersOnly, saveToFile, \
  600. clientToServer,None,None,useBlurhash, federationList, \
  601. aliceSendThreads, alicePostLog, aliceCachedWebfingers, \
  602. alicePersonCache,inReplyTo, inReplyToAtomUri, subject)
  603. print('sendResult: '+str(sendResult))
  604. queuePath=bobDir+'/accounts/bob@'+bobDomain+'/queue'
  605. inboxPath=bobDir+'/accounts/bob@'+bobDomain+'/inbox'
  606. aliceMessageArrived=False
  607. for i in range(20):
  608. time.sleep(1)
  609. if os.path.isdir(inboxPath):
  610. if len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])>0:
  611. aliceMessageArrived=True
  612. print('Alice message sent to Bob!')
  613. break
  614. assert aliceMessageArrived==True
  615. print('Message from Alice to Bob succeeded, since it was granted capabilities')
  616. print('\n\n*********************************************************')
  617. print("\nBob changes Alice's capabilities so that she can't reply on his posts")
  618. bobCapsFilename= \
  619. bobDir+'/accounts/bob@'+bobDomain+'/ocap/accept/'+ \
  620. httpPrefix+':##'+aliceDomain+':'+str(alicePort)+'#users#alice.json'
  621. aliceCapsFilename= \
  622. aliceDir+'/accounts/alice@'+aliceDomain+'/ocap/granted/'+ \
  623. httpPrefix+':##'+bobDomain+':'+str(bobPort)+'#users#bob.json'
  624. sessionBob = createSession(bobDomain,bobPort,useTor)
  625. bobSendThreads = []
  626. bobPostLog = []
  627. bobPersonCache={}
  628. bobCachedWebfingers={}
  629. print("Bob's capabilities for Alice:")
  630. with open(bobCapsFilename, 'r') as fp:
  631. bobCapsJson=commentjson.load(fp)
  632. pprint(bobCapsJson)
  633. assert "inbox:noreply" not in bobCapsJson['capability']
  634. print("Alice's capabilities granted by Bob")
  635. with open(aliceCapsFilename, 'r') as fp:
  636. aliceCapsJson=commentjson.load(fp)
  637. pprint(aliceCapsJson)
  638. assert "inbox:noreply" not in aliceCapsJson['capability']
  639. newCapabilities=["inbox:write","objects:read","inbox:noreply"]
  640. sendCapabilitiesUpdate(sessionBob,bobDir,httpPrefix, \
  641. 'bob',bobDomain,bobPort, \
  642. httpPrefix+'://'+aliceDomain+':'+\
  643. str(alicePort)+'/users/alice',
  644. newCapabilities, \
  645. bobSendThreads, bobPostLog, \
  646. bobCachedWebfingers,bobPersonCache, \
  647. federationList,True,__version__)
  648. bobChanged=False
  649. bobNewCapsJson=None
  650. for i in range(20):
  651. time.sleep(1)
  652. with open(bobCapsFilename, 'r') as fp:
  653. bobNewCapsJson=commentjson.load(fp)
  654. if "inbox:noreply" in bobNewCapsJson['capability']:
  655. print("Bob's capabilities were changed")
  656. pprint(bobNewCapsJson)
  657. bobChanged=True
  658. break
  659. assert bobChanged
  660. aliceChanged=False
  661. aliceNewCapsJson=None
  662. for i in range(20):
  663. time.sleep(1)
  664. with open(aliceCapsFilename, 'r') as fp:
  665. aliceNewCapsJson=commentjson.load(fp)
  666. if "inbox:noreply" in aliceNewCapsJson['capability']:
  667. print("Alice's granted capabilities were changed")
  668. pprint(aliceNewCapsJson)
  669. aliceChanged=True
  670. break
  671. assert aliceChanged
  672. # check that the capabilities id has changed
  673. assert bobNewCapsJson['id']!=bobCapsJson['id']
  674. assert aliceNewCapsJson['id']!=aliceCapsJson['id']
  675. # stop the servers
  676. thrAlice.kill()
  677. thrAlice.join()
  678. assert thrAlice.isAlive()==False
  679. thrBob.kill()
  680. thrBob.join()
  681. assert thrBob.isAlive()==False
  682. thrEve.kill()
  683. thrEve.join()
  684. assert thrEve.isAlive()==False
  685. assert os.path.isfile(bobDir+'/accounts/bob@'+bobDomain+ \
  686. '/ocap/accept/'+httpPrefix+':##'+ \
  687. aliceDomain+':'+str(alicePort)+ \
  688. '#users#alice.json')
  689. assert os.path.isfile(aliceDir+'/accounts/alice@'+ \
  690. aliceDomain+'/ocap/granted/'+ \
  691. httpPrefix+':##'+bobDomain+':'+ \
  692. str(bobPort)+'#users#bob.json')
  693. assert 'alice@'+aliceDomain in open(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt').read()
  694. assert 'bob@'+bobDomain in open(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt').read()
  695. # queue item removed
  696. assert len([name for name in os.listdir(queuePath) if os.path.isfile(os.path.join(queuePath, name))])==0
  697. os.chdir(baseDir)
  698. shutil.rmtree(baseDir+'/.tests')
  699. def testFollowersOfPerson():
  700. print('testFollowersOfPerson')
  701. currDir=os.getcwd()
  702. nickname='mxpop'
  703. domain='diva.domain'
  704. password='birb'
  705. port=80
  706. httpPrefix='https'
  707. federationList=[]
  708. baseDir=currDir+'/.tests_followersofperson'
  709. if os.path.isdir(baseDir):
  710. shutil.rmtree(baseDir)
  711. os.mkdir(baseDir)
  712. os.chdir(baseDir)
  713. createPerson(baseDir,nickname,domain,port,httpPrefix,True,password)
  714. createPerson(baseDir,'maxboardroom',domain,port,httpPrefix,True,password)
  715. createPerson(baseDir,'ultrapancake',domain,port,httpPrefix,True,password)
  716. createPerson(baseDir,'drokk',domain,port,httpPrefix,True,password)
  717. createPerson(baseDir,'sausagedog',domain,port,httpPrefix,True,password)
  718. clearFollows(baseDir,nickname,domain)
  719. followPerson(baseDir,nickname,domain,'maxboardroom',domain,federationList,False)
  720. followPerson(baseDir,'drokk',domain,'ultrapancake',domain,federationList,False)
  721. # deliberate duplication
  722. followPerson(baseDir,'drokk',domain,'ultrapancake',domain,federationList,False)
  723. followPerson(baseDir,'sausagedog',domain,'ultrapancake',domain,federationList,False)
  724. followPerson(baseDir,nickname,domain,'ultrapancake',domain,federationList,False)
  725. followPerson(baseDir,nickname,domain,'someother','randodomain.net',federationList,False)
  726. followList=getFollowersOfPerson(baseDir,'ultrapancake',domain)
  727. assert len(followList)==3
  728. assert 'mxpop@'+domain in followList
  729. assert 'drokk@'+domain in followList
  730. assert 'sausagedog@'+domain in followList
  731. os.chdir(currDir)
  732. shutil.rmtree(baseDir)
  733. def testNoOfFollowersOnDomain():
  734. print('testNoOfFollowersOnDomain')
  735. currDir=os.getcwd()
  736. nickname='mxpop'
  737. domain='diva.domain'
  738. otherdomain='soup.dragon'
  739. password='birb'
  740. port=80
  741. httpPrefix='https'
  742. federationList=[]
  743. baseDir=currDir+'/.tests_nooffollowersOndomain'
  744. if os.path.isdir(baseDir):
  745. shutil.rmtree(baseDir)
  746. os.mkdir(baseDir)
  747. os.chdir(baseDir)
  748. createPerson(baseDir,nickname,domain,port,httpPrefix,True,password)
  749. createPerson(baseDir,'maxboardroom',otherdomain,port,httpPrefix,True,password)
  750. createPerson(baseDir,'ultrapancake',otherdomain,port,httpPrefix,True,password)
  751. createPerson(baseDir,'drokk',otherdomain,port,httpPrefix,True,password)
  752. createPerson(baseDir,'sausagedog',otherdomain,port,httpPrefix,True,password)
  753. followPerson(baseDir,'drokk',otherdomain,nickname,domain,federationList,False)
  754. followPerson(baseDir,'sausagedog',otherdomain,nickname,domain,federationList,False)
  755. followPerson(baseDir,'maxboardroom',otherdomain,nickname,domain,federationList,False)
  756. followerOfPerson(baseDir,nickname,domain,'cucumber','sandwiches.party',federationList,False)
  757. followerOfPerson(baseDir,nickname,domain,'captainsensible','damned.zone',federationList,False)
  758. followerOfPerson(baseDir,nickname,domain,'pilchard','zombies.attack',federationList,False)
  759. followerOfPerson(baseDir,nickname,domain,'drokk',otherdomain,federationList,False)
  760. followerOfPerson(baseDir,nickname,domain,'sausagedog',otherdomain,federationList,False)
  761. followerOfPerson(baseDir,nickname,domain,'maxboardroom',otherdomain,federationList,False)
  762. followersOnOtherDomain=noOfFollowersOnDomain(baseDir,nickname+'@'+domain, otherdomain)
  763. assert followersOnOtherDomain==3
  764. unfollowerOfPerson(baseDir,nickname,domain,'sausagedog',otherdomain)
  765. followersOnOtherDomain=noOfFollowersOnDomain(baseDir,nickname+'@'+domain, otherdomain)
  766. assert followersOnOtherDomain==2
  767. os.chdir(currDir)
  768. shutil.rmtree(baseDir)
  769. def testGroupFollowers():
  770. print('testGroupFollowers')
  771. currDir=os.getcwd()
  772. nickname='test735'
  773. domain='mydomain.com'
  774. password='somepass'
  775. port=80
  776. httpPrefix='https'
  777. federationList=[]
  778. baseDir=currDir+'/.tests_testgroupfollowers'
  779. if os.path.isdir(baseDir):
  780. shutil.rmtree(baseDir)
  781. os.mkdir(baseDir)
  782. os.chdir(baseDir)
  783. createPerson(baseDir,nickname,domain,port,httpPrefix,True,password)
  784. clearFollowers(baseDir,nickname,domain)
  785. followerOfPerson(baseDir,nickname,domain,'badger','wild.domain',federationList,False)
  786. followerOfPerson(baseDir,nickname,domain,'squirrel','wild.domain',federationList,False)
  787. followerOfPerson(baseDir,nickname,domain,'rodent','wild.domain',federationList,False)
  788. followerOfPerson(baseDir,nickname,domain,'utterly','clutterly.domain',federationList,False)
  789. followerOfPerson(baseDir,nickname,domain,'zonked','zzz.domain',federationList,False)
  790. followerOfPerson(baseDir,nickname,domain,'nap','zzz.domain',federationList,False)
  791. grouped=groupFollowersByDomain(baseDir,nickname,domain)
  792. assert len(grouped.items())==3
  793. assert grouped.get('zzz.domain')
  794. assert grouped.get('clutterly.domain')
  795. assert grouped.get('wild.domain')
  796. assert len(grouped['zzz.domain'])==2
  797. assert len(grouped['wild.domain'])==3
  798. assert len(grouped['clutterly.domain'])==1
  799. os.chdir(currDir)
  800. shutil.rmtree(baseDir)
  801. def testFollows():
  802. print('testFollows')
  803. currDir=os.getcwd()
  804. nickname='test529'
  805. domain='testdomain.com'
  806. password='mypass'
  807. port=80
  808. httpPrefix='https'
  809. federationList=['wild.com','mesh.com']
  810. baseDir=currDir+'/.tests_testfollows'
  811. if os.path.isdir(baseDir):
  812. shutil.rmtree(baseDir)
  813. os.mkdir(baseDir)
  814. os.chdir(baseDir)
  815. createPerson(baseDir,nickname,domain,port,httpPrefix,True,password)
  816. clearFollows(baseDir,nickname,domain)
  817. followPerson(baseDir,nickname,domain,'badger','wild.com',federationList,False)
  818. followPerson(baseDir,nickname,domain,'squirrel','secret.com',federationList,False)
  819. followPerson(baseDir,nickname,domain,'rodent','drainpipe.com',federationList,False)
  820. followPerson(baseDir,nickname,domain,'batman','mesh.com',federationList,False)
  821. followPerson(baseDir,nickname,domain,'giraffe','trees.com',federationList,False)
  822. f = open(baseDir+'/accounts/'+nickname+'@'+domain+'/following.txt', "r")
  823. domainFound=False
  824. for followingDomain in f:
  825. testDomain=followingDomain.split('@')[1].replace('\n','')
  826. if testDomain=='mesh.com':
  827. domainFound=True
  828. if testDomain not in federationList:
  829. print(testDomain)
  830. assert(False)
  831. assert(domainFound)
  832. unfollowPerson(baseDir,nickname,domain,'batman','mesh.com')
  833. domainFound=False
  834. for followingDomain in f:
  835. testDomain=followingDomain.split('@')[1].replace('\n','')
  836. if testDomain=='mesh.com':
  837. domainFound=True
  838. assert(domainFound==False)
  839. clearFollowers(baseDir,nickname,domain)
  840. followerOfPerson(baseDir,nickname,domain,'badger','wild.com',federationList,False)
  841. followerOfPerson(baseDir,nickname,domain,'squirrel','secret.com',federationList,False)
  842. followerOfPerson(baseDir,nickname,domain,'rodent','drainpipe.com',federationList,False)
  843. followerOfPerson(baseDir,nickname,domain,'batman','mesh.com',federationList,False)
  844. followerOfPerson(baseDir,nickname,domain,'giraffe','trees.com',federationList,False)
  845. f = open(baseDir+'/accounts/'+nickname+'@'+domain+'/followers.txt', "r")
  846. for followerDomain in f:
  847. testDomain=followerDomain.split('@')[1].replace('\n','')
  848. if testDomain not in federationList:
  849. print(testDomain)
  850. assert(False)
  851. os.chdir(currDir)
  852. shutil.rmtree(baseDir)
  853. def testCreatePerson():
  854. print('testCreatePerson')
  855. currDir=os.getcwd()
  856. nickname='test382'
  857. domain='badgerdomain.com'
  858. password='mypass'
  859. port=80
  860. httpPrefix='https'
  861. clientToServer=False
  862. useBlurhash=False
  863. baseDir=currDir+'/.tests_createperson'
  864. if os.path.isdir(baseDir):
  865. shutil.rmtree(baseDir)
  866. os.mkdir(baseDir)
  867. os.chdir(baseDir)
  868. privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,httpPrefix,True,password)
  869. assert os.path.isfile(baseDir+'/accounts/passwords')
  870. deleteAllPosts(baseDir,nickname,domain,'inbox')
  871. deleteAllPosts(baseDir,nickname,domain,'outbox')
  872. setPreferredNickname(baseDir,nickname,domain,'badger')
  873. setBio(baseDir,nickname,domain,'Randomly roaming in your backyard')
  874. archivePostsForPerson(nickname,domain,baseDir,'inbox',None,4)
  875. archivePostsForPerson(nickname,domain,baseDir,'outbox',None,4)
  876. createPublicPost(baseDir,nickname, domain, port,httpPrefix, "G'day world!", False, True, clientToServer,None,None,useBlurhash, None, None, 'Not suitable for Vogons')
  877. os.chdir(currDir)
  878. shutil.rmtree(baseDir)
  879. def testDelegateRoles():
  880. print('testDelegateRoles')
  881. currDir=os.getcwd()
  882. nickname='test382'
  883. nicknameDelegated='test383'
  884. domain='badgerdomain.com'
  885. password='mypass'
  886. port=80
  887. httpPrefix='https'
  888. clientToServer=False
  889. useBlurhash=False
  890. baseDir=currDir+'/.tests_delegaterole'
  891. if os.path.isdir(baseDir):
  892. shutil.rmtree(baseDir)
  893. os.mkdir(baseDir)
  894. os.chdir(baseDir)
  895. privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,httpPrefix,True,password)
  896. privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nicknameDelegated,domain,port,httpPrefix,True,'insecure')
  897. httpPrefix='http'
  898. project='artechoke'
  899. role='delegator'
  900. newRoleJson = {
  901. 'type': 'Delegate',
  902. 'actor': httpPrefix+'://'+domain+'/users/'+nickname,
  903. 'object': {
  904. 'type': 'Role',
  905. 'actor': httpPrefix+'://'+domain+'/users/'+nicknameDelegated,
  906. 'object': project+';'+role,
  907. 'to': [],
  908. 'cc': []
  909. },
  910. 'to': [],
  911. 'cc': []
  912. }
  913. assert outboxDelegate(baseDir,nickname,newRoleJson,False)
  914. # second time delegation has already happened so should return false
  915. assert outboxDelegate(baseDir,nickname,newRoleJson,False)==False
  916. assert '"delegator"' in open(baseDir+'/accounts/'+nickname+'@'+domain+'.json').read()
  917. assert '"delegator"' in open(baseDir+'/accounts/'+nicknameDelegated+'@'+domain+'.json').read()
  918. newRoleJson = {
  919. 'type': 'Delegate',
  920. 'actor': httpPrefix+'://'+domain+'/users/'+nicknameDelegated,
  921. 'object': {
  922. 'type': 'Role',
  923. 'actor': httpPrefix+'://'+domain+'/users/'+nickname,
  924. 'object': 'otherproject;otherrole',
  925. 'to': [],
  926. 'cc': []
  927. },
  928. 'to': [],
  929. 'cc': []
  930. }
  931. # non-delegators cannot assign roles
  932. assert outboxDelegate(baseDir,nicknameDelegated,newRoleJson,False)==False
  933. assert '"otherrole"' not in open(baseDir+'/accounts/'+nickname+'@'+domain+'.json').read()
  934. os.chdir(currDir)
  935. shutil.rmtree(baseDir)
  936. def testAuthentication():
  937. print('testAuthentication')
  938. currDir=os.getcwd()
  939. nickname='test8743'
  940. password='SuperSecretPassword12345'
  941. baseDir=currDir+'/.tests_authentication'
  942. if os.path.isdir(baseDir):
  943. shutil.rmtree(baseDir)
  944. os.mkdir(baseDir)
  945. os.chdir(baseDir)
  946. assert storeBasicCredentials(baseDir,'othernick','otherpass')
  947. assert storeBasicCredentials(baseDir,'bad:nick','otherpass')==False
  948. assert storeBasicCredentials(baseDir,'badnick','otherpa:ss')==False
  949. assert storeBasicCredentials(baseDir,nickname,password)
  950. authHeader=createBasicAuthHeader(nickname,password)
  951. assert authorizeBasic(baseDir,'/users/'+nickname+'/inbox',authHeader,False)
  952. assert authorizeBasic(baseDir,'/users/'+nickname,authHeader,False)==False
  953. assert authorizeBasic(baseDir,'/users/othernick/inbox',authHeader,False)==False
  954. authHeader=createBasicAuthHeader(nickname,password+'1')
  955. assert authorizeBasic(baseDir,'/users/'+nickname+'/inbox',authHeader,False)==False
  956. password='someOtherPassword'
  957. assert storeBasicCredentials(baseDir,nickname,password)
  958. authHeader=createBasicAuthHeader(nickname,password)
  959. assert authorizeBasic(baseDir,'/users/'+nickname+'/inbox',authHeader,False)
  960. os.chdir(currDir)
  961. shutil.rmtree(baseDir)
  962. def testClientToServer():
  963. print('Testing sending a post via c2s')
  964. global testServerAliceRunning
  965. global testServerBobRunning
  966. testServerAliceRunning = False
  967. testServerBobRunning = False
  968. httpPrefix='http'
  969. useTor=False
  970. federationList=[]
  971. baseDir=os.getcwd()
  972. if os.path.isdir(baseDir+'/.tests'):
  973. shutil.rmtree(baseDir+'/.tests')
  974. os.mkdir(baseDir+'/.tests')
  975. ocapAlways=False
  976. # create the servers
  977. aliceDir=baseDir+'/.tests/alice'
  978. aliceDomain='127.0.0.42'
  979. alicePort=61935
  980. thrAlice = \
  981. threadWithTrace(target=createServerAlice, \
  982. args=(aliceDir,aliceDomain,alicePort, \
  983. federationList,False,False, \
  984. ocapAlways),daemon=True)
  985. bobDir=baseDir+'/.tests/bob'
  986. bobDomain='127.0.0.64'
  987. bobPort=61936
  988. thrBob = \
  989. threadWithTrace(target=createServerBob, \
  990. args=(bobDir,bobDomain,bobPort, \
  991. federationList,False,False, \
  992. ocapAlways),daemon=True)
  993. thrAlice.start()
  994. thrBob.start()
  995. assert thrAlice.isAlive()==True
  996. assert thrBob.isAlive()==True
  997. # wait for both servers to be running
  998. ctr=0
  999. while not (testServerAliceRunning and testServerBobRunning):
  1000. time.sleep(1)
  1001. ctr+=1
  1002. if ctr>60:
  1003. break
  1004. print('Alice online: '+str(testServerAliceRunning))
  1005. print('Bob online: '+str(testServerBobRunning))
  1006. time.sleep(1)
  1007. print('\n\n*******************************************************')
  1008. print('Alice sends to Bob via c2s')
  1009. sessionAlice = createSession(aliceDomain,alicePort,useTor)
  1010. followersOnly=False
  1011. attachedImageFilename=baseDir+'/img/logo.png'
  1012. attachedImageDescription='Logo'
  1013. useBlurhash=False
  1014. cachedWebfingers={}
  1015. personCache={}
  1016. password='alicepass'
  1017. outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox'
  1018. inboxPath=bobDir+'/accounts/bob@'+bobDomain+'/inbox'
  1019. assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==0
  1020. assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==0
  1021. sendResult= \
  1022. sendPostViaServer(__version__, \
  1023. aliceDir,sessionAlice,'alice',password, \
  1024. aliceDomain,alicePort, \
  1025. 'bob',bobDomain,bobPort,None, \
  1026. httpPrefix,'Sent from my ActivityPub client',followersOnly, \
  1027. attachedImageFilename,attachedImageDescription,useBlurhash, \
  1028. cachedWebfingers,personCache, \
  1029. True,None,None,None)
  1030. print('sendResult: '+str(sendResult))
  1031. for i in range(30):
  1032. if os.path.isdir(outboxPath):
  1033. if len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==1:
  1034. break
  1035. time.sleep(1)
  1036. assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==1
  1037. print(">>> c2s post arrived in Alice's outbox")
  1038. for i in range(30):
  1039. if os.path.isdir(inboxPath):
  1040. if len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1:
  1041. break
  1042. time.sleep(1)
  1043. assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1
  1044. print(">>> s2s post arrived in Bob's inbox")
  1045. print("c2s send success")
  1046. print('\n\nGetting message id for the post')
  1047. statusNumber=0
  1048. outboxPostFilename=None
  1049. outboxPostId=None
  1050. for name in os.listdir(outboxPath):
  1051. if '#statuses#' in name:
  1052. statusNumber=int(name.split('#statuses#')[1].replace('.json','').replace('#activity',''))
  1053. outboxPostFilename=outboxPath+'/'+name
  1054. with open(outboxPostFilename, 'r') as fp:
  1055. postJsonObject=commentjson.load(fp)
  1056. outboxPostId=postJsonObject['id'].replace('/activity','')
  1057. assert outboxPostId
  1058. print('message id obtained: '+outboxPostId)
  1059. assert validInbox(bobDir,'bob',bobDomain)
  1060. assert validInboxFilenames(bobDir,'bob',bobDomain,aliceDomain,alicePort)
  1061. print('\n\nAlice follows Bob')
  1062. sendFollowRequestViaServer(sessionAlice,'alice',password, \
  1063. aliceDomain,alicePort, \
  1064. 'bob',bobDomain,bobPort, \
  1065. httpPrefix, \
  1066. cachedWebfingers,personCache, \
  1067. True,__version__)
  1068. for t in range(10):
  1069. if os.path.isfile(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt'):
  1070. if 'alice@'+aliceDomain+':'+str(alicePort) in open(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt').read():
  1071. if os.path.isfile(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt'):
  1072. if 'bob@'+bobDomain+':'+str(bobPort) in open(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt').read():
  1073. break
  1074. time.sleep(1)
  1075. assert os.path.isfile(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt')
  1076. assert os.path.isfile(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt')
  1077. assert 'alice@'+aliceDomain+':'+str(alicePort) in open(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt').read()
  1078. assert 'bob@'+bobDomain+':'+str(bobPort) in open(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt').read()
  1079. assert validInbox(bobDir,'bob',bobDomain)
  1080. assert validInboxFilenames(bobDir,'bob',bobDomain,aliceDomain,alicePort)
  1081. print('\n\nBob follows Alice')
  1082. sendFollowRequestViaServer(sessionAlice,'bob','bobpass', \
  1083. bobDomain,bobPort, \
  1084. 'alice',aliceDomain,alicePort, \
  1085. httpPrefix, \
  1086. cachedWebfingers,personCache, \
  1087. True,__version__)
  1088. for t in range(10):
  1089. if os.path.isfile(aliceDir+'/accounts/alice@'+aliceDomain+'/followers.txt'):
  1090. if 'bob@'+bobDomain+':'+str(bobPort) in open(aliceDir+'/accounts/alice@'+aliceDomain+'/followers.txt').read():
  1091. if os.path.isfile(bobDir+'/accounts/bob@'+bobDomain+'/following.txt'):
  1092. if 'alice@'+aliceDomain+':'+str(alicePort) in open(bobDir+'/accounts/bob@'+bobDomain+'/following.txt').read():
  1093. break
  1094. time.sleep(1)
  1095. assert os.path.isfile(aliceDir+'/accounts/alice@'+aliceDomain+'/followers.txt')
  1096. assert os.path.isfile(bobDir+'/accounts/bob@'+bobDomain+'/following.txt')
  1097. assert 'bob@'+bobDomain+':'+str(bobPort) in open(aliceDir+'/accounts/alice@'+aliceDomain+'/followers.txt').read()
  1098. assert 'alice@'+aliceDomain+':'+str(alicePort) in open(bobDir+'/accounts/bob@'+bobDomain+'/following.txt').read()
  1099. print('\n\nBob likes the post')
  1100. sessionBob = createSession(bobDomain,bobPort,useTor)
  1101. password='bobpass'
  1102. outboxPath=bobDir+'/accounts/bob@'+bobDomain+'/outbox'
  1103. inboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/inbox'
  1104. print(str(len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])))
  1105. assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==1
  1106. print(str(len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])))
  1107. assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1
  1108. sendLikeViaServer(sessionBob,'bob','bobpass', \
  1109. bobDomain,bobPort, \
  1110. httpPrefix,outboxPostId, \
  1111. cachedWebfingers,personCache, \
  1112. True,__version__)
  1113. for i in range(20):
  1114. if os.path.isdir(outboxPath) and os.path.isdir(inboxPath):
  1115. if len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==2:
  1116. if len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1:
  1117. break
  1118. time.sleep(1)
  1119. assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==2
  1120. assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1
  1121. print('Post liked')
  1122. print('\n\nBob repeats the post')
  1123. print(str(len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])))
  1124. assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==2
  1125. print(str(len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])))
  1126. assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1
  1127. sendAnnounceViaServer(sessionBob,'bob',password, \
  1128. bobDomain,bobPort, \
  1129. httpPrefix,outboxPostId, \
  1130. cachedWebfingers, \
  1131. personCache,True,__version__)
  1132. for i in range(20):
  1133. if os.path.isdir(outboxPath) and os.path.isdir(inboxPath):
  1134. if len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==3:
  1135. if len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==2:
  1136. break
  1137. time.sleep(1)
  1138. assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==3
  1139. assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==2
  1140. print('Post repeated')
  1141. inboxPath=bobDir+'/accounts/bob@'+bobDomain+'/inbox'
  1142. outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox'
  1143. postsBefore = len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])
  1144. print('\n\nAlice deletes her post: '+outboxPostId+' '+str(postsBefore))
  1145. password='alicepass'
  1146. sendDeleteViaServer(sessionAlice,'alice',password,
  1147. aliceDomain,alicePort, \
  1148. httpPrefix,outboxPostId, \
  1149. cachedWebfingers,personCache, \
  1150. True,__version__)
  1151. for i in range(30):
  1152. if os.path.isdir(inboxPath):
  1153. if len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==postsBefore-1:
  1154. break
  1155. time.sleep(1)
  1156. assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==postsBefore-1
  1157. print(">>> post deleted from Alice's outbox and Bob's inbox")
  1158. assert validInbox(bobDir,'bob',bobDomain)
  1159. assert validInboxFilenames(bobDir,'bob',bobDomain,aliceDomain,alicePort)
  1160. print('\n\nAlice unfollows Bob')
  1161. password='alicepass'
  1162. sendUnfollowRequestViaServer(sessionAlice,'alice',password, \
  1163. aliceDomain,alicePort, \
  1164. 'bob',bobDomain,bobPort, \
  1165. httpPrefix, \
  1166. cachedWebfingers,personCache, \
  1167. True,__version__)
  1168. for t in range(10):
  1169. if 'alice@'+aliceDomain+':'+str(alicePort) not in open(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt').read():
  1170. if 'bob@'+bobDomain+':'+str(bobPort) not in open(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt').read():
  1171. break
  1172. time.sleep(1)
  1173. assert os.path.isfile(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt')
  1174. assert os.path.isfile(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt')
  1175. assert 'alice@'+aliceDomain+':'+str(alicePort) not in open(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt').read()
  1176. assert 'bob@'+bobDomain+':'+str(bobPort) not in open(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt').read()
  1177. assert validInbox(bobDir,'bob',bobDomain)
  1178. assert validInboxFilenames(bobDir,'bob',bobDomain,aliceDomain,alicePort)
  1179. assert validInbox(aliceDir,'alice',aliceDomain)
  1180. assert validInboxFilenames(aliceDir,'alice',aliceDomain,bobDomain,bobPort)
  1181. # stop the servers
  1182. thrAlice.kill()
  1183. thrAlice.join()
  1184. assert thrAlice.isAlive()==False
  1185. thrBob.kill()
  1186. thrBob.join()
  1187. assert thrBob.isAlive()==False
  1188. os.chdir(baseDir)
  1189. #shutil.rmtree(aliceDir)
  1190. #shutil.rmtree(bobDir)
  1191. def runAllTests():
  1192. print('Running tests...')
  1193. testHttpsig()
  1194. testCache()
  1195. testThreads()
  1196. testCreatePerson()
  1197. testAuthentication()
  1198. testFollowersOfPerson()
  1199. testNoOfFollowersOnDomain()
  1200. testFollows()
  1201. testGroupFollowers()
  1202. testDelegateRoles()
  1203. print('Tests succeeded\n')