analytics/lib/plausible_web/controllers/avatar_controller.ex
Uku Taht 97b24c0492
Nolt sso (along with a better nav dropdown) (#3395)
* Add SSO link with signed JWT token

* Falls back to Nolt URL without SSO if token cannot be generated

* Add profile image (gravatar) to Nolt SSO link

* Improve navbar dropdown

* Add 'contact support' link to nav dropdown

* Add CSS rule to prevent horizontal jumps

* Dark mode styling

* Close dropdown when link is clicked

* Clarify links in dropdown

* Clarify CSS comment

* Use Alpine.data() over window

* Rename suggestions_dropdown -> combo-box

* Mix format

* Make logout link look good on dark mode

* Use proxy for gravatar

* Do not use Gravatar proxy in self-hosted

* Changelog

* Add Github Repo link to nav dropdown

* Make dialyzer happy

* Add proxy for Gravatar

* Update assets/css/app.css

Co-authored-by: hq1 <hq@mtod.org>

* Update lib/plausible_web/controllers/avatar_controller.ex

Co-authored-by: hq1 <hq@mtod.org>

* Fix alpine <> Liveview integration

---------

Co-authored-by: hq1 <hq@mtod.org>
2023-10-17 12:01:27 +03:00

39 lines
1.5 KiB
Elixir

defmodule PlausibleWeb.AvatarController do
@moduledoc """
This module proxies requests to BASE_URL/avatar/:hash to www.gravatar.com/avatar/:hash.
The purpose is to make use of Gravatar's convenient avatar service without exposing information
that could be used for tracking the Plausible user. Compared to requesting the Gravatar directly
from the browser, this proxy module protects the Plausible user from disclosing to Gravatar:
1. The client IP address
2. User-Agent
3. Referer header which can be used to track which site the user is visiting (i.e. plausible.io or self-hosted URL)
The downside is the added latency from the request having to go through the Plausible server, rather than contacting the
local CDN server operated by Gravatar's service.
"""
use PlausibleWeb, :controller
alias Plausible.HTTPClient
@gravatar_base_url "https://www.gravatar.com"
def avatar(conn, params) do
url = Path.join(@gravatar_base_url, ["avatar/", params["hash"]]) <> "?s=150&d=identicon"
case HTTPClient.impl().get(url) do
{:ok, %Finch.Response{status: 200, body: body, headers: headers}} ->
conn
|> forward_headers(headers)
|> send_resp(200, body)
{:error, _} ->
send_resp(conn, 400, "")
end
end
@forwarded_headers ["content-type", "cache-control", "expires"]
defp forward_headers(conn, headers) do
headers_to_forward = Enum.filter(headers, fn {k, _} -> k in @forwarded_headers end)
%Plug.Conn{conn | resp_headers: headers_to_forward}
end
end