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"))
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
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)
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)
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)
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)
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)
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")
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)
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))
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)