2021-04-09 10:30:51 +03:00
defmodule PlausibleWeb.AuthorizeStatsApiPlug do
2021-02-05 12:23:30 +03:00
import Plug.Conn
use Plausible.Repo
2023-10-11 23:24:16 +03:00
alias Plausible.Auth
2022-10-04 15:34:45 +03:00
alias Plausible.Sites
2023-12-06 17:07:37 +03:00
alias Plausible.RateLimit
2021-04-15 11:38:44 +03:00
alias PlausibleWeb.Api.Helpers , as : H
2021-02-05 12:23:30 +03:00
def init ( options ) do
options
end
def call ( conn , _opts ) do
2021-05-25 11:58:49 +03:00
with { :ok , token } <- get_bearer_token ( conn ) ,
2023-10-11 23:24:16 +03:00
{ :ok , api_key } <- Auth . find_api_key ( token ) ,
2021-05-25 11:58:49 +03:00
:ok <- check_api_key_rate_limit ( api_key ) ,
2021-02-22 11:21:25 +03:00
{ :ok , site } <- verify_access ( api_key , conn . params [ " site_id " ] ) do
2022-10-18 18:11:30 +03:00
Plausible.OpenTelemetry . add_site_attributes ( site )
2024-02-14 11:32:36 +03:00
site = Plausible.Imported . load_import_data ( site )
2021-02-22 11:21:25 +03:00
assign ( conn , :site , site )
2021-02-05 12:23:30 +03:00
else
2021-02-22 11:21:25 +03:00
{ :error , :missing_api_key } ->
2021-04-15 11:38:44 +03:00
H . unauthorized (
2021-02-22 11:21:25 +03:00
conn ,
" Missing API key. Please use a valid Plausible API key as a Bearer Token. "
)
{ :error , :missing_site_id } ->
2021-04-15 11:38:44 +03:00
H . bad_request (
2021-02-22 11:21:25 +03:00
conn ,
" Missing site ID. Please provide the required site_id parameter with your request. "
)
2021-05-25 11:58:49 +03:00
{ :error , :rate_limit , limit } ->
H . too_many_requests (
conn ,
2023-04-18 17:38:32 +03:00
" Too many API requests. Your API key is limited to #{ limit } requests per hour. Please contact us to request more capacity. "
2021-05-25 11:58:49 +03:00
)
2021-02-22 11:21:25 +03:00
{ :error , :invalid_api_key } ->
2021-04-15 11:38:44 +03:00
H . unauthorized (
2021-02-22 11:21:25 +03:00
conn ,
" Invalid API key or site ID. Please make sure you're using a valid API key with access to the site you've requested. "
)
2022-10-04 15:34:45 +03:00
2023-10-11 23:24:16 +03:00
{ :error , :upgrade_required } ->
H . payment_required (
conn ,
2024-02-01 20:00:09 +03:00
" The account that owns this API key does not have access to Stats API. Please make sure you're using the API key of a subscriber account and that the subscription plan includes Stats API "
2023-10-11 23:24:16 +03:00
)
2022-10-04 15:34:45 +03:00
{ :error , :site_locked } ->
H . payment_required (
conn ,
" This Plausible site is locked due to missing active subscription. In order to access it, the site owner should subscribe to a suitable plan "
)
2021-02-22 11:21:25 +03:00
end
end
defp verify_access ( _api_key , nil ) , do : { :error , :missing_site_id }
defp verify_access ( api_key , site_id ) do
2023-04-04 11:55:12 +03:00
domain_based_search =
from s in Plausible.Site , where : s . domain == ^ site_id or s . domain_changed_from == ^ site_id
case Repo . one ( domain_based_search ) do
2022-10-04 15:34:45 +03:00
% Plausible.Site { } = site ->
is_member? = Sites . is_member? ( api_key . user_id , site )
is_super_admin? = Plausible.Auth . is_super_admin? ( api_key . user_id )
cond do
2023-10-11 23:24:16 +03:00
is_super_admin? ->
{ :ok , site }
Sites . locked? ( site ) ->
{ :error , :site_locked }
Plausible.Billing.Feature.StatsAPI . check_availability ( api_key . user ) !== :ok ->
{ :error , :upgrade_required }
is_member? ->
{ :ok , site }
true ->
{ :error , :invalid_api_key }
2022-10-04 15:34:45 +03:00
end
2021-02-22 11:21:25 +03:00
2022-10-04 15:34:45 +03:00
nil ->
{ :error , :invalid_api_key }
2021-02-05 12:23:30 +03:00
end
end
defp get_bearer_token ( conn ) do
authorization_header =
Plug.Conn . get_req_header ( conn , " authorization " )
|> List . first ( )
case authorization_header do
2021-02-22 11:21:25 +03:00
" Bearer " <> token -> { :ok , String . trim ( token ) }
_ -> { :error , :missing_api_key }
2021-02-05 12:23:30 +03:00
end
end
2021-05-25 11:58:49 +03:00
@one_hour 60 * 60 * 1000
defp check_api_key_rate_limit ( api_key ) do
2023-12-06 17:07:37 +03:00
case RateLimit . check_rate (
" api_request: #{ api_key . id } " ,
@one_hour ,
api_key . hourly_request_limit
) do
2021-05-25 11:58:49 +03:00
{ :allow , _ } -> :ok
{ :deny , _ } -> { :error , :rate_limit , api_key . hourly_request_limit }
end
end
2021-02-05 12:23:30 +03:00
end