epicyon/webfinger.py

183 lines
6.1 KiB
Python
Raw Normal View History

2019-06-28 18:55:29 +00:00
__filename__ = "webfinger.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "0.0.1"
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
import base64
from Crypto.PublicKey import RSA
from Crypto.Util import number
import requests
import json
import commentjson
import os
from session import getJson
2019-06-30 15:03:26 +00:00
from cache import storeWebfingerInCache
from cache import getWebfingerFromCache
2019-06-28 18:55:29 +00:00
2019-07-01 11:09:09 +00:00
def parseHandle(handle: str) -> (str,str):
2019-06-28 18:55:29 +00:00
if '.' not in handle:
return None, None
if '/@' in handle:
2019-07-02 20:54:22 +00:00
domain, username = \
handle.replace('https://','').replace('http://','').split('/@')
2019-06-28 18:55:29 +00:00
else:
if '/users/' in handle:
2019-07-02 20:54:22 +00:00
domain, username = \
handle.replace('https://','').replace('http://','').split('/users/')
2019-06-28 18:55:29 +00:00
else:
if '@' in handle:
username, domain = handle.split('@')
else:
return None, None
return username, domain
2019-07-01 14:30:48 +00:00
def webfingerHandle(session,handle: str,https: bool,cachedWebfingers: {}) -> {}:
2019-06-28 18:55:29 +00:00
username, domain = parseHandle(handle)
if not username:
return None
2019-07-01 21:01:43 +00:00
wfDomain=domain
if ':' in wfDomain:
wfDomain=wfDomain.split(':')[0]
print('***********cachedWebfingers '+str(cachedWebfingers))
wf=getWebfingerFromCache(username+'@'+wfDomain,cachedWebfingers)
2019-06-30 15:03:26 +00:00
if wf:
return wf
2019-06-28 18:55:29 +00:00
prefix='https'
if not https:
2019-07-01 21:01:43 +00:00
prefix='http'
2019-06-28 18:55:29 +00:00
url = '{}://{}/.well-known/webfinger'.format(prefix,domain)
2019-07-01 21:01:43 +00:00
par = {'resource': 'acct:{}'.format(username+'@'+wfDomain)}
2019-06-28 18:55:29 +00:00
hdr = {'Accept': 'application/jrd+json'}
#try:
result = getJson(session, url, hdr, par)
#except:
2019-07-01 21:01:43 +00:00
# print("Unable to webfinger " + url + ' ' + str(hdr) + ' ' + str(par))
storeWebfingerInCache(username+'@'+wfDomain,result,cachedWebfingers)
2019-06-28 18:55:29 +00:00
return result
2019-07-01 11:09:09 +00:00
def generateMagicKey(publicKeyPem) -> str:
2019-06-28 18:55:29 +00:00
"""See magic_key method in
https://github.com/tootsuite/mastodon/blob/707ddf7808f90e3ab042d7642d368c2ce8e95e6f/app/models/account.rb
"""
privkey = RSA.importKey(publicKeyPem)
mod = base64.urlsafe_b64encode(number.long_to_bytes(privkey.n)).decode("utf-8")
pubexp = base64.urlsafe_b64encode(number.long_to_bytes(privkey.e)).decode("utf-8")
return f"data:application/magic-public-key,RSA.{mod}.{pubexp}"
2019-07-02 20:54:22 +00:00
def storeWebfingerEndpoint(username: str,domain: str,baseDir: str, \
wfJson: {}) -> bool:
2019-06-28 18:55:29 +00:00
"""Stores webfinger endpoint for a user to a file
"""
handle=username+'@'+domain
wfSubdir='/wfendpoints'
if not os.path.isdir(baseDir+wfSubdir):
os.mkdir(baseDir+wfSubdir)
filename=baseDir+wfSubdir+'/'+handle.lower()+'.json'
with open(filename, 'w') as fp:
commentjson.dump(wfJson, fp, indent=4, sort_keys=False)
return True
2019-07-02 20:54:22 +00:00
def createWebfingerEndpoint(username: str,domain: str,port: int, \
https: bool,publicKeyPem) -> {}:
2019-06-28 18:55:29 +00:00
"""Creates a webfinger endpoint for a user
"""
prefix='https'
if not https:
prefix='http'
2019-06-30 18:23:18 +00:00
if port!=80 and port!=443:
domain=domain+':'+str(port)
2019-06-28 18:55:29 +00:00
account = {
"aliases": [
prefix+"://"+domain+"/@"+username,
prefix+"://"+domain+"/users/"+username
],
"links": [
{
"href": prefix+"://"+domain+"/@"+username,
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html"
},
{
"href": prefix+"://"+domain+"/users/"+username+".atom",
"rel": "http://schemas.google.com/g/2010#updates-from",
"type": "application/atom+xml"
},
{
"href": prefix+"://"+domain+"/users/"+username,
"rel": "self",
"type": "application/activity+json"
},
{
"href": prefix+"://"+domain+"/api/salmon/1",
"rel": "salmon"
},
{
"href": generateMagicKey(publicKeyPem),
"rel": "magic-public-key"
},
{
"rel": "http://ostatus.org/schema/1.0/subscribe",
"template": prefix+"://"+domain+"/authorize_interaction?uri={uri}"
}
],
"subject": "acct:"+username+"@"+domain
}
return account
def webfingerMeta() -> str:
"""
"""
return "<?xml version=1.0' encoding=UTF-8'?>" \
"<XRD xmlns=http://docs.oasis-open.org/ns/xri/xrd-1.0'" \
" xmlns:hm=http://host-meta.net/xrd/1.0'>" \
"" \
"<hm:Host>example.com</hm:Host>" \
"" \
"<Link rel=lrdd" \
" template=http://example.com/describe?uri={uri}'>" \
" <Title>Resource Descriptor</Title>" \
" </Link>" \
"</XRD>"
2019-07-01 11:09:09 +00:00
def webfingerLookup(path: str,baseDir: str) -> {}:
2019-06-28 18:55:29 +00:00
"""Lookup the webfinger endpoint for an account
"""
2019-07-01 21:01:43 +00:00
print('############### _webfinger lookup 1')
2019-06-28 18:55:29 +00:00
if not path.startswith('/.well-known/webfinger?'):
return None
2019-07-01 21:01:43 +00:00
print('############### _webfinger lookup 2')
2019-06-28 18:55:29 +00:00
handle=None
if 'resource=acct:' in path:
2019-07-01 21:01:43 +00:00
print('############### _webfinger lookup 3')
2019-06-28 18:55:29 +00:00
handle=path.split('resource=acct:')[1].strip()
else:
2019-07-01 21:01:43 +00:00
print('############### _webfinger lookup 4')
2019-06-28 18:55:29 +00:00
if 'resource=acct%3A' in path:
2019-07-01 21:01:43 +00:00
print('############### _webfinger lookup 5')
handle=path.split('resource=acct%3A')[1].replace('%40','@').strip()
print('############### _webfinger lookup 6')
2019-06-28 18:55:29 +00:00
if not handle:
return None
2019-07-01 21:01:43 +00:00
print('############### _webfinger lookup 7')
2019-06-28 18:55:29 +00:00
if '&' in handle:
handle=handle.split('&')[0].strip()
2019-07-01 21:01:43 +00:00
print('############### _webfinger lookup 8')
2019-06-28 18:55:29 +00:00
if '@' not in handle:
return None
filename=baseDir+'/wfendpoints/'+handle.lower()+'.json'
2019-07-01 21:01:43 +00:00
print('############### _webfinger lookup 9: '+filename)
2019-06-28 18:55:29 +00:00
if not os.path.isfile(filename):
return None
2019-07-01 21:01:43 +00:00
print('############### _webfinger lookup 10')
2019-06-28 18:55:29 +00:00
wfJson={"user": "unknown"}
with open(filename, 'r') as fp:
wfJson=commentjson.load(fp)
return wfJson