Beispiel #1
0
    def test_bad_source_url(self, mock_get, mock_post):
        got = application.get_response('/webmention', method='POST', body=b'')
        self.assertEqual(400, got.status_int)

        mock_get.side_effect = ValueError('foo bar')
        got = application.get_response('/webmention',
                                       method='POST',
                                       body=urlencode({
                                           'source': 'bad'
                                       }).encode())
        self.assertEqual(400, got.status_int)
Beispiel #2
0
    def test_render_errors(self):
        for source, target in ('', ''), ('abc', ''), ('', 'xyz'):
            resp = application.get_response('/render?source=%s&target=%s' %
                                            (source, target))
            self.assertEqual(400, resp.status_int, resp.body)

        # no Response
        resp = application.get_response('/render?source=abc&target=xyz')
        self.assertEqual(404, resp.status_int)

        # no source data
        Response(id='abc xyz').put()
        resp = application.get_response('/render?source=abc&target=xyz')
        self.assertEqual(404, resp.status_int)
Beispiel #3
0
 def test_render_atom(self):
     Response(id='abc xyz', source_atom=self.atom).put()
     resp = application.get_response('/render?source=abc&target=xyz')
     self.assertEqual(200, resp.status_int)
     self.assert_multiline_equals(self.html,
                                  resp.body.decode(),
                                  ignore_blanks=True)
Beispiel #4
0
    def _test_inbox_mention(self, as2, mock_head, mock_get, mock_post):
        mock_head.return_value = requests_response(url='http://target')
        mock_get.return_value = requests_response(
            '<html><head><link rel="webmention" href="/webmention"></html>')
        mock_post.return_value = requests_response()

        got = application.get_response('/foo.com/inbox',
                                       method='POST',
                                       body=json_dumps(as2).encode())
        self.assertEqual(200, got.status_int, got.body)
        mock_get.assert_called_once_with('http://target/',
                                         headers=common.HEADERS,
                                         verify=False)

        expected_headers = copy.deepcopy(common.HEADERS)
        expected_headers['Accept'] = '*/*'
        mock_post.assert_called_once_with(
            'http://target/webmention',
            data={
                'source':
                'http://localhost/render?source=http%3A%2F%2Fthis%2Fmention&target=http%3A%2F%2Ftarget%2F',
                'target': 'http://target/',
            },
            allow_redirects=False,
            headers=expected_headers,
            verify=False)

        resp = Response.get_by_id('http://this/mention http://target/')
        self.assertEqual('in', resp.direction)
        self.assertEqual('activitypub', resp.protocol)
        self.assertEqual('complete', resp.status)
        self.assertEqual(self.handler.redirect_unwrap(as2),
                         json_loads(resp.source_as2))
Beispiel #5
0
 def test_bad_inner_xml(self, *mocks):
     slap = magicsigs.magic_envelope('not xml', common.CONTENT_TYPE_ATOM,
                                     self.key)
     got = application.get_response('/foo.com/salmon',
                                    method='POST',
                                    body=slap)
     self.assertEqual(400, got.status_int)
Beispiel #6
0
    def test_webfinger_handler_custom_username(self, mock_get):
        self.html = """
<body class="h-card">
<a class="u-url" rel="me" href="/about-me">
  <img class="u-photo" src="/me.jpg" />
  Mrs. ☕ Foo
</a>
<a class="u-url" href="acct:[email protected]"></a>
<a class="u-url" href="acct:[email protected]"></a>
</body>
"""
        self.expected_webfinger['subject'] = "acct:[email protected]"
        self.expected_webfinger['aliases'] = [u'https://foo.com/about-me',
            u'acct:[email protected]',
            u'acct:[email protected]',
            u'https://foo.com/']
        mock_get.return_value = requests_response(self.html, url='https://foo.com/')

        for resource in ('*****@*****.**', 'acct:[email protected]',
                         'foo.com', 'http://foo.com/', 'https://foo.com/'):
            url = '/.well-known/webfinger?%s' % urllib.parse.urlencode(
                {'resource': resource})
            got = application.get_response(url, headers={'Accept': 'application/json'})
            body = got.body.decode()
            self.assertEqual(200, got.status_int, body)
            self.assertEqual('application/json; charset=utf-8',
                              got.headers['Content-Type'])
            self.assertEqual(self.expected_webfinger, json_loads(body))
