예제 #1
0
    def update_callback(self, ids):
        failed_feeds = []
        feeds = Feed.query.filter(Feed.id.in_(ids)).all()
        app.logger.info(u"Admin Updating Callback URL for Feeds: {0}".format(u", ".join(map(str, feeds))))
        for feed in feeds:
            if feed.status != STATUS.UNSUBSCRIBED:
                try:
                    result = subscriber.unsubscribe(feed.topic)
                    if result[0] in [200, 202, 204]:
                        feed.callback_url = get_public_url(feed)
                        result = subscriber.subscribe(feed.topic, find_feed=False)
                        flash_status(result)
                        db.session.add(feed)
                    else:
                        failed_feeds.append(feed)
                except Exception as e:
                    app.logger.error(str(e))
                    flash(str(e), ALERT.ERROR)
            else:
                feed.callback_url = get_public_url(feed)
                db.session.add(feed)

        db.session.commit()

        if failed_feeds:
            flash(u"Callback URL Update failed for: {0}".format(u", ".join(map(str, failed_feeds))))

        return redirect(url_for("feedview.index_view"))
예제 #2
0
    def create_feed_from_feedinfo(self, feed_info, hub=None, feed_format=None):

        if not hub:
            if feed_info.hub:
                hub = feed_info.hub
            else:
                hub = app.config['DEFAULT_HUB']

        feed = Feed.query.filter_by(topic=feed_info.url).first()
        if not feed:
            feed = Feed(topic=feed_info.url, hub=hub)

            feed.create_unique_url()
            feed.create_secret()

            feed.feed_format = feed_format if feed_format else feed.feed_format

            if not feed.callback_url:
                feed.callback_url = get_public_url(feed)

        feed.site_url = feed_info.site_url,
        feed.description = feed_info.descripton,
        feed.site_name = feed_info.site_name,
        feed.title = feed_info.title,
        feed.site_icon_link = feed_info.site_icon_link

        return feed
예제 #3
0
    def unsubscribe(self, topic):
        """
        Unsubscribes from a topic.

        :param topic: URL of the feed to unsubscribe from
        :return: method
        """

        feed = Feed.query.filter_by(topic=topic).first()
        if not feed:
            return Response(status=403)

        feed.status = STATUS.PENDING_UNSUB

        db.session.add(feed)
        db.session.commit()

        if feed.callback_url:
            callback_url = feed.callback_url
        else:
            callback_url = get_public_url(feed)

        mode = 'unsubscribe'
        auth = (app.config['PUBSUB_USER'], app.config['PUBSUB_PASS'])

        r = self.send_subscription(topic,
                                   feed.hub,
                                   callback_url,
                                   mode,
                                   auth=auth,
                                   secret=feed.secret)

        return self.handle_subscription_response(feed, mode, r)
예제 #4
0
    def test_verification_subscribe_is_successful(self):
        mode = "subscribe"
        challenge = b"np2ik162yb9"
        lease_seconds = 315360000

        feed = FeedFactory(status=STATUS.PENDING_SUB)
        db.session.commit()

        payload = {
            'hub.mode': mode,
            'hub.topic': feed.topic,
            'hub.challenge': challenge,
            'hub.lease_seconds': lease_seconds
        }

        with self.app.test_client() as c:
            response = c.get(get_public_url(feed), query_string=payload)

            self.assert200(response)
            self.assertEqual(response.data, challenge)

            self.assertEqual(feed.lease_seconds, lease_seconds)
            self.assertIsNotNone(feed.lease_end)
            self.assertIsNotNone(feed.sub_time)
            self.assertAlmostEqual(
                feed.lease_end,
                feed.sub_time + timedelta(seconds=lease_seconds),
                delta=timedelta(seconds=1))
            self.assertEqual(feed.status, STATUS.SUBSCRIBED)
예제 #5
0
    def test_get_public_url(self):
        topic = 'http://test.com/feed'
        hub = 'http://hub.com'
        feed = Feed(topic=topic, hub=hub)

        url = get_public_url(feed)
        self.assertEqual(url, 'http://localhost/push/' + feed.unique_url)
