Skip to content

Commit

Permalink
Merge pull request #108 from PlaidWeb/feature/93-relmeauth
Browse files Browse the repository at this point in the history
Add support for RelMeAuth, sort of
  • Loading branch information
fluffy-critter committed Feb 26, 2023
2 parents bf94f88 + 04c2b66 commit dc9f73c
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 8 deletions.
29 changes: 21 additions & 8 deletions authl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,17 @@ def get_handler_for_url(self, url: str) -> typing.Tuple[typing.Optional[handlers
:returns: a tuple of ``(handler, hander_id, profile_url)``.
"""
# pylint:disable=too-many-return-statements

url = url.strip()

# If webfinger detects profiles for this address, try all of those first
for profile in webfinger.get_profiles(url):
LOGGER.debug("Checking profile %s", profile)
resp = self.get_handler_for_url(profile)
if resp[0]:
return resp

if not url:
return None, '', ''

# check webfinger profiles
resp = self.check_profiles(webfinger.get_profiles(url))
if resp and resp[0]:
return resp

by_url = self._match_url(url)
if by_url[0]:
return by_url
Expand All @@ -104,13 +102,28 @@ def get_handler_for_url(self, url: str) -> typing.Tuple[typing.Optional[handlers
LOGGER.debug("%s response matches %s", profile, handler)
return handler, hid, request.url

# check for RelMeAuth candidates
resp = self.check_profiles(utils.extract_rel('me', profile, soup, request.links))
if resp and resp[0]:
return resp

LOGGER.debug("No handler found for URL %s", url)
return None, '', ''

def get_handler_by_id(self, handler_id):
""" Get the handler with the given ID, for a transaction in progress. """
return self._handlers.get(handler_id)

def check_profiles(self, profiles) -> typing.Tuple[typing.Optional[handlers.Handler], str, str]:
""" Given a list of profile URLs, check them for a handle-able identity """
for profile in profiles:
LOGGER.debug("Checking profile %s", profile)
resp = self.get_handler_for_url(profile)
if resp and resp[0]:
return resp

return None, '', ''

@property
def handlers(self):
""" Provides a list of all of the registered handlers. """
Expand Down
16 changes: 16 additions & 0 deletions authl/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,19 @@ def pkce_challenge(verifier: str, method: str = 'S256') -> str:
return encoded.decode().strip('=')

raise ValueError(f'Unknown PKCE method {method}')


def extract_rel(rel: str, base_url, content, links) -> typing.Set[str]:
""" Given a parsed page/response, extract all of the URLs that match a particular link rel """
result: typing.Set[str] = set()

if links and rel in links:
LOGGER.debug("%s: Found %s link header: %s", base_url, rel, links[rel]['url'])
result.add(links[rel]['url'])

if content:
for link in content.find_all(('link', 'a'), rel=rel):
LOGGER.debug("%s: Found %s link tag: %s", base_url, rel, link.get('href'))
result.add(urllib.parse.urljoin(base_url, link.get('href')))

return result
29 changes: 29 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,32 @@ def test_scheme_fallback(requests_mock):
'http://foo/bar') == (handler_http, 'insecure', 'http://foo/bar')
assert instance.get_handler_for_url('foo/bar') == (handler_https, 'secure', 'https://foo/bar')
assert instance.get_handler_for_url('example://foo') == (None, '', '')


def test_relme_auth(requests_mock):
""" test RelMeAuth profile support """
handler_1 = UrlHandler('test://foo', 'a')
handler_2 = UrlHandler('https://social.example/bob', 'b')
instance = Authl([handler_1, handler_2])

requests_mock.get('https://foo-link.example/', text='<link rel="me" href="test://foo">')
requests_mock.get('https://foo-a.example/', text='<a rel="me" href="test://foo">')
requests_mock.get('https://header.example/', headers={'Link': '<test://foo>; rel="me"'})

requests_mock.get('https://social.example/relative-link', text='<link rel="me" href="bob">')
requests_mock.get('https://social.example/relative-a', text='<a rel="me" href="bob">')
requests_mock.get('https://multiple.example/', text='''
<link rel="me" href="test://bar">
<link rel="me" href="test://baz">
<a href="https://social.example/bob" rel="me">
''')

for url in ('https://foo-link.example/',
'https://foo-a.example',
'https://header.example'):
assert instance.get_handler_for_url(url) == (handler_1, 'a', 'test://foo')

for url in ('https://social.example/relative-link',
'https://social.example/relative-a',
'https://multiple.example/'):
assert instance.get_handler_for_url(url) == (handler_2, 'b', 'https://social.example/bob')

0 comments on commit dc9f73c

Please sign in to comment.