Beispiel #7
0
 def test_host_meta_handler_xrds(self):
     got = application.get_response('/.well-known/host-meta.xrds')
     self.assertEqual(200, got.status_int)
     self.assertEqual('application/xrds+xml; charset=utf-8',
                       got.headers['Content-Type'])
     body = got.body.decode()
     self.assertTrue(body.startswith('<XRDS'), body)
Beispiel #8
0
    def test_activitypub_create_default_url_to_wm_source(
            self, mock_get, mock_post):
        """Source post has no u-url. AS2 id should default to webmention source."""
        missing_url = requests_response("""\
<html>
<body class="h-entry">
<a class="u-repost-of p-name" href="http://orig/post">reposted!</a>
<a class="p-author h-card" href="http://orig">Ms. ☕ Baz</a>
<a href="http://localhost/"></a>
</body>
</html>
""",
                                        content_type=CONTENT_TYPE_HTML)
        mock_get.side_effect = [missing_url, self.orig_as2, self.actor]
        mock_post.return_value = requests_response('abc xyz', status=203)

        got = application.get_response('/webmention',
                                       method='POST',
                                       body=urlencode({
                                           'source':
                                           'http://a/repost',
                                           'target':
                                           'https://fed.brid.gy/',
                                       }).encode())
        self.assertEqual(203, got.status_int)

        args, kwargs = mock_post.call_args
        self.assertEqual(('https://foo.com/inbox', ), args)
        self.assert_equals(self.repost_as2, json_loads(kwargs['data']))
Beispiel #9
0
    def test_activitypub_link_rel_alternate_as2(self, mock_get, mock_post):
        mock_get.side_effect = [
            self.reply, self.not_fediverse, self.orig_html_as2, self.orig_as2,
            self.actor
        ]
        mock_post.return_value = requests_response('abc xyz')

        got = application.get_response('/webmention',
                                       method='POST',
                                       body=urlencode({
                                           'source':
                                           'http://a/reply',
                                           'target':
                                           'https://fed.brid.gy/',
                                       }).encode())
        self.assertEqual(200, got.status_int)

        mock_get.assert_has_calls((
            self.req('http://a/reply'),
            self.req('http://not/fediverse', headers=CONNEG_HEADERS_AS2_HTML),
            self.req('http://orig/post', headers=CONNEG_HEADERS_AS2_HTML),
            self.req('http://orig/as2', headers=CONNEG_HEADERS_AS2),
            self.req('http://orig/author', headers=CONNEG_HEADERS_AS2_HTML),
        ))

        args, kwargs = mock_post.call_args
        self.assertEqual(('https://foo.com/inbox', ), args)
        self.assertEqual(self.as2_create, json_loads(kwargs['data']))
Beispiel #10
0
    def test_activitypub_follow(self, mock_get, mock_post):
        mock_get.side_effect = [self.follow, self.actor]
        mock_post.return_value = requests_response('abc xyz')

        got = application.get_response('/webmention',
                                       method='POST',
                                       body=urlencode({
                                           'source':
                                           'http://a/follow',
                                           'target':
                                           'https://fed.brid.gy/',
                                       }).encode())
        self.assertEqual(200, got.status_int)

        mock_get.assert_has_calls((
            self.req('http://a/follow'),
            self.req('http://followee/', headers=CONNEG_HEADERS_AS2_HTML),
        ))

        args, kwargs = mock_post.call_args
        self.assertEqual(('https://foo.com/inbox', ), args)
        self.assertEqual(self.follow_as2, json_loads(kwargs['data']))

        headers = kwargs['headers']
        self.assertEqual(CONTENT_TYPE_AS2, headers['Content-Type'])

        rsa_key = kwargs['auth'].header_signer._rsa._key
        self.assertEqual(self.key.private_pem(), rsa_key.exportKey())

        resp = Response.get_by_id('http://a/follow http://followee/')
        self.assertEqual('out', resp.direction)
        self.assertEqual('activitypub', resp.protocol)
        self.assertEqual('complete', resp.status)
        self.assertEqual(self.follow_mf2, json_loads(resp.source_mf2))
