towerops/lib/towerops_web/plugs/detect_eu_user.ex
Graham McIntire 4421727e61 fix: resolve all Elixir 1.20 type system warnings across project
Removes unreachable catch-all clauses, drops unused `require Logger`
lines, adds pin operators to bitstring size patterns, reorders function
heads for correct dispatch, and replaces deprecated LoggerBackends calls.
2026-05-28 14:52:30 -05:00

80 lines
2.1 KiB
Elixir

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
# Fetch cookies to check for existing consent
conn = fetch_cookies(conn)
requires_consent = detect_eu_user(conn)
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