diff --git a/config/config.exs b/config/config.exs index 5f13716..874c363 100644 --- a/config/config.exs +++ b/config/config.exs @@ -20,8 +20,9 @@ config :pinchflat, tmpfile_directory: Path.join([System.tmp_dir!(), "pinchflat", "data"]), # Setting BASIC_AUTH_USERNAME and BASIC_AUTH_PASSWORD implies you want to use basic auth. # If either is unset, basic auth will not be used. - basic_auth_username: System.get_env("BASIC_AUTH_USERNAME"), - basic_auth_password: System.get_env("BASIC_AUTH_PASSWORD"), + basic_auth_username: "", + basic_auth_password: "", + expose_xml_feed: false, url_base: System.get_env("BASIC_AUTH_PASSWORD") || "http://localhost:4008", file_watcher_poll_interval: 1000 diff --git a/config/runtime.exs b/config/runtime.exs index f84239a..e3d0166 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -47,6 +47,10 @@ if config_env() == :prod do metadata_path = System.get_env("METADATA_PATH", Path.join([config_path, "metadata"])) extras_path = System.get_env("EXTRAS_PATH", Path.join([config_path, "extras"])) + # For running PF as a podcast host on self-hosted environments + expose_xml_feed = String.length(System.get_env("EXPOSE_XML_FEED", "")) > 0 + url_base = System.get_env("URL_BASE", "") + # We want to force _some_ level of useful logging in production acceptable_log_levels = ~w(debug info)a log_level = String.to_existing_atom(System.get_env("LOG_LEVEL", "info")) @@ -65,7 +69,8 @@ if config_env() == :prod do extras_directory: extras_path, tmpfile_directory: Path.join([System.tmp_dir!(), "pinchflat", "data"]), dns_cluster_query: System.get_env("DNS_CLUSTER_QUERY"), - url_base: System.get_env("URL_BASE") || "http://localhost:8945" + url_base: url_base, + expose_xml_feed: expose_xml_feed config :pinchflat, Pinchflat.Repo, database: db_path, diff --git a/lib/pinchflat_web/router.ex b/lib/pinchflat_web/router.ex index ac166ff..68d3091 100644 --- a/lib/pinchflat_web/router.ex +++ b/lib/pinchflat_web/router.ex @@ -17,6 +17,10 @@ defmodule PinchflatWeb.Router do plug :accepts, ["json"] end + pipeline :feeds do + plug :maybe_basic_auth + end + scope "/", PinchflatWeb do pipe_through :browser @@ -30,12 +34,11 @@ defmodule PinchflatWeb.Router do end end - # Routes in here are NOT protected by basic auth. This is necessary for + # Routes in here _may not be_ protected by basic auth. This is necessary for # media streaming to work for RSS podcast feeds. - # - # TODO: consider putting the basic auth here behind a config flag - # so people that want RSS feeds to work can enable it. scope "/", PinchflatWeb do + pipe_through :feeds + get "/sources/:uuid/feed", Podcasts.PodcastController, :rss_feed get "/sources/:uuid/feed_image", Podcasts.PodcastController, :feed_image @@ -59,6 +62,14 @@ defmodule PinchflatWeb.Router do end end + defp maybe_basic_auth(conn, opts) do + if Application.get_env(:pinchflat, :expose_xml_feed) do + conn + else + basic_auth(conn, opts) + end + end + defp basic_auth(conn, _opts) do username = Application.get_env(:pinchflat, :basic_auth_username) password = Application.get_env(:pinchflat, :basic_auth_password) diff --git a/test/pinchflat_web/routing_test.exs b/test/pinchflat_web/routing_test.exs index 37c047d..42d4e31 100644 --- a/test/pinchflat_web/routing_test.exs +++ b/test/pinchflat_web/routing_test.exs @@ -1,6 +1,8 @@ defmodule PinchflatWeb.RoutingTest do use PinchflatWeb.ConnCase + import Pinchflat.SourcesFixtures + describe "basic_auth plug" do setup do old_username = Application.get_env(:pinchflat, :basic_auth_username) @@ -54,4 +56,53 @@ defmodule PinchflatWeb.RoutingTest do assert conn.status == 200 end end + + describe "maybe_basic_auth plug" do + setup do + old_username = Application.get_env(:pinchflat, :basic_auth_username) + old_password = Application.get_env(:pinchflat, :basic_auth_password) + old_expore_xml_feed = Application.get_env(:pinchflat, :expose_xml_feed) + + source = source_fixture() + + on_exit(fn -> + Application.put_env(:pinchflat, :basic_auth_username, old_username) + Application.put_env(:pinchflat, :basic_auth_password, old_password) + Application.put_env(:pinchflat, :expose_xml_feed, old_expore_xml_feed) + end) + + {:ok, source: source} + end + + test "uses basic auth when expose_xml_feed is false", %{source: source} do + Application.put_env(:pinchflat, :basic_auth_username, "user") + Application.put_env(:pinchflat, :basic_auth_password, "pass") + Application.put_env(:pinchflat, :expose_xml_feed, false) + + conn = get(build_conn(), "/sources/#{source.uuid}/feed") + + assert conn.status == 401 + assert {"www-authenticate", "Basic realm=\"Pinchflat\""} in conn.resp_headers + end + + test "does not use basic auth when expose_xml_feed is true", %{source: source} do + Application.put_env(:pinchflat, :basic_auth_username, "user") + Application.put_env(:pinchflat, :basic_auth_password, "pass") + Application.put_env(:pinchflat, :expose_xml_feed, true) + + conn = get(build_conn(), "/sources/#{source.uuid}/feed") + + assert conn.status == 200 + end + + test "does not use basic auth when username/password aren't set", %{source: source} do + Application.put_env(:pinchflat, :basic_auth_username, nil) + Application.put_env(:pinchflat, :basic_auth_password, nil) + Application.put_env(:pinchflat, :expose_xml_feed, false) + + conn = get(build_conn(), "/sources/#{source.uuid}/feed") + + assert conn.status == 200 + end + end end