Beispiel #11
0
    def test_salmon_relative_atom_href_with_base(self, mock_get, mock_post):
        orig_base = requests_response(
            """\
<html>
<meta>
<base href='/base/'>
<link href='atom/1' rel='alternate' type='application/atom+xml'>
</meta>
</html>""", 'http://orig/url')
        mock_get.side_effect = [
            self.reply, self.not_fediverse, orig_base, self.orig_atom
        ]

        got = application.get_response('/webmention',
                                       method='POST',
                                       body=urlencode({
                                           'source':
                                           'http://a/reply',
                                           'target':
                                           'http://orig/post',
                                       }).encode())
        self.assertEqual(200, got.status_int)

        mock_get.assert_any_call('http://orig/base/atom/1',
                                 headers=HEADERS,
                                 stream=True,
                                 timeout=util.HTTP_TIMEOUT)
        data = self.verify_salmon(mock_post)
Beispiel #12
0
    def test_endpoint_param(self, mock_get):
        mock_get.return_value = self.resp

        got = application.get_response(
            '/wm/http://url?endpoint=https://end/point')
        self.assertEqual(200, got.status_int)
        self.assertEqual('<https://end/point>; rel="webmention"',
                         got.headers['Link'])
Beispiel #13
0
    def test_inbox_undo_follow_doesnt_exist(self, mock_head, mock_get,
                                            mock_post):
        mock_head.return_value = requests_response(url='https://realize.be/')

        got = application.get_response(
            '/foo.com/inbox',
            method='POST',
            body=json_dumps(UNDO_FOLLOW_WRAPPED).encode())
        self.assertEqual(200, got.status_int)
Beispiel #14
0
 def test_target_fetch_has_no_content_type(self, mock_get, mock_post):
     mock_get.side_effect = (requests_response(self.reply_html),
                             requests_response(self.reply_html,
                                               content_type='None'))
     got = application.get_response('/webmention',
                                    method='POST',
                                    body=urlencode({
                                        'source': 'http://a/post'
                                    }).encode())
     self.assertEqual(502, got.status_int)
Beispiel #15
0
    def test_inbox_follow_accept(self, mock_head, mock_get, mock_post):
        mock_head.return_value = requests_response(url='https://realize.be/')
        mock_get.side_effect = [
            # source actor
            requests_response(FOLLOW_WITH_ACTOR['actor'],
                              content_type=common.CONTENT_TYPE_AS2),
            # target post webmention discovery
            requests_response(
                '<html><head><link rel="webmention" href="/webmention"></html>'
            ),
        ]
        mock_post.return_value = requests_response()

        got = application.get_response(
            '/foo.com/inbox',
            method='POST',
            body=json_dumps(FOLLOW_WRAPPED).encode())
        self.assertEqual(200, got.status_int)

        as2_headers = copy.deepcopy(common.HEADERS)
        as2_headers.update(common.CONNEG_HEADERS_AS2_HTML)
        mock_get.assert_has_calls((call(FOLLOW['actor'],
                                        headers=as2_headers,
                                        stream=True,
                                        timeout=15), ))

        # check AP Accept
        self.assertEqual(2, len(mock_post.call_args_list))
        args, kwargs = mock_post.call_args_list[0]
        self.assertEqual(('http://follower/inbox', ), args)
        self.assertEqual(ACCEPT, kwargs['json'])

        # check webmention
        args, kwargs = mock_post.call_args_list[1]
        self.assertEqual(('https://realize.be/webmention', ), args)
        self.assertEqual(
            {
                'source':
                'http://localhost/render?source=https%3A%2F%2Fmastodon.social%2F6d1a&target=https%3A%2F%2Frealize.be%2F',
                'target': 'https://realize.be/',
            }, kwargs['data'])

        resp = Response.get_by_id(
            'https://mastodon.social/6d1a https://realize.be/')
        self.assertEqual('in', resp.direction)
        self.assertEqual('activitypub', resp.protocol)
        self.assertEqual('complete', resp.status)
        self.assertEqual(FOLLOW_WITH_ACTOR, json_loads(resp.source_as2))

        # check that we stored a Follower object
        follower = Follower.get_by_id('realize.be %s' % (FOLLOW['actor']))
        self.assertEqual('active', follower.status)
        self.assertEqual(FOLLOW_WRAPPED_WITH_ACTOR,
                         json_loads(follower.last_follow))
