mirror of
https://github.com/plausible/analytics.git
synced 2024-12-22 09:01:40 +03:00
e71de6dc1f
* Invite existing user to a site * Add invitation flow for non-existing users * Accept and reject invitations * Use invitation flow for existing users * Locking mechanism for sites * Authorization for site settings * Show usage based on site ownership * Add ability to remove members from a site * Do not show settings link to viewer roles * Ability to remove invitations * Remove `Plausible.Sites.count_for/1` * Fix tests * Do not show the trial banner after the trial * Correct trial emails * Transfer ownership * Send invitation email to existing user * Add invitation email flows * Add plug for role-based authorization * Rename AuthorizeStatsPlug -> AuthorizeSiteAccess * Add email flow for ownership transfer * Fix URLs in emails * Fix small copy issues * Make 'People' its own section in site settings * Notify user via email if their access has been removed * Check site lock status when invitation is accepted * Check lock status when user subscribes * Make sure only admins and owners can create shared links * Changelog * Add LockSites to daily cron * Clean invitations after 48 hours * Add notices about expiry * Add invitation expired page * Add doc link
81 lines
2.3 KiB
Elixir
81 lines
2.3 KiB
Elixir
defmodule PlausibleWeb.AuthorizeStatsApiPlug do
|
|
import Plug.Conn
|
|
use Plausible.Repo
|
|
alias Plausible.Auth.ApiKey
|
|
alias PlausibleWeb.Api.Helpers, as: H
|
|
|
|
def init(options) do
|
|
options
|
|
end
|
|
|
|
def call(conn, _opts) do
|
|
with {:ok, token} <- get_bearer_token(conn),
|
|
{:ok, api_key} <- find_api_key(token),
|
|
:ok <- check_api_key_rate_limit(api_key),
|
|
{:ok, site} <- verify_access(api_key, conn.params["site_id"]) do
|
|
assign(conn, :site, site)
|
|
else
|
|
{:error, :missing_api_key} ->
|
|
H.unauthorized(
|
|
conn,
|
|
"Missing API key. Please use a valid Plausible API key as a Bearer Token."
|
|
)
|
|
|
|
{:error, :missing_site_id} ->
|
|
H.bad_request(
|
|
conn,
|
|
"Missing site ID. Please provide the required site_id parameter with your request."
|
|
)
|
|
|
|
{:error, :rate_limit, limit} ->
|
|
H.too_many_requests(
|
|
conn,
|
|
"Too many API requests. Your API key is limited to #{limit} requests per hour."
|
|
)
|
|
|
|
{:error, :invalid_api_key} ->
|
|
H.unauthorized(
|
|
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."
|
|
)
|
|
end
|
|
end
|
|
|
|
defp verify_access(_api_key, nil), do: {:error, :missing_site_id}
|
|
|
|
defp verify_access(api_key, site_id) do
|
|
site = Repo.get_by(Plausible.Site, domain: site_id)
|
|
is_member = site && Plausible.Sites.is_member?(api_key.user_id, site)
|
|
|
|
cond do
|
|
site && is_member -> {:ok, site}
|
|
true -> {:error, :invalid_api_key}
|
|
end
|
|
end
|
|
|
|
defp get_bearer_token(conn) do
|
|
authorization_header =
|
|
Plug.Conn.get_req_header(conn, "authorization")
|
|
|> List.first()
|
|
|
|
case authorization_header do
|
|
"Bearer " <> token -> {:ok, String.trim(token)}
|
|
_ -> {:error, :missing_api_key}
|
|
end
|
|
end
|
|
|
|
defp find_api_key(token) do
|
|
hashed_key = ApiKey.do_hash(token)
|
|
found_key = Repo.get_by(ApiKey, key_hash: hashed_key)
|
|
if found_key, do: {:ok, found_key}, else: {:error, :invalid_api_key}
|
|
end
|
|
|
|
@one_hour 60 * 60 * 1000
|
|
defp check_api_key_rate_limit(api_key) do
|
|
case Hammer.check_rate("api_request:#{api_key.id}", @one_hour, api_key.hourly_request_limit) do
|
|
{:allow, _} -> :ok
|
|
{:deny, _} -> {:error, :rate_limit, api_key.hourly_request_limit}
|
|
end
|
|
end
|
|
end
|