From a596d16cfbda9bab16580d0fdf7a2a9885e5bb38 Mon Sep 17 00:00:00 2001 From: Tom Konidas Date: Sat, 2 Sep 2023 15:06:36 -0400 Subject: [PATCH] Lock down API mutation endpoints --- lib/plexus_web/api_auth_plug.ex | 48 +++++++++++++++++++ lib/plexus_web/router.ex | 13 ++++- .../api/v1/app_controller_test.exs | 4 +- .../api/v1/rating_controller_test.exs | 4 +- test/support/conn_case.ex | 16 +++++++ 5 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 lib/plexus_web/api_auth_plug.ex diff --git a/lib/plexus_web/api_auth_plug.ex b/lib/plexus_web/api_auth_plug.ex new file mode 100644 index 00000000..dcc4f222 --- /dev/null +++ b/lib/plexus_web/api_auth_plug.ex @@ -0,0 +1,48 @@ +defmodule PlexusWeb.APIAuthPlug do + @behaviour Plug + + import Plug.Conn + + @endpoint PlexusWeb.Endpoint + @salt "device auth" + + @impl Plug + def init(opts) do + opts + end + + @impl Plug + def call(conn, _opts) do + with {:ok, token} <- fetch_authorization_token(conn) do + case Phoenix.Token.verify(@endpoint, @salt, token) do + {:ok, %{device_id: _device_id, email: _email}} -> + conn + + {:error, :expired} -> + conn + |> send_resp(401, "Token is expired") + |> halt() + + {:error, :invalid} -> + conn + |> send_resp(401, "Invalid token") + |> halt() + + {:error, :missing} -> + conn + |> send_resp(401, "Token is missing") + |> halt() + end + end + end + + defp fetch_authorization_token(conn) do + case get_req_header(conn, "authorization") do + ["Bearer " <> token] -> + {:ok, token} + + _ -> + send_resp(conn, 401, "Authorization header is missing") + end + end +end diff --git a/lib/plexus_web/router.ex b/lib/plexus_web/router.ex index fb83e6c4..71b44fb4 100644 --- a/lib/plexus_web/router.ex +++ b/lib/plexus_web/router.ex @@ -14,6 +14,10 @@ defmodule PlexusWeb.Router do plug :accepts, ["json"] end + pipeline :authenticated_device do + plug PlexusWeb.APIAuthPlug + end + scope "/", PlexusWeb do pipe_through :browser @@ -36,13 +40,18 @@ defmodule PlexusWeb.Router do scope "/api/v1", PlexusWeb.API.V1 do pipe_through :api + scope "/" do + pipe_through :authenticated_device + + post "/apps", AppController, :create + post "/apps/:package/ratings", RatingController, :create + end + get "/apps", AppController, :index get "/apps/:package", AppController, :show - post "/apps", AppController, :create get "/apps/:package/ratings", RatingController, :index get "/apps/:package/ratings/:id", RatingController, :show - post "/apps/:package/ratings", RatingController, :create post "/devices/register", DeviceController, :register post "/devices/verify", DeviceController, :verify diff --git a/test/plexus_web/controllers/api/v1/app_controller_test.exs b/test/plexus_web/controllers/api/v1/app_controller_test.exs index 0501252e..5e835667 100644 --- a/test/plexus_web/controllers/api/v1/app_controller_test.exs +++ b/test/plexus_web/controllers/api/v1/app_controller_test.exs @@ -11,9 +11,7 @@ defmodule PlexusWeb.API.V1.AppControllerTest do @invalid_attrs %{name: nil, package: nil} - setup %{conn: conn} do - {:ok, conn: put_req_header(conn, "accept", "application/json")} - end + setup [:setup_json, :setup_authenticated_device] describe "index" do test "lists all apps", %{conn: conn} do diff --git a/test/plexus_web/controllers/api/v1/rating_controller_test.exs b/test/plexus_web/controllers/api/v1/rating_controller_test.exs index 39f9bf3e..fdd369da 100644 --- a/test/plexus_web/controllers/api/v1/rating_controller_test.exs +++ b/test/plexus_web/controllers/api/v1/rating_controller_test.exs @@ -27,9 +27,7 @@ defmodule PlexusWeb.API.V1.RatingControllerTest do score: nil } - setup %{conn: conn} do - {:ok, conn: put_req_header(conn, "accept", "application/json")} - end + setup [:setup_json, :setup_authenticated_device] describe "index" do test "lists all ratings", %{conn: conn} do diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index 8ced6eb0..a82bc0c1 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -17,6 +17,8 @@ defmodule PlexusWeb.ConnCase do use ExUnit.CaseTemplate + @salt "device auth" + using do quote do # The default endpoint for testing @@ -35,4 +37,18 @@ defmodule PlexusWeb.ConnCase do Plexus.DataCase.setup_sandbox(tags) {:ok, conn: Phoenix.ConnTest.build_conn()} end + + def setup_json(context) do + {:ok, conn: Plug.Conn.put_req_header(context.conn, "accept", "application/json")} + end + + def setup_authenticated_device(context) do + token = + Phoenix.Token.sign(PlexusWeb.Endpoint, @salt, %{ + device_id: "device_id", + email: "user@techlore.tech" + }) + + {:ok, conn: Plug.Conn.put_req_header(context.conn, "authorization", "Bearer " <> token)} + end end