예제 #6
0
    def test_notification_path(self):
        with open(TEST_FILES_DIR + 'boingboing.xml') as f:
            data = f.read()
        user = UserFactory(active=True)
        feed = FeedFactory(status=STATUS.SUBSCRIBED)
        author = AuthorFactory(name='Cory Doctorow',
                               givenname='Cory',
                               familyname='Doctorow',
                               email='*****@*****.**')
        sub = SubscriptionFactory(user=user,
                                  author=author,
                                  active=True)
        sub.add_period(PERIOD.IMMEDIATE)
        db.session.commit()

        h = hmac.new(bytes(feed.secret, 'UTF-8'), digestmod=hashlib.sha1)
        h.update(data.encode('UTF-8'))
        digest = h.hexdigest()
        sig = "sha1=" + digest

        headers = {}
        headers['X-Hub-Signature'] = sig
        headers['content-type'] = 'application/rss+xml'
        headers['Link'] = str(LinkHeader([Link(feed.hub,
                                               rel="hub"), Link(feed.topic,
                                                                rel="self")]))

        with self.app.test_client() as c:
            notification_received.connect(when_notification_received)
            entries_added.connect(when_entries_added)
            update_user_rss.connect(when_update_user_rss)

            with mail.record_messages() as outbox:
                response = c.post(get_public_url(feed),
                                  headers=headers,
                                  data=data)

                self.assertEqual(response.status_code, 200)

                authors = db.session.query(Author).all()
                self.assertEqual(len(authors), 6)

                entries = db.session.query(Entry).all()
                self.assertEqual(len(entries), 30)
                self.assertIs(type(entries[0].content), str)

                self.assertEqual(len(outbox), 1)

                emails = Email.query.all()
                self.assertEqual(len(emails), 1)
                self.assertEqual(emails[0].address, user.email)
예제 #7
0
    def subscribe(self, topic, hub=None, feed_format=None, find_feed=True):
        """
        Creates a PubSubHubbub subscription to a topic.

        :param topic: URL to subscribe to
        :param hub: PuSH Hub to send subscription request to
        :param feed_format: Whether notifications should be in RSS or JSON format
        :param find_feed: Set to True to search the website for a feed and override the url
            if feed is found.
        :return: method
        """

        feed = Feed.query.filter_by(topic=topic).first()
        if not feed:
            feed = self.create_feed(topic, hub, find_feed)

        feed.status = STATUS.PENDING_SUB

        feed_format = feed_format if feed_format else feed.feed_format

        feed.create_unique_url()
        feed.create_secret()

        if not feed.callback_url:
            feed.callback_url = get_public_url(feed)

        db.session.add(feed)
        db.session.commit()

        mode = 'subscribe'
        auth = (app.config['PUBSUB_USER'], app.config['PUBSUB_PASS'])

        r = self.send_subscription(feed.topic,
                                   feed.hub,
                                   feed.callback_url,
                                   mode,
                                   auth=auth,
                                   secret=feed.secret,
                                   feed_format=feed_format,
                                   retrieve='true',
                                   lease_seconds=2592000)

        return self.handle_subscription_response(feed, mode, r)
예제 #8
0
    def test_notification(self):
        feed = FeedFactory(status=STATUS.SUBSCRIBED)
        db.session.commit()

        h = hmac.new(bytes(feed.secret, 'UTF-8'), digestmod=hashlib.sha1)
        h.update(jsondata.encode('UTF-8'))
        digest = h.hexdigest()
        sig = "sha1=" + digest

        headers = {}
        headers['X-Hub-Signature'] = sig
        headers['content-type'] = 'application/json'
        headers['Link'] = str(LinkHeader([Link(feed.hub,
                                               rel="hub"), Link(feed.topic,
                                                                rel="self")]))

        with self.app.test_client() as c:
            notification_received.connect(when_notification_received)
            response = c.post(get_public_url(feed),
                              headers=headers,
                              data=jsondata)

            self.assertEqual(response.status_code, 200)

            self.assertAlmostEqual(feed.last_update_received,
                                   datetime.utcnow(),
                                   delta=timedelta(seconds=1))

            self.assertAlmostEqual(feed.updated_on,
                                   datetime.utcnow(),
                                   delta=timedelta(seconds=1))
            self.assertEqual(feed.title, 'A wonderful feed')

            entryCount = Entry.query.count()
            self.assertEqual(entryCount, 2)

            entries = Entry.query.all()
            self.assertIsNotNone(entries)

            self.assertEqual(len(entries), 2)
            entry1 = next((e for e in entries if e.link == "http://domain.tld/entry/1"), None)
            self.assertIsNotNone(entry1)
            self.assertEqual(entry1.link, "http://domain.tld/entry/1")
