defmodule ToweropsWeb.Plugs.DetectEUUser do @moduledoc """ Detects if a user is from an EU/EEA country that requires GDPR cookie consent. Uses local GeoIP database to determine the user's country from their IP address. The result is stored in conn.assigns.requires_cookie_consent """ import Plug.Conn alias ToweropsWeb.RemoteIp # EU/EEA country codes that require GDPR cookie consent # EU member states + EEA countries (Norway, Iceland, Liechtenstein) @eu_eea_countries ~w( AT BE BG HR CY CZ DK EE FI FR DE GR HU IE IT LV LT LU MT NL PL PT RO SK SI ES SE IS NO LI ) # British overseas territories and Crown dependencies also covered by GDPR @uk_territories ~w( GB GG JE IM GI ) @doc false def init(opts), do: opts @doc false def call(conn, _opts) do require Logger # Fetch cookies to check for existing consent conn = fetch_cookies(conn) requires_consent = detect_eu_user(conn) # Store in process dictionary so layouts can access it Process.put(:requires_cookie_consent, requires_consent) conn |> put_session(:requires_cookie_consent, requires_consent) |> assign(:requires_cookie_consent, requires_consent) end defp detect_eu_user(conn) do # If user has already accepted cookies, don't show banner if has_consent_cookie?(conn) do false else # In development (localhost), always show the banner for testing hostname = conn.host if hostname in ["localhost", "127.0.0.1"] do true else detect_country_from_headers(conn) end end end defp has_consent_cookie?(conn) do case conn.cookies["cookie_consent"] do "accepted" -> true _ -> false end end defp detect_country_from_headers(conn) do # Get remote IP from connection remote_ip = RemoteIp.from_conn(conn) || "0.0.0.0" case Towerops.GeoIP.lookup(remote_ip) do country_code when is_binary(country_code) -> eu_country?(country_code) nil -> # No country found - default to showing banner (conservative approach) true end end defp eu_country?(country_code) do country_code = String.upcase(country_code) country_code in @eu_eea_countries or country_code in @uk_territories end end