mirror of https://gitlab.com/bashrc2/epicyon
Set pgp public key from desktop client
parent
806209b3b5
commit
67f63c5119
|
|
@ -35,6 +35,7 @@ from announce import sendAnnounceViaServer
|
||||||
from pgp import pgpDecrypt
|
from pgp import pgpDecrypt
|
||||||
from pgp import hasLocalPGPkey
|
from pgp import hasLocalPGPkey
|
||||||
from pgp import pgpEncryptToActor
|
from pgp import pgpEncryptToActor
|
||||||
|
from pgp import pgpPublicKeyUpload
|
||||||
|
|
||||||
|
|
||||||
def _desktopHelp() -> None:
|
def _desktopHelp() -> None:
|
||||||
|
|
@ -850,8 +851,21 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str,
|
||||||
newRepliesExist = False
|
newRepliesExist = False
|
||||||
newDMsExist = False
|
newDMsExist = False
|
||||||
currPostId = ''
|
currPostId = ''
|
||||||
|
pgpKeyUpload = False
|
||||||
while (1):
|
while (1):
|
||||||
session = createSession(proxyType)
|
session = createSession(proxyType)
|
||||||
|
|
||||||
|
if not pgpKeyUpload:
|
||||||
|
pgpKey = \
|
||||||
|
pgpPublicKeyUpload(baseDir, session,
|
||||||
|
nickname, password,
|
||||||
|
domain, port, httpPrefix,
|
||||||
|
cachedWebfingers, personCache,
|
||||||
|
debug, False)
|
||||||
|
if pgpKey:
|
||||||
|
print('PGP public key uploaded')
|
||||||
|
pgpKeyUpload = True
|
||||||
|
|
||||||
notifyJson = None
|
notifyJson = None
|
||||||
speakerJson = \
|
speakerJson = \
|
||||||
getSpeakerFromServer(baseDir, session, nickname, password,
|
getSpeakerFromServer(baseDir, session, nickname, password,
|
||||||
|
|
|
||||||
20
epicyon.py
20
epicyon.py
|
|
@ -48,6 +48,7 @@ from follow import sendUnfollowRequestViaServer
|
||||||
from tests import testPostMessageBetweenServers
|
from tests import testPostMessageBetweenServers
|
||||||
from tests import testFollowBetweenServers
|
from tests import testFollowBetweenServers
|
||||||
from tests import testClientToServer
|
from tests import testClientToServer
|
||||||
|
from tests import testUpdateActor
|
||||||
from tests import runAllTests
|
from tests import runAllTests
|
||||||
from auth import storeBasicCredentials
|
from auth import storeBasicCredentials
|
||||||
from auth import createPassword
|
from auth import createPassword
|
||||||
|
|
@ -531,6 +532,7 @@ if args.testsnetwork:
|
||||||
testPostMessageBetweenServers()
|
testPostMessageBetweenServers()
|
||||||
testFollowBetweenServers()
|
testFollowBetweenServers()
|
||||||
testClientToServer()
|
testClientToServer()
|
||||||
|
testUpdateActor()
|
||||||
print('All tests succeeded')
|
print('All tests succeeded')
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
|
@ -2121,7 +2123,7 @@ if args.testdata:
|
||||||
|
|
||||||
testFollowersOnly = False
|
testFollowersOnly = False
|
||||||
testSaveToFile = True
|
testSaveToFile = True
|
||||||
testClientToServer = False
|
testC2S = False
|
||||||
testCommentsEnabled = True
|
testCommentsEnabled = True
|
||||||
testAttachImageFilename = None
|
testAttachImageFilename = None
|
||||||
testMediaType = None
|
testMediaType = None
|
||||||
|
|
@ -2131,7 +2133,7 @@ if args.testdata:
|
||||||
"like this is totally just a #test man",
|
"like this is totally just a #test man",
|
||||||
testFollowersOnly,
|
testFollowersOnly,
|
||||||
testSaveToFile,
|
testSaveToFile,
|
||||||
testClientToServer,
|
testC2S,
|
||||||
testCommentsEnabled,
|
testCommentsEnabled,
|
||||||
testAttachImageFilename,
|
testAttachImageFilename,
|
||||||
testMediaType, testImageDescription)
|
testMediaType, testImageDescription)
|
||||||
|
|
@ -2139,7 +2141,7 @@ if args.testdata:
|
||||||
"Zoiks!!!",
|
"Zoiks!!!",
|
||||||
testFollowersOnly,
|
testFollowersOnly,
|
||||||
testSaveToFile,
|
testSaveToFile,
|
||||||
testClientToServer,
|
testC2S,
|
||||||
testCommentsEnabled,
|
testCommentsEnabled,
|
||||||
testAttachImageFilename,
|
testAttachImageFilename,
|
||||||
testMediaType, testImageDescription)
|
testMediaType, testImageDescription)
|
||||||
|
|
@ -2147,7 +2149,7 @@ if args.testdata:
|
||||||
"Hey scoob we need like a hundred more #milkshakes",
|
"Hey scoob we need like a hundred more #milkshakes",
|
||||||
testFollowersOnly,
|
testFollowersOnly,
|
||||||
testSaveToFile,
|
testSaveToFile,
|
||||||
testClientToServer,
|
testC2S,
|
||||||
testCommentsEnabled,
|
testCommentsEnabled,
|
||||||
testAttachImageFilename,
|
testAttachImageFilename,
|
||||||
testMediaType, testImageDescription)
|
testMediaType, testImageDescription)
|
||||||
|
|
@ -2155,7 +2157,7 @@ if args.testdata:
|
||||||
"Getting kinda spooky around here",
|
"Getting kinda spooky around here",
|
||||||
testFollowersOnly,
|
testFollowersOnly,
|
||||||
testSaveToFile,
|
testSaveToFile,
|
||||||
testClientToServer,
|
testC2S,
|
||||||
testCommentsEnabled,
|
testCommentsEnabled,
|
||||||
testAttachImageFilename,
|
testAttachImageFilename,
|
||||||
testMediaType, testImageDescription,
|
testMediaType, testImageDescription,
|
||||||
|
|
@ -2165,7 +2167,7 @@ if args.testdata:
|
||||||
"if it wasn't for those pesky hackers",
|
"if it wasn't for those pesky hackers",
|
||||||
testFollowersOnly,
|
testFollowersOnly,
|
||||||
testSaveToFile,
|
testSaveToFile,
|
||||||
testClientToServer,
|
testC2S,
|
||||||
testCommentsEnabled,
|
testCommentsEnabled,
|
||||||
'img/logo.png', 'image/png',
|
'img/logo.png', 'image/png',
|
||||||
'Description of image')
|
'Description of image')
|
||||||
|
|
@ -2173,7 +2175,7 @@ if args.testdata:
|
||||||
"man these centralized sites are like the worst!",
|
"man these centralized sites are like the worst!",
|
||||||
testFollowersOnly,
|
testFollowersOnly,
|
||||||
testSaveToFile,
|
testSaveToFile,
|
||||||
testClientToServer,
|
testC2S,
|
||||||
testCommentsEnabled,
|
testCommentsEnabled,
|
||||||
testAttachImageFilename,
|
testAttachImageFilename,
|
||||||
testMediaType, testImageDescription)
|
testMediaType, testImageDescription)
|
||||||
|
|
@ -2181,7 +2183,7 @@ if args.testdata:
|
||||||
"another mystery solved #test",
|
"another mystery solved #test",
|
||||||
testFollowersOnly,
|
testFollowersOnly,
|
||||||
testSaveToFile,
|
testSaveToFile,
|
||||||
testClientToServer,
|
testC2S,
|
||||||
testCommentsEnabled,
|
testCommentsEnabled,
|
||||||
testAttachImageFilename,
|
testAttachImageFilename,
|
||||||
testMediaType, testImageDescription)
|
testMediaType, testImageDescription)
|
||||||
|
|
@ -2189,7 +2191,7 @@ if args.testdata:
|
||||||
"let's go bowling",
|
"let's go bowling",
|
||||||
testFollowersOnly,
|
testFollowersOnly,
|
||||||
testSaveToFile,
|
testSaveToFile,
|
||||||
testClientToServer,
|
testC2S,
|
||||||
testCommentsEnabled,
|
testCommentsEnabled,
|
||||||
testAttachImageFilename,
|
testAttachImageFilename,
|
||||||
testMediaType, testImageDescription)
|
testMediaType, testImageDescription)
|
||||||
|
|
|
||||||
|
|
@ -1028,7 +1028,7 @@ def sendFollowRequestViaServer(baseDir: str, session,
|
||||||
postJson(session, newFollowJson, [], inboxUrl, headers, 30, True)
|
postJson(session, newFollowJson, [], inboxUrl, headers, 30, True)
|
||||||
if not postResult:
|
if not postResult:
|
||||||
if debug:
|
if debug:
|
||||||
print('DEBUG: POST announce failed for c2s to ' + inboxUrl)
|
print('DEBUG: POST follow failed for c2s to ' + inboxUrl)
|
||||||
return 5
|
return 5
|
||||||
|
|
||||||
if debug:
|
if debug:
|
||||||
|
|
|
||||||
122
outbox.py
122
outbox.py
|
|
@ -21,6 +21,8 @@ from utils import removeIdEnding
|
||||||
from utils import getDomainFromActor
|
from utils import getDomainFromActor
|
||||||
from utils import dangerousMarkup
|
from utils import dangerousMarkup
|
||||||
from utils import isFeaturedWriter
|
from utils import isFeaturedWriter
|
||||||
|
from utils import loadJson
|
||||||
|
from utils import saveJson
|
||||||
from blocking import isBlockedDomain
|
from blocking import isBlockedDomain
|
||||||
from blocking import outboxBlock
|
from blocking import outboxBlock
|
||||||
from blocking import outboxUndoBlock
|
from blocking import outboxUndoBlock
|
||||||
|
|
@ -42,6 +44,119 @@ from shares import outboxShareUpload
|
||||||
from shares import outboxUndoShareUpload
|
from shares import outboxUndoShareUpload
|
||||||
|
|
||||||
|
|
||||||
|
def _outboxPersonReceiveUpdate(recentPostsCache: {},
|
||||||
|
baseDir: str, httpPrefix: str,
|
||||||
|
nickname: str, domain: str, port: int,
|
||||||
|
messageJson: {}, debug: bool) -> None:
|
||||||
|
""" Receive an actor update from c2s
|
||||||
|
For example, setting the PGP key from the desktop client
|
||||||
|
"""
|
||||||
|
# these attachments are updatable via c2s
|
||||||
|
updatableAttachments = ('PGP', 'OpenPGP', 'Email')
|
||||||
|
|
||||||
|
if not messageJson.get('type'):
|
||||||
|
return
|
||||||
|
print("messageJson['type'] " + messageJson['type'])
|
||||||
|
if messageJson['type'] != 'Update':
|
||||||
|
return
|
||||||
|
if not messageJson.get('object'):
|
||||||
|
return
|
||||||
|
if not isinstance(messageJson['object'], dict):
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: c2s actor update object is not dict')
|
||||||
|
return
|
||||||
|
if not messageJson['object'].get('type'):
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: c2s actor update - no type')
|
||||||
|
return
|
||||||
|
if messageJson['object']['type'] != 'Person':
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: not a c2s actor update')
|
||||||
|
return
|
||||||
|
if not messageJson.get('to'):
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: c2s actor update has no "to" field')
|
||||||
|
return
|
||||||
|
if not messageJson.get('actor'):
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: c2s actor update has no actor field')
|
||||||
|
return
|
||||||
|
if not messageJson.get('id'):
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: c2s actor update has no id field')
|
||||||
|
return
|
||||||
|
actor = \
|
||||||
|
httpPrefix + '://' + getFullDomain(domain, port) + '/users/' + nickname
|
||||||
|
if len(messageJson['to']) != 1:
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: c2s actor update - to does not contain one actor ' +
|
||||||
|
messageJson['to'])
|
||||||
|
return
|
||||||
|
if messageJson['to'][0] != actor:
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: c2s actor update - to does not contain actor ' +
|
||||||
|
messageJson['to'] + ' ' + actor)
|
||||||
|
return
|
||||||
|
if not messageJson['id'].startswith(actor + '#updates/'):
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: c2s actor update - unexpected id ' +
|
||||||
|
messageJson['id'])
|
||||||
|
return
|
||||||
|
updatedActorJson = messageJson['object']
|
||||||
|
# load actor from file
|
||||||
|
actorFilename = baseDir + '/accounts/' + nickname + '@' + domain + '.json'
|
||||||
|
if not os.path.isfile(actorFilename):
|
||||||
|
print('actorFilename not found: ' + actorFilename)
|
||||||
|
return
|
||||||
|
actorJson = loadJson(actorFilename)
|
||||||
|
if not actorJson:
|
||||||
|
return
|
||||||
|
actorChanged = False
|
||||||
|
# update fields within actor
|
||||||
|
if 'attachment' in updatedActorJson:
|
||||||
|
for newPropertyValue in updatedActorJson['attachment']:
|
||||||
|
if not newPropertyValue.get('name'):
|
||||||
|
continue
|
||||||
|
if newPropertyValue['name'] not in updatableAttachments:
|
||||||
|
continue
|
||||||
|
if not newPropertyValue.get('type'):
|
||||||
|
continue
|
||||||
|
if not newPropertyValue.get('value'):
|
||||||
|
continue
|
||||||
|
if newPropertyValue['type'] != 'PropertyValue':
|
||||||
|
continue
|
||||||
|
if 'attachment' in actorJson:
|
||||||
|
found = False
|
||||||
|
for propertyValue in actorJson['attachment']:
|
||||||
|
if propertyValue != 'PropertyValue':
|
||||||
|
continue
|
||||||
|
if propertyValue['name'] == newPropertyValue['name']:
|
||||||
|
if propertyValue['value'] != \
|
||||||
|
newPropertyValue['value']:
|
||||||
|
propertyValue['value'] = \
|
||||||
|
newPropertyValue['value']
|
||||||
|
actorChanged = True
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
actorJson['attachment'].append({
|
||||||
|
"name": newPropertyValue['name'],
|
||||||
|
"type": "PropertyValue",
|
||||||
|
"value": newPropertyValue['value']
|
||||||
|
})
|
||||||
|
actorChanged = True
|
||||||
|
# save actor to file
|
||||||
|
if actorChanged:
|
||||||
|
saveJson(actorJson, actorFilename)
|
||||||
|
if debug:
|
||||||
|
print('actor saved: ' + actorFilename)
|
||||||
|
if debug:
|
||||||
|
print('New attachment: ' + str(actorJson['attachment']))
|
||||||
|
messageJson['object'] = actorJson
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: actor update via c2s - ' + nickname + '@' + domain)
|
||||||
|
|
||||||
|
|
||||||
def postMessageToOutbox(session, translate: {},
|
def postMessageToOutbox(session, translate: {},
|
||||||
messageJson: {}, postToNickname: str,
|
messageJson: {}, postToNickname: str,
|
||||||
server, baseDir: str, httpPrefix: str,
|
server, baseDir: str, httpPrefix: str,
|
||||||
|
|
@ -408,6 +523,13 @@ def postMessageToOutbox(session, translate: {},
|
||||||
postToNickname, domain,
|
postToNickname, domain,
|
||||||
port, messageJson, debug)
|
port, messageJson, debug)
|
||||||
|
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: handle actor updates from c2s')
|
||||||
|
_outboxPersonReceiveUpdate(recentPostsCache,
|
||||||
|
baseDir, httpPrefix,
|
||||||
|
postToNickname, domain, port,
|
||||||
|
messageJson, debug)
|
||||||
|
|
||||||
if debug:
|
if debug:
|
||||||
print('DEBUG: sending c2s post to named addresses')
|
print('DEBUG: sending c2s post to named addresses')
|
||||||
if messageJson.get('to'):
|
if messageJson.get('to'):
|
||||||
|
|
|
||||||
33
person.py
33
person.py
|
|
@ -1106,6 +1106,8 @@ def getActorJson(handle: str, http: bool, gnunet: bool,
|
||||||
debug: bool, quiet=False) -> {}:
|
debug: bool, quiet=False) -> {}:
|
||||||
"""Returns the actor json
|
"""Returns the actor json
|
||||||
"""
|
"""
|
||||||
|
if debug:
|
||||||
|
print('getActorJson for ' + handle)
|
||||||
originalActor = handle
|
originalActor = handle
|
||||||
if '/@' in handle or \
|
if '/@' in handle or \
|
||||||
'/users/' in handle or \
|
'/users/' in handle or \
|
||||||
|
|
@ -1117,8 +1119,8 @@ def getActorJson(handle: str, http: bool, gnunet: bool,
|
||||||
handle = handle.replace(prefix, '')
|
handle = handle.replace(prefix, '')
|
||||||
handle = handle.replace('/@', '/users/')
|
handle = handle.replace('/@', '/users/')
|
||||||
if not hasUsersPath(handle):
|
if not hasUsersPath(handle):
|
||||||
if not quiet:
|
if not quiet or debug:
|
||||||
print('Expected actor format: ' +
|
print('getActorJson: Expected actor format: ' +
|
||||||
'https://domain/@nick or https://domain/users/nick')
|
'https://domain/@nick or https://domain/users/nick')
|
||||||
return None
|
return None
|
||||||
if '/users/' in handle:
|
if '/users/' in handle:
|
||||||
|
|
@ -1145,13 +1147,13 @@ def getActorJson(handle: str, http: bool, gnunet: bool,
|
||||||
# format: @nick@domain
|
# format: @nick@domain
|
||||||
if '@' not in handle:
|
if '@' not in handle:
|
||||||
if not quiet:
|
if not quiet:
|
||||||
print('Syntax: --actor nickname@domain')
|
print('getActorJson Syntax: --actor nickname@domain')
|
||||||
return None
|
return None
|
||||||
if handle.startswith('@'):
|
if handle.startswith('@'):
|
||||||
handle = handle[1:]
|
handle = handle[1:]
|
||||||
if '@' not in handle:
|
if '@' not in handle:
|
||||||
if not quiet:
|
if not quiet:
|
||||||
print('Syntax: --actor nickname@domain')
|
print('getActorJsonSyntax: --actor nickname@domain')
|
||||||
return None
|
return None
|
||||||
nickname = handle.split('@')[0]
|
nickname = handle.split('@')[0]
|
||||||
domain = handle.split('@')[1]
|
domain = handle.split('@')[1]
|
||||||
|
|
@ -1168,7 +1170,10 @@ def getActorJson(handle: str, http: bool, gnunet: bool,
|
||||||
httpPrefix = 'gnunet'
|
httpPrefix = 'gnunet'
|
||||||
proxyType = 'gnunet'
|
proxyType = 'gnunet'
|
||||||
else:
|
else:
|
||||||
httpPrefix = 'https'
|
if '127.0.' not in domain and '192.168.' not in domain:
|
||||||
|
httpPrefix = 'https'
|
||||||
|
else:
|
||||||
|
httpPrefix = 'http'
|
||||||
session = createSession(proxyType)
|
session = createSession(proxyType)
|
||||||
if nickname == 'inbox':
|
if nickname == 'inbox':
|
||||||
nickname = domain
|
nickname = domain
|
||||||
|
|
@ -1179,12 +1184,12 @@ def getActorJson(handle: str, http: bool, gnunet: bool,
|
||||||
None, __version__, debug)
|
None, __version__, debug)
|
||||||
if not wfRequest:
|
if not wfRequest:
|
||||||
if not quiet:
|
if not quiet:
|
||||||
print('Unable to webfinger ' + handle)
|
print('getActorJson Unable to webfinger ' + handle)
|
||||||
return None
|
return None
|
||||||
if not isinstance(wfRequest, dict):
|
if not isinstance(wfRequest, dict):
|
||||||
if not quiet:
|
if not quiet:
|
||||||
print('Webfinger for ' + handle + ' did not return a dict. ' +
|
print('getActorJson Webfinger for ' + handle +
|
||||||
str(wfRequest))
|
' did not return a dict. ' + str(wfRequest))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not quiet:
|
if not quiet:
|
||||||
|
|
@ -1192,11 +1197,13 @@ def getActorJson(handle: str, http: bool, gnunet: bool,
|
||||||
|
|
||||||
personUrl = None
|
personUrl = None
|
||||||
if wfRequest.get('errors'):
|
if wfRequest.get('errors'):
|
||||||
if not quiet:
|
if not quiet or debug:
|
||||||
print('wfRequest error: ' + str(wfRequest['errors']))
|
print('getActorJson wfRequest error: ' + str(wfRequest['errors']))
|
||||||
if hasUsersPath(handle):
|
if hasUsersPath(handle):
|
||||||
personUrl = originalActor
|
personUrl = originalActor
|
||||||
else:
|
else:
|
||||||
|
if debug:
|
||||||
|
print('No users path in ' + handle)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
profileStr = 'https://www.w3.org/ns/activitystreams'
|
profileStr = 'https://www.w3.org/ns/activitystreams'
|
||||||
|
|
@ -1228,8 +1235,9 @@ def getActorJson(handle: str, http: bool, gnunet: bool,
|
||||||
getJson(session, personUrl, asHeader, None,
|
getJson(session, personUrl, asHeader, None,
|
||||||
debug, __version__, httpPrefix, None, 20, quiet)
|
debug, __version__, httpPrefix, None, 20, quiet)
|
||||||
if personJson:
|
if personJson:
|
||||||
if not quiet:
|
if not quiet or debug:
|
||||||
pprint(personJson)
|
pprint(personJson)
|
||||||
|
return personJson
|
||||||
else:
|
else:
|
||||||
profileStr = 'https://www.w3.org/ns/activitystreams'
|
profileStr = 'https://www.w3.org/ns/activitystreams'
|
||||||
asHeader = {
|
asHeader = {
|
||||||
|
|
@ -1238,8 +1246,9 @@ def getActorJson(handle: str, http: bool, gnunet: bool,
|
||||||
personJson = \
|
personJson = \
|
||||||
getJson(session, personUrl, asHeader, None,
|
getJson(session, personUrl, asHeader, None,
|
||||||
debug, __version__, httpPrefix, None)
|
debug, __version__, httpPrefix, None)
|
||||||
if not quiet:
|
if not quiet or debug:
|
||||||
if personJson:
|
if personJson:
|
||||||
|
print('getActorJson returned actor')
|
||||||
pprint(personJson)
|
pprint(personJson)
|
||||||
else:
|
else:
|
||||||
print('Failed to get ' + personUrl)
|
print('Failed to get ' + personUrl)
|
||||||
|
|
|
||||||
205
pgp.py
205
pgp.py
|
|
@ -7,11 +7,18 @@ __email__ = "bob@freedombone.net"
|
||||||
__status__ = "Production"
|
__status__ = "Production"
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from person import getActorJson
|
from person import getActorJson
|
||||||
from utils import containsPGPPublicKey
|
from utils import containsPGPPublicKey
|
||||||
from utils import isPGPEncrypted
|
from utils import isPGPEncrypted
|
||||||
|
from utils import getFullDomain
|
||||||
|
from utils import getStatusNumber
|
||||||
|
from webfinger import webfingerHandle
|
||||||
|
from posts import getPersonBox
|
||||||
|
from auth import createBasicAuthHeader
|
||||||
|
from session import postJson
|
||||||
|
|
||||||
|
|
||||||
def getEmailAddress(actorJson: {}) -> str:
|
def getEmailAddress(actorJson: {}) -> str:
|
||||||
|
|
@ -395,3 +402,201 @@ def pgpDecrypt(content: str, fromHandle: str) -> str:
|
||||||
return content
|
return content
|
||||||
decryptResult = decryptResult.decode('utf-8').strip()
|
decryptResult = decryptResult.decode('utf-8').strip()
|
||||||
return decryptResult
|
return decryptResult
|
||||||
|
|
||||||
|
|
||||||
|
def _pgpLocalPublicKeyId() -> str:
|
||||||
|
"""Gets the local pgp public key ID
|
||||||
|
"""
|
||||||
|
cmdStr = \
|
||||||
|
"gpgconf --list-options gpg | " + \
|
||||||
|
"awk -F: '$1 == \"default-key\" {print $10}'"
|
||||||
|
proc = subprocess.Popen([cmdStr],
|
||||||
|
stdout=subprocess.PIPE, shell=True)
|
||||||
|
(result, err) = proc.communicate()
|
||||||
|
if err:
|
||||||
|
return None
|
||||||
|
if not result:
|
||||||
|
return None
|
||||||
|
if len(result) < 5:
|
||||||
|
return None
|
||||||
|
return result.replace('"', '').strip()
|
||||||
|
|
||||||
|
|
||||||
|
def _pgpLocalPublicKey() -> str:
|
||||||
|
"""Gets the local pgp public key
|
||||||
|
"""
|
||||||
|
keyId = _pgpLocalPublicKey()
|
||||||
|
if not keyId:
|
||||||
|
return None
|
||||||
|
cmdStr = "gpg --armor --export " + keyId
|
||||||
|
proc = subprocess.Popen([cmdStr],
|
||||||
|
stdout=subprocess.PIPE, shell=True)
|
||||||
|
(result, err) = proc.communicate()
|
||||||
|
if err:
|
||||||
|
return None
|
||||||
|
if not result:
|
||||||
|
return None
|
||||||
|
return extractPGPPublicKey(result)
|
||||||
|
|
||||||
|
|
||||||
|
def pgpPublicKeyUpload(baseDir: str, session,
|
||||||
|
nickname: str, password: str,
|
||||||
|
domain: str, port: int,
|
||||||
|
httpPrefix: str,
|
||||||
|
cachedWebfingers: {}, personCache: {},
|
||||||
|
debug: bool, test: str) -> {}:
|
||||||
|
if debug:
|
||||||
|
print('pgpPublicKeyUpload')
|
||||||
|
|
||||||
|
if not session:
|
||||||
|
if debug:
|
||||||
|
print('WARN: No session for pgpPublicKeyUpload')
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not test:
|
||||||
|
if debug:
|
||||||
|
print('Getting PGP public key')
|
||||||
|
PGPpubKey = _pgpLocalPublicKey()
|
||||||
|
if not PGPpubKey:
|
||||||
|
return None
|
||||||
|
PGPpubKeyId = _pgpLocalPublicKeyId()
|
||||||
|
else:
|
||||||
|
if debug:
|
||||||
|
print('Testing with PGP public key ' + test)
|
||||||
|
PGPpubKey = test
|
||||||
|
PGPpubKeyId = None
|
||||||
|
|
||||||
|
domainFull = getFullDomain(domain, port)
|
||||||
|
if debug:
|
||||||
|
print('PGP test domain: ' + domainFull)
|
||||||
|
|
||||||
|
handle = nickname + '@' + domainFull
|
||||||
|
|
||||||
|
if debug:
|
||||||
|
print('Getting actor for ' + handle)
|
||||||
|
|
||||||
|
actorJson = getActorJson(handle, False, False, True)
|
||||||
|
if not actorJson:
|
||||||
|
if debug:
|
||||||
|
print('No actor returned for ' + handle)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if debug:
|
||||||
|
print('Actor for ' + handle + ' obtained')
|
||||||
|
|
||||||
|
actor = httpPrefix + '://' + domainFull + '/users/' + nickname
|
||||||
|
handle = actor.replace('/users/', '/@')
|
||||||
|
|
||||||
|
# check that this looks like the correct actor
|
||||||
|
if not actorJson.get('id'):
|
||||||
|
if debug:
|
||||||
|
print('Actor has no id')
|
||||||
|
return None
|
||||||
|
if not actorJson.get('url'):
|
||||||
|
if debug:
|
||||||
|
print('Actor has no url')
|
||||||
|
return None
|
||||||
|
if not actorJson.get('type'):
|
||||||
|
if debug:
|
||||||
|
print('Actor has no type')
|
||||||
|
return None
|
||||||
|
if actorJson['id'] != actor:
|
||||||
|
if debug:
|
||||||
|
print('Actor id is not ' + actor +
|
||||||
|
' instead is ' + actorJson['id'])
|
||||||
|
return None
|
||||||
|
if actorJson['url'] != handle:
|
||||||
|
if debug:
|
||||||
|
print('Actor url is not ' + handle)
|
||||||
|
return None
|
||||||
|
if actorJson['type'] != 'Person':
|
||||||
|
if debug:
|
||||||
|
print('Actor type is not Person')
|
||||||
|
return None
|
||||||
|
|
||||||
|
# set the pgp details
|
||||||
|
if PGPpubKeyId:
|
||||||
|
setPGPfingerprint(actorJson, PGPpubKeyId)
|
||||||
|
else:
|
||||||
|
if debug:
|
||||||
|
print('No PGP key Id. Continuing anyway.')
|
||||||
|
|
||||||
|
if debug:
|
||||||
|
print('Setting PGP key within ' + actor)
|
||||||
|
setPGPpubKey(actorJson, PGPpubKey)
|
||||||
|
|
||||||
|
# create an actor update
|
||||||
|
statusNumber, published = getStatusNumber()
|
||||||
|
actorUpdate = {
|
||||||
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
|
'id': actor + '#updates/' + statusNumber,
|
||||||
|
'type': 'Update',
|
||||||
|
'actor': actor,
|
||||||
|
'to': [actor],
|
||||||
|
'cc': [],
|
||||||
|
'object': actorJson
|
||||||
|
}
|
||||||
|
if debug:
|
||||||
|
print('actor update is ' + str(actorUpdate))
|
||||||
|
|
||||||
|
# lookup the inbox for the To handle
|
||||||
|
wfRequest = \
|
||||||
|
webfingerHandle(session, handle, httpPrefix, cachedWebfingers,
|
||||||
|
domain, __version__, debug)
|
||||||
|
if not wfRequest:
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: pgp actor update webfinger failed for ' +
|
||||||
|
handle)
|
||||||
|
return None
|
||||||
|
if not isinstance(wfRequest, dict):
|
||||||
|
if debug:
|
||||||
|
print('WARN: Webfinger for ' + handle +
|
||||||
|
' did not return a dict. ' + str(wfRequest))
|
||||||
|
return None
|
||||||
|
|
||||||
|
postToBox = 'outbox'
|
||||||
|
|
||||||
|
# get the actor inbox for the To handle
|
||||||
|
(inboxUrl, pubKeyId, pubKey,
|
||||||
|
fromPersonId, sharedInbox, avatarUrl,
|
||||||
|
displayName) = getPersonBox(baseDir, session, wfRequest, personCache,
|
||||||
|
__version__, httpPrefix, nickname,
|
||||||
|
domain, postToBox, 52025)
|
||||||
|
|
||||||
|
if not inboxUrl:
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: No ' + postToBox + ' was found for ' + handle)
|
||||||
|
return None
|
||||||
|
if not fromPersonId:
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: No actor was found for ' + handle)
|
||||||
|
return None
|
||||||
|
|
||||||
|
authHeader = createBasicAuthHeader(nickname, password)
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'host': domain,
|
||||||
|
'Content-type': 'application/json',
|
||||||
|
'Authorization': authHeader
|
||||||
|
}
|
||||||
|
tries = 0
|
||||||
|
quiet = not debug
|
||||||
|
while tries < 4:
|
||||||
|
postResult = \
|
||||||
|
postJson(session, actorUpdate, [], inboxUrl,
|
||||||
|
headers, 30, quiet)
|
||||||
|
if postResult:
|
||||||
|
break
|
||||||
|
time.sleep(2)
|
||||||
|
tries += 1
|
||||||
|
|
||||||
|
if postResult is None:
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: POST pgp actor update failed for c2s to ' +
|
||||||
|
inboxUrl)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if debug:
|
||||||
|
print('DEBUG: c2s POST pgp actor update success')
|
||||||
|
|
||||||
|
return actorUpdate
|
||||||
|
|
|
||||||
119
tests.py
119
tests.py
|
|
@ -53,6 +53,7 @@ from utils import getFollowersOfPerson
|
||||||
from utils import removeHtml
|
from utils import removeHtml
|
||||||
from utils import dangerousMarkup
|
from utils import dangerousMarkup
|
||||||
from pgp import extractPGPPublicKey
|
from pgp import extractPGPPublicKey
|
||||||
|
from pgp import pgpPublicKeyUpload
|
||||||
from utils import containsPGPPublicKey
|
from utils import containsPGPPublicKey
|
||||||
from follow import followerOfPerson
|
from follow import followerOfPerson
|
||||||
from follow import unfollowAccount
|
from follow import unfollowAccount
|
||||||
|
|
@ -1574,7 +1575,7 @@ def testClientToServer():
|
||||||
|
|
||||||
sessionAlice = createSession(proxyType)
|
sessionAlice = createSession(proxyType)
|
||||||
followersOnly = False
|
followersOnly = False
|
||||||
attachedImageFilename = baseDir+'/img/logo.png'
|
attachedImageFilename = baseDir + '/img/logo.png'
|
||||||
mediaType = getAttachmentMediaType(attachedImageFilename)
|
mediaType = getAttachmentMediaType(attachedImageFilename)
|
||||||
attachedImageDescription = 'Logo'
|
attachedImageDescription = 'Logo'
|
||||||
isArticle = False
|
isArticle = False
|
||||||
|
|
@ -3447,6 +3448,122 @@ def testExtractPGPPublicKey():
|
||||||
assert result == pubKey
|
assert result == pubKey
|
||||||
|
|
||||||
|
|
||||||
|
def testUpdateActor():
|
||||||
|
print('Testing update of actor properties')
|
||||||
|
|
||||||
|
global testServerAliceRunning
|
||||||
|
testServerAliceRunning = False
|
||||||
|
|
||||||
|
httpPrefix = 'http'
|
||||||
|
proxyType = None
|
||||||
|
federationList = []
|
||||||
|
|
||||||
|
baseDir = os.getcwd()
|
||||||
|
if os.path.isdir(baseDir + '/.tests'):
|
||||||
|
shutil.rmtree(baseDir + '/.tests')
|
||||||
|
os.mkdir(baseDir + '/.tests')
|
||||||
|
|
||||||
|
# create the server
|
||||||
|
aliceDir = baseDir + '/.tests/alice'
|
||||||
|
aliceDomain = '127.0.0.11'
|
||||||
|
alicePort = 61792
|
||||||
|
aliceSendThreads = []
|
||||||
|
bobAddress = '127.0.0.84:6384'
|
||||||
|
|
||||||
|
global thrAlice
|
||||||
|
if thrAlice:
|
||||||
|
while thrAlice.is_alive():
|
||||||
|
thrAlice.stop()
|
||||||
|
time.sleep(1)
|
||||||
|
thrAlice.kill()
|
||||||
|
|
||||||
|
thrAlice = \
|
||||||
|
threadWithTrace(target=createServerAlice,
|
||||||
|
args=(aliceDir, aliceDomain, alicePort, bobAddress,
|
||||||
|
federationList, False, False,
|
||||||
|
aliceSendThreads),
|
||||||
|
daemon=True)
|
||||||
|
|
||||||
|
thrAlice.start()
|
||||||
|
assert thrAlice.is_alive() is True
|
||||||
|
|
||||||
|
# wait for server to be running
|
||||||
|
ctr = 0
|
||||||
|
while not testServerAliceRunning:
|
||||||
|
time.sleep(1)
|
||||||
|
ctr += 1
|
||||||
|
if ctr > 60:
|
||||||
|
break
|
||||||
|
print('Alice online: ' + str(testServerAliceRunning))
|
||||||
|
|
||||||
|
print('\n\n*******************************************************')
|
||||||
|
print('Alice updates her PGP key')
|
||||||
|
|
||||||
|
sessionAlice = createSession(proxyType)
|
||||||
|
cachedWebfingers = {}
|
||||||
|
personCache = {}
|
||||||
|
password = 'alicepass'
|
||||||
|
outboxPath = aliceDir + '/accounts/alice@' + aliceDomain + '/outbox'
|
||||||
|
actorFilename = aliceDir + '/accounts/' + 'alice@' + aliceDomain + '.json'
|
||||||
|
assert os.path.isfile(actorFilename)
|
||||||
|
assert len([name for name in os.listdir(outboxPath)
|
||||||
|
if os.path.isfile(os.path.join(outboxPath, name))]) == 0
|
||||||
|
pubKey = \
|
||||||
|
'-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n' + \
|
||||||
|
'mDMEWZBueBYJKwYBBAHaRw8BAQdAKx1t6wL0RTuU6/' + \
|
||||||
|
'IBjngMbVJJ3Wg/3UW73/PV\n' + \
|
||||||
|
'I47xKTS0IUJvYiBNb3R0cmFtIDxib2JAZnJlZWRvb' + \
|
||||||
|
'WJvbmUubmV0PoiQBBMWCAA4\n' + \
|
||||||
|
'FiEEmruCwAq/OfgmgEh9zCU2GR+nwz8FAlmQbngCG' + \
|
||||||
|
'wMFCwkIBwMFFQoJCAsFFgID\n' + \
|
||||||
|
'AQACHgECF4AACgkQzCU2GR+nwz/9sAD/YgsHnVszH' + \
|
||||||
|
'Nz1zlVc5EgY1ByDupiJpHj0\n' + \
|
||||||
|
'XsLYk3AbNRgBALn45RqgD4eWHpmOriH09H5Rc5V9i' + \
|
||||||
|
'N4+OiGUn2AzJ6oHuDgEWZBu\n' + \
|
||||||
|
'eBIKKwYBBAGXVQEFAQEHQPRBG2ZQJce475S3e0Dxe' + \
|
||||||
|
'b0Fz5WdEu2q3GYLo4QG+4Ry\n' + \
|
||||||
|
'AwEIB4h4BBgWCAAgFiEEmruCwAq/OfgmgEh9zCU2G' + \
|
||||||
|
'R+nwz8FAlmQbngCGwwACgkQ\n' + \
|
||||||
|
'zCU2GR+nwz+OswD+JOoyBku9FzuWoVoOevU2HH+bP' + \
|
||||||
|
'OMDgY2OLnST9ZSyHkMBAMcK\n' + \
|
||||||
|
'fnaZ2Wi050483Sj2RmQRpb99Dod7rVZTDtCqXk0J\n' + \
|
||||||
|
'=gv5G\n' + \
|
||||||
|
'-----END PGP PUBLIC KEY BLOCK-----'
|
||||||
|
actorUpdate = \
|
||||||
|
pgpPublicKeyUpload(aliceDir, sessionAlice,
|
||||||
|
'alice', password,
|
||||||
|
aliceDomain, alicePort,
|
||||||
|
httpPrefix,
|
||||||
|
cachedWebfingers, personCache,
|
||||||
|
True, pubKey)
|
||||||
|
print('actor update result: ' + str(actorUpdate))
|
||||||
|
assert actorUpdate
|
||||||
|
|
||||||
|
# load alice actor
|
||||||
|
print('Loading actor: ' + actorFilename)
|
||||||
|
actorJson = loadJson(actorFilename)
|
||||||
|
assert actorJson
|
||||||
|
if len(actorJson['attachment']) == 0:
|
||||||
|
print("actorJson['attachment'] has no contents")
|
||||||
|
assert len(actorJson['attachment']) > 0
|
||||||
|
propertyFound = False
|
||||||
|
for propertyValue in actorJson['attachment']:
|
||||||
|
if propertyValue['name'] == 'PGP':
|
||||||
|
print('PGP property set within attachment')
|
||||||
|
assert pubKey in propertyValue['value']
|
||||||
|
propertyFound = True
|
||||||
|
assert propertyFound
|
||||||
|
|
||||||
|
# stop the server
|
||||||
|
thrAlice.kill()
|
||||||
|
thrAlice.join()
|
||||||
|
assert thrAlice.is_alive() is False
|
||||||
|
|
||||||
|
os.chdir(baseDir)
|
||||||
|
if os.path.isdir(baseDir + '/.tests'):
|
||||||
|
shutil.rmtree(baseDir + '/.tests')
|
||||||
|
|
||||||
|
|
||||||
def runAllTests():
|
def runAllTests():
|
||||||
print('Running tests...')
|
print('Running tests...')
|
||||||
testFunctions()
|
testFunctions()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue