Skip to content

Commit

Permalink
Test the new plug
Browse files Browse the repository at this point in the history
  • Loading branch information
zoldar committed Jul 2, 2024
1 parent 461a08e commit fca9c9d
Showing 1 changed file with 235 additions and 0 deletions.
235 changes: 235 additions & 0 deletions test/plausible_web/plugs/authorize_public_api_plug_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
defmodule PlausibleWeb.AuthorizePublicApiPlugTest do
use PlausibleWeb.ConnCase, async: false

alias PlausibleWeb.AuthorizePublicApiPlug

setup %{conn: conn} do
conn =
conn
|> put_private(PlausibleWeb.FirstLaunchPlug, :skip)
|> bypass_through(PlausibleWeb.Router)

{:ok, conn: conn}
end

test "halts with error when bearer token is missing", %{conn: conn} do
conn =
conn
|> get("/")
|> assign(:api_scope, "stats:read:*")
|> AuthorizePublicApiPlug.call(nil)

assert conn.halted
assert json_response(conn, 401)["error"] =~ "Missing API key."
end

test "halts with error when bearer token is invalid against read-only Stats API", %{conn: conn} do
conn =
conn
|> put_req_header("authorization", "Bearer invalid")
|> get("/")
|> assign(:api_scope, "stats:read:*")
|> AuthorizePublicApiPlug.call(nil)

assert conn.halted
assert json_response(conn, 401)["error"] =~ "Invalid API key or site ID."
end

test "halts with error when bearer token is invalid", %{conn: conn} do
conn =
conn
|> put_req_header("authorization", "Bearer invalid")
|> get("/")
|> assign(:api_scope, "sites:provision:*")
|> AuthorizePublicApiPlug.call(nil)

assert conn.halted
assert json_response(conn, 401)["error"] =~ "Invalid API key."
end

test "halts with error on missing site ID when request made to Stats API", %{conn: conn} do
api_key = insert(:api_key, user: build(:user))

conn =
conn
|> put_req_header("authorization", "Bearer #{api_key.key}")
|> get("/")
|> assign(:api_scope, "stats:read:*")
|> AuthorizePublicApiPlug.call(nil)

assert conn.halted
assert json_response(conn, 400)["error"] =~ "Missing site ID."
end

@tag :ee_only
test "halts with error when upgrade is required", %{conn: conn} do
user = insert(:user, trial_expiry_date: nil)
site = insert(:site, members: [user])
api_key = insert(:api_key, user: user)

conn =
conn
|> put_req_header("authorization", "Bearer #{api_key.key}")
|> get("/", %{"site_id" => site.domain})
|> assign(:api_scope, "stats:read:*")
|> AuthorizePublicApiPlug.call(nil)

assert conn.halted

assert json_response(conn, 402)["error"] =~
"The account that owns this API key does not have access"
end

test "halts with error when site is locked", %{conn: conn} do
user = insert(:user)
site = insert(:site, members: [user], locked: true)
api_key = insert(:api_key, user: user)

conn =
conn
|> put_req_header("authorization", "Bearer #{api_key.key}")
|> get("/", %{"site_id" => site.domain})
|> assign(:api_scope, "stats:read:*")
|> AuthorizePublicApiPlug.call(nil)

assert conn.halted
assert json_response(conn, 402)["error"] =~ "This Plausible site is locked"
end

test "halts with error when site ID is invalid", %{conn: conn} do
user = insert(:user)
_site = insert(:site, members: [user])
api_key = insert(:api_key, user: user)

conn =
conn
|> put_req_header("authorization", "Bearer #{api_key.key}")
|> get("/", %{"site_id" => "invalid.domain"})
|> assign(:api_scope, "stats:read:*")
|> AuthorizePublicApiPlug.call(nil)

assert conn.halted
assert json_response(conn, 401)["error"] =~ "Invalid API key or site ID."
end

test "halts with error when API key owner does not have access to the requested site", %{
conn: conn
} do
user = insert(:user)
site = insert(:site)
api_key = insert(:api_key, user: user)

conn =
conn
|> put_req_header("authorization", "Bearer #{api_key.key}")
|> get("/", %{"site_id" => site.domain})
|> assign(:api_scope, "stats:read:*")
|> AuthorizePublicApiPlug.call(nil)

assert conn.halted
assert json_response(conn, 401)["error"] =~ "Invalid API key or site ID."
end

test "halts with error when API lacks required scope", %{conn: conn} do
user = insert(:user)
api_key = insert(:api_key, user: user)

conn =
conn
|> put_req_header("authorization", "Bearer #{api_key.key}")
|> get("/")
|> assign(:api_scope, "sites:provision:*")
|> AuthorizePublicApiPlug.call(nil)

assert conn.halted
assert json_response(conn, 401)["error"] =~ "Invalid API key."
end

test "halts with error when API rate limit hit", %{conn: conn} do
user = insert(:user)
api_key = insert(:api_key, user: user, hourly_request_limit: 1)

conn =
conn
|> put_req_header("authorization", "Bearer #{api_key.key}")
|> get("/")
|> assign(:api_scope, "sites:read:*")

first_resp = AuthorizePublicApiPlug.call(conn, nil)
second_resp = AuthorizePublicApiPlug.call(conn, nil)

refute first_resp.halted
assert second_resp.halted
assert json_response(second_resp, 429)["error"] =~ "Too many API requests."
end

test "passes and sets current user when valid API key with required scope provided", %{
conn: conn
} do
user = insert(:user)
api_key = insert(:api_key, user: user, scopes: ["sites:provision:*"])

conn =
conn
|> put_req_header("authorization", "Bearer #{api_key.key}")
|> get("/")
|> assign(:api_scope, "sites:provision:*")
|> AuthorizePublicApiPlug.call(nil)

refute conn.halted
assert conn.assigns.current_user.id == user.id
end

test "passes and sets current user and site when valid API key and site ID provided", %{
conn: conn
} do
user = insert(:user)
site = insert(:site, members: [user])
api_key = insert(:api_key, user: user)

conn =
conn
|> put_req_header("authorization", "Bearer #{api_key.key}")
|> get("/", %{"site_id" => site.domain})
|> assign(:api_scope, "stats:read:*")
|> AuthorizePublicApiPlug.call(nil)

refute conn.halted
assert conn.assigns.current_user.id == user.id
assert conn.assigns.site.id == site.id
end

@tag :ee_only
test "passes for super admin user even if not a member of the requested site", %{conn: conn} do
user = insert(:user)
patch_env(:super_admin_user_ids, [user.id])
site = insert(:site, locked: true)
api_key = insert(:api_key, user: user)

conn =
conn
|> put_req_header("authorization", "Bearer #{api_key.key}")
|> get("/", %{"site_id" => site.domain})
|> assign(:api_scope, "stats:read:*")
|> AuthorizePublicApiPlug.call(nil)

refute conn.halted
assert conn.assigns.current_user.id == user.id
assert conn.assigns.site.id == site.id
end

test "passes for subscope match", %{conn: conn} do
user = insert(:user)
api_key = insert(:api_key, user: user, scopes: ["funnels:*"])

conn =
conn
|> put_req_header("authorization", "Bearer #{api_key.key}")
|> get("/")
|> assign(:api_scope, "funnels:read:*")
|> AuthorizePublicApiPlug.call(nil)

refute conn.halted
assert conn.assigns.current_user.id == user.id
end
end

0 comments on commit fca9c9d

Please sign in to comment.