diff --git a/app/controllers/api/push_controller.rb b/app/controllers/api/push_controller.rb index bc547d65..75a1f757 100644 --- a/app/controllers/api/push_controller.rb +++ b/app/controllers/api/push_controller.rb @@ -2,36 +2,66 @@ class Api::PushController < ApiController def update - mode = params['hub.mode'] - topic = params['hub.topic'] - callback = params['hub.callback'] - lease_seconds = params['hub.lease_seconds'] - secret = params['hub.secret'] - - case mode - when 'subscribe' - response, status = Pubsubhubbub::SubscribeService.new.call(topic_to_account(topic), callback, secret, lease_seconds) - when 'unsubscribe' - response, status = Pubsubhubbub::UnsubscribeService.new.call(topic_to_account(topic), callback) - else - response = "Unknown mode: #{mode}" - status = 422 - end - + response, status = process_push_request render plain: response, status: status end private - def topic_to_account(topic_url) - return if topic_url.blank? + def process_push_request + case hub_mode + when 'subscribe' + Pubsubhubbub::SubscribeService.new.call(account_from_topic, hub_callback, hub_secret, hub_lease_seconds) + when 'unsubscribe' + Pubsubhubbub::UnsubscribeService.new.call(account_from_topic, hub_callback) + else + ["Unknown mode: #{hub_mode}", 422] + end + end - uri = Addressable::URI.parse(topic_url).normalize - params = Rails.application.routes.recognize_path(uri.path) - domain = uri.host + (uri.port ? ":#{uri.port}" : '') + def hub_mode + params['hub.mode'] + end - return unless TagManager.instance.web_domain?(domain) && params[:controller] == 'accounts' && params[:action] == 'show' && params[:format] == 'atom' + def hub_topic + params['hub.topic'] + end - Account.find_local(params[:username]) + def hub_callback + params['hub.callback'] + end + + def hub_lease_seconds + params['hub.lease_seconds'] + end + + def hub_secret + params['hub.secret'] + end + + def account_from_topic + if hub_topic.present? && local_domain? && account_feed_path? + Account.find_local(hub_topic_params[:username]) + end + end + + def hub_topic_params + @_hub_topic_params ||= Rails.application.routes.recognize_path(hub_topic_uri.path) + end + + def hub_topic_uri + @_hub_topic_uri ||= Addressable::URI.parse(hub_topic).normalize + end + + def local_domain? + TagManager.instance.web_domain?(hub_topic_domain) + end + + def hub_topic_domain + hub_topic_uri.host + (hub_topic_uri.port ? ":#{hub_topic_uri.port}" : '') + end + + def account_feed_path? + hub_topic_params[:controller] == 'accounts' && hub_topic_params[:action] == 'show' && hub_topic_params[:format] == 'atom' end end diff --git a/spec/controllers/api/push_controller_spec.rb b/spec/controllers/api/push_controller_spec.rb index e699006f..18bfa70e 100644 --- a/spec/controllers/api/push_controller_spec.rb +++ b/spec/controllers/api/push_controller_spec.rb @@ -3,11 +3,56 @@ require 'rails_helper' RSpec.describe Api::PushController, type: :controller do describe 'POST #update' do context 'with hub.mode=subscribe' do - pending + it 'creates a subscription' do + service = double(call: ['', 202]) + allow(Pubsubhubbub::SubscribeService).to receive(:new).and_return(service) + account = Fabricate(:account) + account_topic_url = "https://#{Rails.configuration.x.local_domain}/users/#{account.username}.atom" + post :update, params: { + 'hub.mode' => 'subscribe', + 'hub.topic' => account_topic_url, + 'hub.callback' => 'https://callback.host/api', + 'hub.lease_seconds' => '3600', + 'hub.secret' => 'as1234df', + } + + expect(service).to have_received(:call).with( + account, + 'https://callback.host/api', + 'as1234df', + '3600', + ) + expect(response).to have_http_status(:success) + end end context 'with hub.mode=unsubscribe' do - pending + it 'unsubscribes the account' do + service = double(call: ['', 202]) + allow(Pubsubhubbub::UnsubscribeService).to receive(:new).and_return(service) + account = Fabricate(:account) + account_topic_url = "https://#{Rails.configuration.x.local_domain}/users/#{account.username}.atom" + post :update, params: { + 'hub.mode' => 'unsubscribe', + 'hub.topic' => account_topic_url, + 'hub.callback' => 'https://callback.host/api', + } + + expect(service).to have_received(:call).with( + account, + 'https://callback.host/api', + ) + expect(response).to have_http_status(:success) + end + end + + context 'with unknown mode' do + it 'returns an unknown mode error' do + post :update, params: { 'hub.mode' => 'fake' } + + expect(response).to have_http_status(422) + expect(response.body).to match(/Unknown mode/) + end end end end