Beispiel #16
0
    def test_user_handler(self, mock_get):
        mock_get.return_value = requests_response(self.html, url = 'https://foo.com/')

        got = application.get_response('/acct:foo.com',
                                       headers={'Accept': 'application/json'})
        self.assertEqual(200, got.status_int)
        self.assertEqual('application/json; charset=utf-8',
                          got.headers['Content-Type'])
        mock_get.assert_called_once_with('http://foo.com/', headers=common.HEADERS,
                                         stream=True, timeout=util.HTTP_TIMEOUT)

        self.assertEqual(self.expected_webfinger, json_loads(got.body.decode()))

        # check that magic key is persistent
        again = json_loads(application.get_response(
            '/acct:foo.com', headers={'Accept': 'application/json'}).body.decode())
        self.assertEqual(self.key.href(), again['magic_keys'][0]['value'])

        links = {l['rel']: l['href'] for l in again['links']}
        self.assertEqual(self.key.href(), links['magic-public-key'])
Beispiel #17
0
    def test_inbox_undo_follow_inactive(self, mock_head, mock_get, mock_post):
        mock_head.return_value = requests_response(url='https://realize.be/')
        Follower(id=Follower._id('realize.be',
                                 'https://mastodon.social/users/swentel'),
                 status='inactive').put()

        got = application.get_response(
            '/foo.com/inbox',
            method='POST',
            body=json_dumps(UNDO_FOLLOW_WRAPPED).encode())
        self.assertEqual(200, got.status_int)
Beispiel #18
0
    def test_get(self, mock_get):
        self.resp.status_code = 202
        mock_get.return_value = self.resp

        got = application.get_response('/wm/http://url')
        self.assertEqual(202, got.status_int)
        self.assertEqual(self.resp._content, got.body)
        self.assertEqual(['bar'], got.headers.getall('Foo'))
        self.assertEqual(
            ['first', '<http://localhost/webmention>; rel="webmention"'],
            got.headers.getall('Link'))
Beispiel #19
0
    def test_target_fetch_fails(self, mock_get, mock_post):
        mock_get.side_effect = (requests_response(
            self.reply_html.replace('http://orig/post', 'bad'),
            content_type=CONTENT_TYPE_HTML), requests.Timeout('foo bar'))

        got = application.get_response('/webmention',
                                       method='POST',
                                       body=urlencode({
                                           'source': 'http://a/post'
                                       }).encode())
        self.assertEqual(502, got.status_int)
Beispiel #20
0
    def test_inbox_undo_follow(self, mock_head, mock_get, mock_post):
        mock_head.return_value = requests_response(url='https://realize.be/')

        Follower(id=Follower._id('realize.be', FOLLOW['actor'])).put()

        got = application.get_response(
            '/foo.com/inbox',
            method='POST',
            body=json_dumps(UNDO_FOLLOW_WRAPPED).encode())
        self.assertEqual(200, got.status_int)

        follower = Follower.get_by_id('realize.be %s' % FOLLOW['actor'])
        self.assertEqual('inactive', follower.status)
Beispiel #21
0
    def test_user_handler_no_hcard(self, mock_get):
        mock_get.return_value = requests_response("""
<body>
<div class="h-entry">
  <p class="e-content">foo bar</p>
</div>
</body>
""")
        got = application.get_response('/acct:foo.com')
        mock_get.assert_called_once_with('http://foo.com/', headers=common.HEADERS,
                                         stream=True, timeout=util.HTTP_TIMEOUT)
        self.assertEqual(400, got.status_int)
        self.assertIn('representative h-card', got.body.decode())
Beispiel #22
0
    def test_webfinger_handler(self, mock_get):
        mock_get.return_value = requests_response(self.html, url='https://foo.com/')

        for resource in ('*****@*****.**', 'acct:[email protected]', '*****@*****.**',
                         'foo.com', 'http://foo.com/', 'https://foo.com/'):
            url = '/.well-known/webfinger?%s' % urllib.parse.urlencode(
                {'resource': resource})
            got = application.get_response(url, headers={'Accept': 'application/json'})
            body = got.body.decode()
            self.assertEqual(200, got.status_int, body)
            self.assertEqual('application/json; charset=utf-8',
                              got.headers['Content-Type'])
            self.assertEqual(self.expected_webfinger, json_loads(body))
