-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathwebfinger.py
164 lines (114 loc) · 4.68 KB
/
webfinger.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
from __future__ import print_function
import logging
__version__ = '1.0'
RELS = {
'activity_streams': 'http://activitystrea.ms/spec/1.0',
'avatar': 'http://webfinger.net/rel/avatar',
'hcard': 'http://microformats.org/profile/hcard',
'open_id': 'http://specs.openid.net/auth/2.0/provider',
'opensocial': 'http://ns.opensocial.org/2008/opensocial/activitystreams',
'portable_contacts': 'http://portablecontacts.net/spec/1.0',
'profile': 'http://webfinger.net/rel/profile-page',
'webfist': 'http://webfist.org/spec/rel',
'xfn': 'http://gmpg.org/xfn/11',
}
WEBFINGER_TYPE = 'application/jrd+json'
LEGACY_WEBFINGER_TYPES = ['application/json']
UNOFFICIAL_ENDPOINTS = {
'facebook.com': 'facebook-webfinger.appspot.com',
'twitter.com': 'twitter-webfinger.appspot.com',
}
logger = logging.getLogger("webfinger")
class WebFingerException(Exception):
pass
class WebFingerResponse(object):
""" Response that wraps an RD object. It provides attribute-style access
to links for specific rels, responding with the href attribute
of the matched element.
"""
def __init__(self, jrd):
self.jrd = jrd
def __getattr__(self, name):
if name in RELS:
return self.rel(RELS[name])
return getattr(self.jrd, name)
@property
def subject(self):
return self.jrd.get('subject')
@property
def aliases(self):
return self.jrd.get('aliases', [])
@property
def properties(self):
return self.jrd.get('properties', {})
@property
def links(self):
return self.jrd.get('links', [])
def rel(self, relation, attr='href'):
for link in self.links:
if link.get('rel') == relation:
if attr:
return link.get(attr)
return link
class WebFingerClient(object):
def __init__(self, timeout=None, official=False):
self.official = official
self.timeout = timeout
def _parse_host(self, resource):
host = resource.split("@")[-1]
if host in UNOFFICIAL_ENDPOINTS and not self.official:
unofficial_host = UNOFFICIAL_ENDPOINTS[host]
logging.debug('host %s is not supported, using unofficial endpoint %s' % (host, unofficial_host))
host = unofficial_host
return host
def finger(self, resource, host=None, rel=None, raw=False):
import requests
if not host:
host = self._parse_host(resource)
url = "https://%s/.well-known/webfinger" % host
headers = {
'User-Agent': 'python-webfinger/%s' % __version__,
'Accept': WEBFINGER_TYPE,
}
params = {'resource': resource}
if rel:
params['rel'] = rel
resp = requests.get(url, params=params, headers=headers, timeout=self.timeout, verify=True)
logging.debug('fetching JRD from %s' % resp.url)
content_type = resp.headers.get('Content-Type', '').split(';', 1)[0].strip()
logging.debug('response content type: %s' % content_type)
if content_type != WEBFINGER_TYPE and content_type not in LEGACY_WEBFINGER_TYPES:
raise WebFingerException('Invalid response type from server')
if raw:
return resp.json()
return WebFingerResponse(resp.json())
def finger(resource, rel=None):
""" Convenience method for invoking WebFingerClient.
"""
return WebFingerClient().finger(resource, rel=rel)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description="Simple webfinger client.")
parser.add_argument("acct", metavar="URI", help="account URI")
parser.add_argument("-d", "--debug", dest="debug", action="store_true", help="print debug logging output to console")
parser.add_argument("-r", "--rel", metavar="REL", dest="rel", help="desired relation")
args = parser.parse_args()
if args.debug:
logging.basicConfig(level=logging.DEBUG)
wf = finger(args.acct, rel=args.rel)
print("--- %s ---" % wf.subject)
if args.rel:
link = wf.rel(args.rel)
if link is None:
print("*** Link not found for rel=%s" % args.rel)
print("%s:\n\t%s" % (args.rel, link))
else:
print("Activity Streams: ", wf.activity_streams)
print("Avatar: ", wf.avatar)
print("HCard: ", wf.hcard)
print("OpenID: ", wf.open_id)
print("Open Social: ", wf.opensocial)
print("Portable Contacts: ", wf.portable_contacts)
print("Profile: ", wf.profile)
print("WebFist: ", wf.webfist)
print("XFN: ", wf.rel("http://gmpg.org/xfn/11"))