2019-04-30 08:27:42 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2022-07-28 10:27:38 +08:00
|
|
|
RSpec.describe Admin::WebHooksController do
|
2022-11-03 11:42:44 +08:00
|
|
|
fab!(:web_hook) { Fabricate(:web_hook) }
|
|
|
|
fab!(:admin) { Fabricate(:admin) }
|
|
|
|
fab!(:moderator) { Fabricate(:moderator) }
|
|
|
|
fab!(:user) { Fabricate(:user) }
|
2017-04-27 05:23:37 +08:00
|
|
|
|
2022-11-03 11:42:44 +08:00
|
|
|
describe "#create" do
|
|
|
|
context "when logged in as admin" do
|
|
|
|
before { sign_in(admin) }
|
2017-04-27 05:23:37 +08:00
|
|
|
|
|
|
|
it "creates a webhook" do
|
2018-06-11 12:47:29 +08:00
|
|
|
post "/admin/api/web_hooks.json",
|
|
|
|
params: {
|
2017-08-31 12:06:56 +08:00
|
|
|
web_hook: {
|
|
|
|
payload_url: "https://meta.discourse.org/",
|
|
|
|
content_type: 1,
|
|
|
|
secret: "a_secret_for_webhooks",
|
|
|
|
wildcard_web_hook: false,
|
|
|
|
active: true,
|
|
|
|
verify_certificate: true,
|
|
|
|
web_hook_event_type_ids: [1],
|
|
|
|
group_ids: [],
|
|
|
|
category_ids: [],
|
|
|
|
},
|
2018-06-11 12:47:29 +08:00
|
|
|
}
|
2017-08-31 12:06:56 +08:00
|
|
|
|
2018-06-07 16:11:09 +08:00
|
|
|
expect(response.status).to eq(200)
|
2017-04-27 05:23:37 +08:00
|
|
|
|
2020-05-07 23:04:12 +08:00
|
|
|
json = response.parsed_body
|
2018-06-11 12:47:29 +08:00
|
|
|
expect(json["web_hook"]["payload_url"]).to eq("https://meta.discourse.org/")
|
2019-03-21 23:13:09 +08:00
|
|
|
expect(
|
|
|
|
UserHistory.where(
|
|
|
|
acting_user_id: admin.id,
|
|
|
|
action: UserHistory.actions[:web_hook_create],
|
|
|
|
).count,
|
|
|
|
).to eq(1)
|
2017-04-27 05:23:37 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it "returns error when field is not filled correctly" do
|
2018-06-11 12:47:29 +08:00
|
|
|
post "/admin/api/web_hooks.json",
|
|
|
|
params: {
|
2017-08-31 12:06:56 +08:00
|
|
|
web_hook: {
|
|
|
|
content_type: 1,
|
|
|
|
secret: "a_secret_for_webhooks",
|
|
|
|
wildcard_web_hook: false,
|
|
|
|
active: true,
|
|
|
|
verify_certificate: true,
|
|
|
|
web_hook_event_type_ids: [1],
|
|
|
|
group_ids: [],
|
|
|
|
category_ids: [],
|
|
|
|
},
|
2018-06-11 12:47:29 +08:00
|
|
|
}
|
2017-08-31 12:06:56 +08:00
|
|
|
|
2018-06-11 12:47:29 +08:00
|
|
|
expect(response.status).to eq(422)
|
2020-05-07 23:04:12 +08:00
|
|
|
response_body = response.parsed_body
|
2017-04-27 05:23:37 +08:00
|
|
|
|
|
|
|
expect(response_body["errors"]).to be_present
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-11-03 11:42:44 +08:00
|
|
|
shared_examples "webhook creation not allowed" do
|
|
|
|
it "prevents creation with a 404 response" do
|
|
|
|
post "/admin/api/web_hooks.json",
|
|
|
|
params: {
|
|
|
|
web_hook: {
|
|
|
|
payload_url: "https://meta.discourse.org/",
|
|
|
|
content_type: 1,
|
|
|
|
secret: "a_secret_for_webhooks",
|
|
|
|
wildcard_web_hook: false,
|
|
|
|
active: true,
|
|
|
|
verify_certificate: true,
|
|
|
|
web_hook_event_type_ids: [1],
|
|
|
|
group_ids: [],
|
|
|
|
category_ids: [],
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(response.status).to eq(404)
|
|
|
|
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
|
|
|
|
expect(response.parsed_body["web_hook"]).to be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when logged in as a moderator" do
|
|
|
|
before { sign_in(moderator) }
|
|
|
|
|
|
|
|
include_examples "webhook creation not allowed"
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when logged in as a non-staff user" do
|
|
|
|
before { sign_in(user) }
|
|
|
|
|
|
|
|
include_examples "webhook creation not allowed"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#update" do
|
|
|
|
context "when logged in as admin" do
|
|
|
|
before { sign_in(admin) }
|
|
|
|
|
2019-03-21 23:13:09 +08:00
|
|
|
it "logs webhook update" do
|
|
|
|
put "/admin/api/web_hooks/#{web_hook.id}.json",
|
|
|
|
params: {
|
|
|
|
web_hook: {
|
|
|
|
active: false,
|
|
|
|
payload_url: "https://test.com",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(
|
|
|
|
UserHistory.where(
|
|
|
|
acting_user_id: admin.id,
|
|
|
|
action: UserHistory.actions[:web_hook_update],
|
2021-04-21 17:36:32 +08:00
|
|
|
new_value: "active: false, payload_url: https://test.com",
|
|
|
|
).exists?,
|
|
|
|
).to eq(true)
|
2019-03-21 23:13:09 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-11-03 11:42:44 +08:00
|
|
|
shared_examples "webhook update not allowed" do
|
|
|
|
it "prevents updates with a 404 response" do
|
|
|
|
current_payload_url = web_hook.payload_url
|
|
|
|
put "/admin/api/web_hooks/#{web_hook.id}.json",
|
|
|
|
params: {
|
|
|
|
web_hook: {
|
|
|
|
active: false,
|
|
|
|
payload_url: "https://test.com",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
web_hook.reload
|
|
|
|
expect(response.status).to eq(404)
|
|
|
|
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
|
|
|
|
expect(web_hook.payload_url).to eq(current_payload_url)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when logged in as a moderator" do
|
|
|
|
before { sign_in(moderator) }
|
|
|
|
|
|
|
|
include_examples "webhook update not allowed"
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when logged in as a non-staff user" do
|
|
|
|
before { sign_in(user) }
|
|
|
|
|
|
|
|
include_examples "webhook update not allowed"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#destroy" do
|
|
|
|
context "when logged in as admin" do
|
|
|
|
before { sign_in(admin) }
|
|
|
|
|
2019-03-21 23:13:09 +08:00
|
|
|
it "logs webhook destroy" do
|
|
|
|
delete "/admin/api/web_hooks/#{web_hook.id}.json",
|
|
|
|
params: {
|
|
|
|
web_hook: {
|
|
|
|
active: false,
|
|
|
|
payload_url: "https://test.com",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(
|
|
|
|
UserHistory.where(
|
|
|
|
acting_user_id: admin.id,
|
|
|
|
action: UserHistory.actions[:web_hook_destroy],
|
|
|
|
).exists?,
|
|
|
|
).to eq(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-11-03 11:42:44 +08:00
|
|
|
shared_examples "webhook deletion not allowed" do
|
|
|
|
it "prevents deletion with a 404 response" do
|
|
|
|
delete "/admin/api/web_hooks/#{web_hook.id}.json"
|
|
|
|
|
|
|
|
expect(response.status).to eq(404)
|
|
|
|
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
|
|
|
|
expect(web_hook.reload).to be_present
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when logged in as a moderator" do
|
|
|
|
before { sign_in(moderator) }
|
|
|
|
|
|
|
|
include_examples "webhook deletion not allowed"
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when logged in as a non-staff user" do
|
|
|
|
before { sign_in(user) }
|
|
|
|
|
|
|
|
include_examples "webhook deletion not allowed"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#ping" do
|
|
|
|
context "when logged in as admin" do
|
|
|
|
before { sign_in(admin) }
|
|
|
|
|
2017-04-27 05:23:37 +08:00
|
|
|
it "enqueues the ping event" do
|
2018-06-11 12:47:29 +08:00
|
|
|
expect do post "/admin/api/web_hooks/#{web_hook.id}/ping.json" end.to change {
|
|
|
|
Jobs::EmitWebHookEvent.jobs.size
|
|
|
|
}.by(1)
|
2017-04-27 05:23:37 +08:00
|
|
|
|
2018-06-07 16:11:09 +08:00
|
|
|
expect(response.status).to eq(200)
|
2018-06-11 12:47:29 +08:00
|
|
|
job_args = Jobs::EmitWebHookEvent.jobs.first["args"].first
|
|
|
|
expect(job_args["web_hook_id"]).to eq(web_hook.id)
|
|
|
|
expect(job_args["event_type"]).to eq("ping")
|
2017-04-27 05:23:37 +08:00
|
|
|
end
|
|
|
|
end
|
2022-11-02 00:33:17 +08:00
|
|
|
|
2022-11-03 11:42:44 +08:00
|
|
|
shared_examples "webhook ping not allowed" do
|
|
|
|
it "fails to enqueue a ping with 404 response" do
|
|
|
|
expect do post "/admin/api/web_hooks/#{web_hook.id}/ping.json" end.not_to change {
|
|
|
|
Jobs::EmitWebHookEvent.jobs.size
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(response.status).to eq(404)
|
|
|
|
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
|
2022-11-02 00:33:17 +08:00
|
|
|
end
|
2022-11-03 11:42:44 +08:00
|
|
|
end
|
2022-11-02 00:33:17 +08:00
|
|
|
|
2022-11-03 11:42:44 +08:00
|
|
|
context "when logged in as a moderator" do
|
|
|
|
before { sign_in(moderator) }
|
2022-11-02 00:33:17 +08:00
|
|
|
|
2022-11-03 11:42:44 +08:00
|
|
|
include_examples "webhook ping not allowed"
|
|
|
|
end
|
2022-11-02 00:33:17 +08:00
|
|
|
|
2022-11-03 11:42:44 +08:00
|
|
|
context "when logged in as a non-staff user" do
|
|
|
|
before { sign_in(user) }
|
2022-11-02 00:33:17 +08:00
|
|
|
|
2022-11-03 11:42:44 +08:00
|
|
|
include_examples "webhook ping not allowed"
|
|
|
|
end
|
|
|
|
end
|
2022-11-02 00:33:17 +08:00
|
|
|
|
2022-11-03 11:42:44 +08:00
|
|
|
describe "#redeliver_event" do
|
|
|
|
let!(:web_hook_event) do
|
|
|
|
WebHookEvent.create!(web_hook: web_hook, payload: "abc", headers: JSON.dump(aa: "1", bb: "2"))
|
|
|
|
end
|
|
|
|
|
|
|
|
before { sign_in(admin) }
|
|
|
|
|
|
|
|
it "emits the web hook and updates the response headers and body" do
|
|
|
|
stub_request(:post, web_hook.payload_url).with(
|
|
|
|
body: "abc",
|
|
|
|
headers: {
|
|
|
|
"aa" => 1,
|
|
|
|
"bb" => 2,
|
|
|
|
},
|
|
|
|
).to_return(
|
|
|
|
status: 402,
|
|
|
|
body: "efg",
|
|
|
|
headers: {
|
|
|
|
"Content-Type" => "application/json",
|
|
|
|
"yoo" => "man",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
post "/admin/api/web_hooks/#{web_hook.id}/events/#{web_hook_event.id}/redeliver.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
parsed_event = response.parsed_body["web_hook_event"]
|
|
|
|
expect(parsed_event["id"]).to eq(web_hook_event.id)
|
|
|
|
expect(parsed_event["status"]).to eq(402)
|
|
|
|
|
|
|
|
expect(JSON.parse(parsed_event["headers"])).to eq({ "aa" => "1", "bb" => "2" })
|
|
|
|
expect(parsed_event["payload"]).to eq("abc")
|
|
|
|
|
|
|
|
expect(JSON.parse(parsed_event["response_headers"])).to eq(
|
|
|
|
{ "content-type" => "application/json", "yoo" => "man" },
|
|
|
|
)
|
|
|
|
expect(parsed_event["response_body"]).to eq("efg")
|
|
|
|
end
|
2022-11-02 00:33:17 +08:00
|
|
|
|
2022-11-03 11:42:44 +08:00
|
|
|
it "doesn't emit the web hook if the payload URL resolves to an internal IP" do
|
|
|
|
FinalDestination::TestHelper.stub_to_fail do
|
|
|
|
post "/admin/api/web_hooks/#{web_hook.id}/events/#{web_hook_event.id}/redeliver.json"
|
2022-11-02 00:33:17 +08:00
|
|
|
end
|
2022-11-03 11:42:44 +08:00
|
|
|
expect(response.status).to eq(200)
|
2022-11-02 00:33:17 +08:00
|
|
|
|
2022-11-03 11:42:44 +08:00
|
|
|
parsed_event = response.parsed_body["web_hook_event"]
|
|
|
|
expect(parsed_event["id"]).to eq(web_hook_event.id)
|
|
|
|
expect(parsed_event["response_headers"]).to eq(
|
|
|
|
{ error: I18n.t("webhooks.payload_url.blocked_or_internal") }.to_json,
|
|
|
|
)
|
|
|
|
expect(parsed_event["status"]).to eq(-1)
|
|
|
|
expect(parsed_event["response_body"]).to eq(nil)
|
|
|
|
end
|
2022-11-02 00:33:17 +08:00
|
|
|
|
2022-11-03 11:42:44 +08:00
|
|
|
it "doesn't emit the web hook if the payload URL resolves to a blocked IP" do
|
|
|
|
FinalDestination::TestHelper.stub_to_fail do
|
|
|
|
post "/admin/api/web_hooks/#{web_hook.id}/events/#{web_hook_event.id}/redeliver.json"
|
2022-11-02 00:33:17 +08:00
|
|
|
end
|
2022-11-03 11:42:44 +08:00
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
parsed_event = response.parsed_body["web_hook_event"]
|
|
|
|
expect(parsed_event["id"]).to eq(web_hook_event.id)
|
|
|
|
expect(parsed_event["response_headers"]).to eq(
|
|
|
|
{ error: I18n.t("webhooks.payload_url.blocked_or_internal") }.to_json,
|
|
|
|
)
|
|
|
|
expect(parsed_event["status"]).to eq(-1)
|
|
|
|
expect(parsed_event["response_body"]).to eq(nil)
|
2022-11-02 00:33:17 +08:00
|
|
|
end
|
2017-04-27 05:23:37 +08:00
|
|
|
end
|
|
|
|
end
|