Beispiel #23
0
    def test_inbox_delete_actor(self, mock_head, mock_get, mock_post):
        follower = Follower.get_or_create('realize.be', DELETE['actor'])
        Follower.get_or_create('snarfed.org', DELETE['actor'])
        # other unrelated follower
        other = Follower.get_or_create('realize.be',
                                       'https://mas.to/users/other')
        self.assertEqual(3, Follower.query().count())

        got = application.get_response('/realize.be/inbox',
                                       method='POST',
                                       body=json_dumps(DELETE).encode())
        self.assertEqual(200, got.status_int)

        self.assertEqual([other], Follower.query().fetch())
Beispiel #24
0
    def test_salmon_reply(self, mock_get, mock_post):
        mock_get.side_effect = [
            self.reply, self.not_fediverse, self.orig_html_atom, self.orig_atom
        ]

        got = application.get_response('/webmention',
                                       method='POST',
                                       body=urlencode({
                                           'source':
                                           'http://a/reply',
                                           'target':
                                           'http://orig/post',
                                       }).encode())
        self.assertEqual(200, got.status_int)

        mock_get.assert_has_calls((
            self.req('http://a/reply'),
            self.req('http://not/fediverse', headers=CONNEG_HEADERS_AS2_HTML),
            self.req('http://orig/post', headers=CONNEG_HEADERS_AS2_HTML),
            self.req('http://orig/atom'),
        ))

        data = self.verify_salmon(mock_post)
        parsed = feedparser.parse(data)
        entry = parsed.entries[0]

        self.assertEqual('http://a/reply', entry['id'])
        self.assertIn(
            {
                'rel': 'alternate',
                'href': 'http://a/reply',
                'type': 'text/html',
            }, entry['links'])
        self.assertEqual(
            {
                'type': 'text/html',
                'href': 'http://orig/post',
                'ref': 'tag:fed.brid.gy,2017-08-22:orig-post',
            }, entry['thr_in-reply-to'])
        self.assertEqual(
            """\
<a class="u-in-reply-to" href="http://not/fediverse"></a><br></br>
<a class="u-in-reply-to" href="http://orig/post">foo ☕ bar</a><br></br>
<a href="http://localhost/"></a>""", entry.content[0]['value'])

        resp = Response.get_by_id('http://a/reply http://orig/post')
        self.assertEqual('out', resp.direction)
        self.assertEqual('ostatus', resp.protocol)
        self.assertEqual('complete', resp.status)
        self.assertEqual(self.reply_mf2, json_loads(resp.source_mf2))
Beispiel #25
0
    def test_rsvp_not_supported(self, *mocks):
        slap = magicsigs.magic_envelope(
            """\
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom'
       xmlns:activity='http://activitystrea.ms/spec/1.0/'>
  <uri>https://my/rsvp</uri>
  <activity:verb>http://activitystrea.ms/schema/1.0/rsvp</activity:verb>
  <activity:object>http://orig/event</activity:object>
</entry>""", common.CONTENT_TYPE_ATOM, self.key)
        got = application.get_response('/foo.com/salmon',
                                       method='POST',
                                       body=slap)
        self.assertEqual(501, got.status_int)
Beispiel #26
0
    def test_user_handler_with_push_header(self, mock_get):
        mock_get.return_value = requests_response(
            self.html, url = 'https://foo.com/', headers={
                'Link': 'badly formatted, '
                        "<xyz>; rel='foo',"
                        '<http://a.custom.hub/>; rel="hub"',
            })

        got = application.get_response('/acct:foo.com',
                                       headers={'Accept': 'application/json'})
        self.assertEqual(200, got.status_int)
        self.assertIn({
            'rel': 'hub',
            'href': 'http://a.custom.hub/',
        }, json_loads(got.body.decode())['links'])