예제 #9
0
    def test_notification_rss(self):
        feed = FeedFactory(status=STATUS.SUBSCRIBED)
        db.session.commit()

        with open(TEST_FILES_DIR + 'rss.xml') as f:
            data = f.read()

        h = hmac.new(bytes(feed.secret, 'UTF-8'), digestmod=hashlib.sha1)
        h.update(data.encode('UTF-8'))
        digest = h.hexdigest()
        sig = "sha1=" + digest

        headers = {}
        headers['X-Hub-Signature'] = sig
        headers['content-type'] = 'application/rss+xml'
        headers['Link'] = str(LinkHeader([Link(feed.hub,
                                               rel="hub"), Link(feed.topic,
                                                                rel="self")]))

        with self.app.test_client() as c:
            notification_received.connect(when_notification_received)
            response = c.post(get_public_url(feed),
                              headers=headers,
                              data=data)

            self.assertEqual(response.status_code, 200)

            self.assertAlmostEqual(feed.last_update_received,
                                   datetime.utcnow(),
                                   delta=timedelta(seconds=1))

            self.assertAlmostEqual(feed.updated_on,
                                   datetime.utcnow(),
                                   delta=timedelta(seconds=1))
            self.assertEqual(feed.title, 'David Beath')

            entryCount = Entry.query.count()
            self.assertEqual(entryCount, 10)
예제 #10
0
    def test_verification_denied_is_successful(self):
        mode = "denied"
        challenge = "np2ik162yb9"
        lease_seconds = 315360000

        feed = FeedFactory(status=STATUS.PENDING_UNSUB)
        db.session.commit()

        payload = {
            'hub.mode': mode,
            'hub.topic': feed.topic,
            'hub.challenge': challenge,
            'hub.lease_seconds': lease_seconds
        }

        with self.app.test_client() as c:
            response = c.get(get_public_url(feed), query_string=payload)

            self.assert200(response)

            self.assertEqual(feed.status, STATUS.DENIED)
            self.assertAlmostEqual(feed.unsub_time,
                                   datetime.utcnow(),
                                   delta=timedelta(seconds=1))
예제 #11
0
    def test_json_notification_path(self):
        with open(TEST_FILES_DIR + 'notification.json') as f:
            data = f.read()
        user = UserFactory(active=True)
        feed = FeedFactory(status=STATUS.SUBSCRIBED,
                           topic='http://testfeed.test')
        author = AuthorFactory(name='Testy McTesterson',
                               givenname='Testy',
                               familyname='McTesterson')
        sub = SubscriptionFactory(user=user,
                                  author=author,
                                  active=True)
        sub.add_period(PERIOD.IMMEDIATE)
        db.session.commit()

        h = hmac.new(bytes(feed.secret, 'UTF-8'), digestmod=hashlib.sha1)
        h.update(data.encode('UTF-8'))
        digest = h.hexdigest()
        sig = "sha1=" + digest

        headers = {}
        headers['X-Hub-Signature'] = sig
        headers['content-type'] = 'application/json'
        headers['Link'] = str(LinkHeader([Link(feed.hub,
                                               rel='hub'),
                                          Link(feed.topic,
                                               rel='self')]))

        with self.app.test_client() as c:
            notification_received.connect(when_notification_received)
            entries_added.connect(when_entries_added)
            update_user_rss.connect(when_update_user_rss)

            with mail.record_messages() as outbox:
                response = c.post(get_public_url(feed),
                                  headers=headers,
                                  data=data)

                self.assertEqual(response.status_code, 200)

                authors = db.session.query(Author).all()
                self.assertEqual(len(authors), 3)

                author2 = Author.query.filter_by(name='John Doe').first()
                self.assertEqual(author2.name, 'John Doe')
                self.assertEqual(author2.givenname, 'John')
                self.assertEqual(author2.familyname, 'Doe')

                author3 = Author.query.filter_by(name=u'Tĕstá ĀũʈĥőŘ').first()
                self.assertEqual(author3.name, u'Tĕstá ĀũʈĥőŘ')
                self.assertEqual(author3.givenname, u'Tĕstá')
                self.assertEqual(author3.familyname, u'ĀũʈĥőŘ')

                entries = Entry.query.all()
                self.assertEqual(len(entries), 2)
                self.assertIs(type(entries[0].content), str)
                self.assertIs(type(entries[1].content), str)

                entry2 = Entry.query.filter_by(guid='domain.tld/2015-12-02').first()
                self.assertEqual(entry2.content,
                                 u'This is the second entry, it contains unicode Tĕstá ĀũʈĥőŘ')

                self.assertEqual(len(outbox), 1)

                emails = Email.query.all()
                self.assertEqual(len(emails), 1)
                self.assertEqual(emails[0].address, user.email)