Beispiel #27
0
    def test_no_backlink(self, mock_get, mock_post):
        mock_get.return_value = requests_response(
            self.reply_html.replace('<a href="http://localhost/"></a>', ''),
            content_type=CONTENT_TYPE_HTML)

        got = application.get_response('/webmention',
                                       method='POST',
                                       body=urlencode({
                                           'source':
                                           'http://a/post',
                                           'target':
                                           'https://fed.brid.gy/',
                                       }).encode())
        self.assertEqual(400, got.status_int)

        mock_get.assert_has_calls((self.req('http://a/post'), ))
Beispiel #28
0
 def test_inbox_unsupported_type(self, *_):
     got = application.get_response(
         '/foo.com/inbox',
         method='POST',
         body=json_dumps({
             '@context': ['https://www.w3.org/ns/activitystreams'],
             'id':
             'https://xoxo.zone/users/aaronpk#follows/40',
             'type':
             'Block',
             'actor':
             'https://xoxo.zone/users/aaronpk',
             'object':
             'http://snarfed.org/',
         }).encode())
     self.assertEqual(501, got.status_int)
Beispiel #29
0
    def send_slap(self, mock_urlopen, mock_head, mock_get, mock_post,
                  atom_slap):
        # salmon magic key discovery. first host-meta, then webfinger
        mock_urlopen.side_effect = [
            UrlopenResult(
                200, """\
<?xml version='1.0' encoding='UTF-8'?>
<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
  <Link rel='lrdd' type='application/xrd+xml' template='http://webfinger/{uri}' />
</XRD>"""),
            UrlopenResult(
                200, """\
<?xml version='1.0' encoding='UTF-8'?>
<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
    <Subject>[email protected]</Subject>
    <Link rel='magic-public-key' href='%s' />
</XRD>""" % self.key.href()),
        ]

        # webmention discovery
        mock_head.return_value = requests_response(url='http://orig/post')
        mock_get.return_value = requests_response(
            '<html><head><link rel="webmention" href="/webmention"></html>')
        # webmention post
        mock_post.return_value = requests_response()

        slap = magicsigs.magic_envelope(atom_slap, common.CONTENT_TYPE_ATOM,
                                        self.key)
        got = application.get_response('/[email protected]/salmon',
                                       method='POST',
                                       body=slap)
        self.assertEqual(200, got.status_int)

        # check salmon magic key discovery
        mock_urlopen.assert_has_calls((
            mock.call('http://fedsoc.net/.well-known/host-meta'),
            mock.call('http://webfinger/[email protected]'),
        ))

        # check webmention discovery
        self.expected_headers = copy.deepcopy(common.HEADERS)
        self.expected_headers['Accept'] = '*/*'
        mock_get.assert_called_once_with('http://orig/post',
                                         headers=common.HEADERS,
                                         verify=False)
Beispiel #30
0
    def test_activitypub_create_author_only_url(self, mock_get, mock_post):
        """Mf2 author property is just a URL. We should run full authorship.

        https://indieweb.org/authorship
        """
        repost = requests_response("""\
<html>
<body class="h-entry">
<a class="u-repost-of p-name" href="http://orig/post">reposted!</a>
<a class="u-author" href="http://orig"></a>
<a href="http://localhost/"></a>
</body>
</html>
""",
                                   content_type=CONTENT_TYPE_HTML)
        author = requests_response("""\
<html>
<body class="h-card">
<a class="p-name u-url" rel="me" href="http://orig">Ms. ☕ Baz</a>
<img class="u-photo" src="/pic" />
</body>
</html>
""",
                                   url='http://orig',
                                   content_type=CONTENT_TYPE_HTML)
        mock_get.side_effect = [repost, author, self.orig_as2, self.actor]
        mock_post.return_value = requests_response('abc xyz', status=201)

        got = application.get_response('/webmention',
                                       method='POST',
                                       body=urlencode({
                                           'source':
                                           'http://a/repost',
                                           'target':
                                           'https://fed.brid.gy/',
                                       }).encode())
        self.assertEqual(201, got.status_int)

        args, kwargs = mock_post.call_args
        self.assertEqual(('https://foo.com/inbox', ), args)

        repost_as2 = copy.deepcopy(self.repost_as2)
        repost_as2['actor']['image'] = repost_as2['actor']['icon'] = \
            {'type': 'Image', 'url': 'http://orig/pic'},
        self.assert_equals(repost_as2, json_loads(kwargs['data']))