diff --git a/.gitignore b/.gitignore index c38e85477..11ba8cc3d 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,7 @@ plausible-report.xml # Geolocation databases /priv/geodb/*.mmdb +/priv/geodb/*.mmdb.gz # Auto-generated tracker files /priv/tracker/js/*.js diff --git a/config/.env.test b/config/.env.test index 4f5107ae5..cb773330e 100644 --- a/config/.env.test +++ b/config/.env.test @@ -11,3 +11,4 @@ SELFHOST=false SITE_LIMIT=3 HCAPTCHA_SITEKEY=test HCAPTCHA_SECRET=scottiger +IP_GEOLOCATION_DB=test/priv/GeoLite2-City-Test.mmdb diff --git a/config/runtime.exs b/config/runtime.exs index cfb89a941..53ed3ca92 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -142,11 +142,13 @@ geolite2_country_db = get_var_from_path_or_env( config_dir, "GEOLITE2_COUNTRY_DB", - Application.app_dir(:plausible, "/priv/geodb/dbip-country.mmdb") + Application.app_dir(:plausible, "/priv/geodb/dbip-country.mmdb.gz") ) ip_geolocation_db = get_var_from_path_or_env(config_dir, "IP_GEOLOCATION_DB", geolite2_country_db) geonames_source_file = get_var_from_path_or_env(config_dir, "GEONAMES_SOURCE_FILE") +maxmind_license_key = get_var_from_path_or_env(config_dir, "MAXMIND_LICENSE_KEY") +maxmind_edition = get_var_from_path_or_env(config_dir, "MAXMIND_EDITION", "GeoLite2-City") if System.get_env("DISABLE_AUTH") do require Logger @@ -433,17 +435,38 @@ config :kaffy, ] ] -if config_env() != :test do - config :geolix, - databases: [ - %{ - id: :geolocation, - adapter: Geolix.Adapter.MMDB2, - source: ip_geolocation_db, - result_as: :raw - } - ] -end +geo_opts = + cond do + maxmind_license_key -> + [ + license_key: maxmind_license_key, + edition: maxmind_edition, + async: true + ] + + ip_geolocation_db -> + [path: ip_geolocation_db] + + true -> + raise """ + Missing geolocation database configuration. + + Please set the IP_GEOLOCATION_DB environment value to the location of + your IP geolocation .mmdb file: + + IP_GEOLOCATION_DB=/etc/plausible/dbip-city.mmdb + + Or authenticate with MaxMind by + configuring MAXMIND_LICENSE_KEY and (optionally) MAXMIND_EDITION environment + variables: + + MAXMIND_LICENSE_KEY=LNpsJCCKPis6XvBP + MAXMIND_EDITION=GeoLite2-City # this is the default edition + + """ + end + +config :plausible, Plausible.Geo, geo_opts if geonames_source_file do config :location, :geonames_source_file, geonames_source_file diff --git a/config/test.exs b/config/test.exs index df2aba638..093cb8319 100644 --- a/config/test.exs +++ b/config/test.exs @@ -24,45 +24,6 @@ config :plausible, :google, config :bamboo, :refute_timeout, 10 -geolix_sample_lookup = %{ - city: %{geoname_id: 2_988_507, names: %{en: "Paris"}}, - continent: %{code: "EU", geoname_id: 6_255_148, names: %{en: "Europe"}}, - country: %{ - geoname_id: 3_017_382, - is_in_european_union: true, - iso_code: "FR", - names: %{en: "France"} - }, - ip_address: {2, 2, 2, 2}, - location: %{ - latitude: 48.8566, - longitude: 2.35222, - time_zone: "Europe/Paris", - weather_code: "FRXX0076" - }, - postal: %{code: "75000"}, - subdivisions: [ - %{geoname_id: 3_012_874, iso_code: "IDF", names: %{en: "Île-de-France"}}, - %{geoname_id: 2_968_815, iso_code: "75", names: %{en: "Paris"}} - ] -} - -config :geolix, - databases: [ - %{ - id: :geolocation, - adapter: Geolix.Adapter.Fake, - data: %{ - {1, 1, 1, 1} => %{country: %{iso_code: "US"}}, - {2, 2, 2, 2} => geolix_sample_lookup, - {1, 1, 1, 1, 1, 1, 1, 1} => %{country: %{iso_code: "US"}}, - {0, 0, 0, 0} => %{country: %{iso_code: "ZZ"}, city: %{geoname_id: 123_123}}, - {0, 0, 0, 1} => %{country: %{iso_code: "XX"}, subdivisions: [%{iso_code: "IDF"}]}, - {0, 0, 0, 2} => %{country: %{iso_code: "T1"}, subdivisions: [%{}, %{iso_code: "IDF"}]} - } - } - ] - config :plausible, session_timeout: 0, http_impl: Plausible.HTTPClient.Mock, diff --git a/lib/mix/tasks/download_country_database.ex b/lib/mix/tasks/download_country_database.ex index 5a2027af7..d81855d6e 100644 --- a/lib/mix/tasks/download_country_database.ex +++ b/lib/mix/tasks/download_country_database.ex @@ -34,7 +34,7 @@ defmodule Mix.Tasks.DownloadCountryDatabase do if res.status_code == 200 do File.mkdir("priv/geodb") - File.write!("priv/geodb/dbip-country.mmdb", res.body) + File.write!("priv/geodb/dbip-country.mmdb.gz", res.body) Logger.info("Downloaded and saved the database successfully") else Logger.error("Unable to download and save the database. Response: #{inspect(res)}") diff --git a/lib/plausible/application.ex b/lib/plausible/application.ex index 1a6c36d5d..4e2a9aed0 100644 --- a/lib/plausible/application.ex +++ b/lib/plausible/application.ex @@ -30,9 +30,14 @@ defmodule Plausible.Application do ] opts = [strategy: :one_for_one, name: Plausible.Supervisor] + setup_sentry() setup_opentelemetry() + + setup_geolocation() Location.load_all() + Plausible.Geo.await_loader() + Supervisor.start_link(children, opts) end @@ -119,4 +124,9 @@ defmodule Plausible.Application do OpentelemetryEcto.setup([:plausible, :clickhouse_repo]) OpentelemetryOban.setup() end + + defp setup_geolocation do + opts = Application.fetch_env!(:plausible, Plausible.Geo) + :ok = Plausible.Geo.load_db(opts) + end end diff --git a/lib/plausible/geo.ex b/lib/plausible/geo.ex new file mode 100644 index 000000000..4d4fff7f4 --- /dev/null +++ b/lib/plausible/geo.ex @@ -0,0 +1,189 @@ +defmodule Plausible.Geo do + @moduledoc """ + This module provides an API for fetching IP geolocation. + """ + + require Logger + + @db :geolocation + + @doc """ + Starts the geodatabase loading process. Two modes are supported: local file + and MaxMind license key. + + ## Options + + * `:path` - the path to the .mmdb database local file. When present, + `:license_key` and `:edition` are not required. + + * `:license_key` - the [license key](https://support.maxmind.com/hc/en-us/articles/4407111582235-Generate-a-License-Key) + from MaxMind to authenticate requests to MaxMind. + + * `:edition` - the name of the MaxMind database to be downloaded from MaxMind + servers. Defaults to `GeoLite2-City`. + + * `:async` - when used, configures the database loading to run + asynchronously. + + ## Examples + + Loading from a local file: + + iex> load_db(path: "/etc/plausible/dbip-city.mmdb") + :ok + + Downloading a MaxMind DB (this license key is no longer active): + + iex> load_db(license_key: "LNpsJCCKPis6XvBP", edition: "GeoLite2-City", async: true) + :ok + + """ + def load_db(opts) do + cond do + license_key = opts[:license_key] -> + edition = opts[:edition] || "GeoLite2-City" + :ok = :locus.start_loader(@db, {:maxmind, edition}, license_key: license_key) + + path = opts[:path] -> + :ok = :locus.start_loader(@db, path) + + true -> + raise "failed to load geolocation db: need :path or :license_key to be provided" + end + + unless opts[:async] do + {:ok, _version} = :locus.await_loader(@db) + end + + :ok + end + + @doc """ + Waits for the database to start after calling `load_db/1` with the async option. + """ + def await_loader, do: :locus.await_loader(@db) + + @doc """ + Returns geodatabase type. + + Used for deciding whether to show the DB-IP disclaimer or not. + + ## Examples + + In the case of a DB-IP database: + + iex> database_type() + "DBIP-City-Lite" + + In the case of a MaxMind database: + + iex> database_type() + "GeoLite2-City" + + """ + def database_type do + case :locus.get_info(@db, :metadata) do + {:ok, %{database_type: type}} -> type + _other -> nil + end + end + + @doc """ + Looks up geo info about an IP address. + + ## Examples + + iex> lookup("8.7.6.5") + %{ + "city" => %{ + "geoname_id" => 5349755, + "names" => %{ + "de" => "Fontana", + "en" => "Fontana", + "ja" => "フォンタナ", + "ru" => "Фонтана" + } + }, + "continent" => %{ + "code" => "NA", + "geoname_id" => 6255149, + "names" => %{ + "de" => "Nordamerika", + "en" => "North America", + "es" => "Norteamérica", + "fr" => "Amérique du Nord", + "ja" => "北アメリカ", + "pt-BR" => "América do Norte", + "ru" => "Северная Америка", + "zh-CN" => "北美洲" + } + }, + "country" => %{ + "geoname_id" => 6252001, + "iso_code" => "US", + "names" => %{ + "de" => "Vereinigte Staaten", + "en" => "United States", + "es" => "Estados Unidos", + "fr" => "États Unis", + "ja" => "アメリカ", + "pt-BR" => "EUA", + "ru" => "США", + "zh-CN" => "美国" + } + }, + "location" => %{ + "accuracy_radius" => 50, + "latitude" => 34.1211, + "longitude" => -117.4362, + "metro_code" => 803, + "time_zone" => "America/Los_Angeles" + }, + "postal" => %{"code" => "92336"}, + "registered_country" => %{ + "geoname_id" => 6252001, + "iso_code" => "US", + "names" => %{ + "de" => "Vereinigte Staaten", + "en" => "United States", + "es" => "Estados Unidos", + "fr" => "États Unis", + "ja" => "アメリカ", + "pt-BR" => "EUA", + "ru" => "США", + "zh-CN" => "美国" + } + }, + "subdivisions" => [ + %{ + "geoname_id" => 5332921, + "iso_code" => "CA", + "names" => %{ + "de" => "Kalifornien", + "en" => "California", + "es" => "California", + "fr" => "Californie", + "ja" => "カリフォルニア州", + "pt-BR" => "Califórnia", + "ru" => "Калифорния", + "zh-CN" => "加州" + } + } + ] + } + + """ + def lookup(ip_address) do + case :locus.lookup(@db, ip_address) do + {:ok, entry} -> + entry + + :not_found -> + nil + + {:error, reason} -> + Logger.error("failed to lookup ip address: " <> inspect(reason)) + nil + end + end +end diff --git a/lib/plausible/ingestion/event.ex b/lib/plausible/ingestion/event.ex index 84d79ed9c..528a070f1 100644 --- a/lib/plausible/ingestion/event.ex +++ b/lib/plausible/ingestion/event.ex @@ -168,7 +168,7 @@ defmodule Plausible.Ingestion.Event do end defp put_geolocation(%__MODULE__{} = event) do - result = Plausible.Ingestion.Geolocation.lookup(event.request.remote_ip) + result = Plausible.Ingestion.Geolocation.lookup(event.request.remote_ip) || %{} update_attrs(event, result) end diff --git a/lib/plausible/ingestion/geolocation.ex b/lib/plausible/ingestion/geolocation.ex index f30f30af1..43a564de6 100644 --- a/lib/plausible/ingestion/geolocation.ex +++ b/lib/plausible/ingestion/geolocation.ex @@ -1,33 +1,39 @@ defmodule Plausible.Ingestion.Geolocation do @moduledoc false - alias Plausible.Ingestion.CityOverrides - def lookup(remote_ip) do - result = Geolix.lookup(remote_ip, where: :geolocation) + def lookup(ip_address) do + case Plausible.Geo.lookup(ip_address) do + %{} = entry -> + country_code = + entry + |> get_in(["country", "iso_code"]) + |> ignore_unknown_country() - country_code = - get_in(result, [:country, :iso_code]) - |> ignore_unknown_country() + city_geoname_id = country_code && get_in(entry, ["city", "geoname_id"]) + city_geoname_id = Plausible.Ingestion.CityOverrides.get(city_geoname_id, city_geoname_id) - city_geoname_id = country_code && get_in(result, [:city, :geoname_id]) - city_geoname_id = CityOverrides.get(city_geoname_id, city_geoname_id) + %{ + country_code: country_code, + subdivision1_code: subdivision1_code(country_code, entry), + subdivision2_code: subdivision2_code(country_code, entry), + city_geoname_id: city_geoname_id + } - %{ - country_code: country_code, - subdivision1_code: subdivision1_code(country_code, result), - subdivision2_code: subdivision2_code(country_code, result), - city_geoname_id: city_geoname_id - } + nil -> + nil + end end - defp subdivision1_code(country_code, %{subdivisions: [%{iso_code: iso_code} | _rest]}) + defp subdivision1_code(country_code, %{"subdivisions" => [%{"iso_code" => iso_code} | _rest]}) when not is_nil(country_code) do country_code <> "-" <> iso_code end defp subdivision1_code(_, _), do: nil - defp subdivision2_code(country_code, %{subdivisions: [_first, %{iso_code: iso_code} | _rest]}) + defp subdivision2_code(country_code, %{ + "subdivisions" => [_first, %{"iso_code" => iso_code} | _rest] + }) when not is_nil(country_code) do country_code <> "-" <> iso_code end diff --git a/lib/plausible_web/controllers/api/external_controller.ex b/lib/plausible_web/controllers/api/external_controller.ex index 29e5dd906..21ccf0a69 100644 --- a/lib/plausible_web/controllers/api/external_controller.ex +++ b/lib/plausible_web/controllers/api/external_controller.ex @@ -86,14 +86,7 @@ defmodule PlausibleWeb.Api.ExternalController do |> Keyword.take([:version, :commit, :created, :tags]) |> Map.new() - geo_database = - case Geolix.metadata(where: :geolocation) do - %{database_type: type} -> - type - - _ -> - "(not configured)" - end + geo_database = Plausible.Geo.database_type() || "(not configured)" json(conn, %{ geo_database: geo_database, diff --git a/lib/plausible_web/controllers/stats_controller.ex b/lib/plausible_web/controllers/stats_controller.ex index 2e719c72a..bb35bdb8b 100644 --- a/lib/plausible_web/controllers/stats_controller.ex +++ b/lib/plausible_web/controllers/stats_controller.ex @@ -316,14 +316,13 @@ defmodule PlausibleWeb.StatsController do end defp is_dbip() do - if Application.get_env(:plausible, :is_selfhost) do - case Geolix.metadata(where: :geolocation) do - %{database_type: type} -> + is_or_nil = + if Application.get_env(:plausible, :is_selfhost) do + if type = Plausible.Geo.database_type() do String.starts_with?(type, "DBIP") - - _ -> - false + end end - end + + !!is_or_nil end end diff --git a/mix.exs b/mix.exs index 3c0b4e1d4..4fe851e16 100644 --- a/mix.exs +++ b/mix.exs @@ -80,9 +80,8 @@ defmodule Plausible.MixProject do {:floki, "~> 0.32.0", only: :test}, {:fun_with_flags, "~> 1.9.0"}, {:fun_with_flags_ui, "~> 0.8"}, + {:locus, "~> 2.3"}, {:gen_cycle, "~> 1.0"}, - {:geolix, "~> 2.0"}, - {:geolix_adapter_mmdb2, "~> 0.6.0"}, {:hackney, "~> 1.8"}, {:hammer, "~> 6.0"}, {:httpoison, "~> 1.4"}, diff --git a/mix.lock b/mix.lock index 9addbb7fb..18ebc549d 100644 --- a/mix.lock +++ b/mix.lock @@ -49,8 +49,6 @@ "fun_with_flags_ui": {:hex, :fun_with_flags_ui, "0.8.0", "70587e344ba2035516a639e7bd8cb2ce8d54ee6c5f5dfd3e4e6f6a776e14ac1d", [:mix], [{:cowboy, ">= 2.0.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:fun_with_flags, "~> 1.8", [hex: :fun_with_flags, repo: "hexpm", optional: false]}, {:plug, "~> 1.12", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 2.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "95e1e5afae77e259a4a43f930f3da97ff3c388a72d4622f7848cb7ee34de6338"}, "gen_cycle": {:hex, :gen_cycle, "1.0.3", "ee0117b9c65814f58827abfe84967d4cb5380db0fbac992358e5b669b3ebcbcb", [:rebar3], [], "hexpm", "fffdadf7fb73de75e3429eb8a6982768e7240803561942602350b689991749e4"}, "gen_smtp": {:hex, :gen_smtp, "1.2.0", "9cfc75c72a8821588b9b9fe947ae5ab2aed95a052b81237e0928633a13276fd3", [:rebar3], [{:ranch, ">= 1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "5ee0375680bca8f20c4d85f58c2894441443a743355430ff33a783fe03296779"}, - "geolix": {:hex, :geolix, "2.0.0", "7e65764bedfc98d08a3ddb24c417657c9d438eff163280b45fbb7de289626acd", [:mix], [], "hexpm", "8742bf588ed0bb7def2c443204d09d355990846c6efdff96ded66aac24c301df"}, - "geolix_adapter_mmdb2": {:hex, :geolix_adapter_mmdb2, "0.6.0", "6ab9dbf6ea395817aa1fd2597be25d0dda1853c7f664e62716e47728d18bc4f9", [:mix], [{:geolix, "~> 2.0", [hex: :geolix, repo: "hexpm", optional: false]}, {:mmdb2_decoder, "~> 3.0", [hex: :mmdb2_decoder, repo: "hexpm", optional: false]}], "hexpm", "06ff962feae8a310cffdf86b74bfcda6e2d0dccb439bb1f62df2b657b1c0269b"}, "gettext": {:hex, :gettext, "0.19.1", "564953fd21f29358e68b91634799d9d26989f8d039d7512622efb3c3b1c97892", [:mix], [], "hexpm", "10c656c0912b8299adba9b061c06947511e3f109ab0d18b44a866a4498e77222"}, "gproc": {:hex, :gproc, "0.8.0", "cea02c578589c61e5341fce149ea36ccef236cc2ecac8691fba408e7ea77ec2f", [:rebar3], [], "hexpm", "580adafa56463b75263ef5a5df4c86af321f68694e7786cb057fd805d1e2a7de"}, "grpcbox": {:hex, :grpcbox, "0.16.0", "b83f37c62d6eeca347b77f9b1ec7e9f62231690cdfeb3a31be07cd4002ba9c82", [:rebar3], [{:acceptor_pool, "~>1.0.0", [hex: :acceptor_pool, repo: "hexpm", optional: false]}, {:chatterbox, "~>0.13.0", [hex: :ts_chatterbox, repo: "hexpm", optional: false]}, {:ctx, "~>0.6.0", [hex: :ctx, repo: "hexpm", optional: false]}, {:gproc, "~>0.8.0", [hex: :gproc, repo: "hexpm", optional: false]}], "hexpm", "294df743ae20a7e030889f00644001370a4f7ce0121f3bbdaf13cf3169c62913"}, @@ -67,6 +65,7 @@ "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"}, "kaffy": {:hex, :kaffy, "0.9.0", "bef34c9729f6a3af4d0dea8eede8bcb9e11371a83ac9a8b393991bce81839517", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.11", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "d18ff57b8e68feb433aed11e71510cd357abc7034e75358af5deff7d0d4c6ed3"}, "location": {:git, "https://github.com/plausible/location.git", "8faf4f08b06905adde43554dc1d9d35675654816", []}, + "locus": {:hex, :locus, "2.3.6", "c9f53fd5df872fca66a54dc0aa2f8b2d3640388e56a0c39a741be0df6d8854bf", [:rebar3], [{:tls_certificate_check, "~> 1.9", [hex: :tls_certificate_check, repo: "hexpm", optional: false]}], "hexpm", "6087aa9a69673e7011837fb4b3d7f756560adde76892c32f5f93904ee30064e2"}, "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, @@ -75,7 +74,6 @@ "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mint": {:hex, :mint, "1.4.2", "50330223429a6e1260b2ca5415f69b0ab086141bc76dc2fbf34d7c389a6675b2", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "ce75a5bbcc59b4d7d8d70f8b2fc284b1751ffb35c7b6a6302b5192f8ab4ddd80"}, - "mmdb2_decoder": {:hex, :mmdb2_decoder, "3.0.1", "78e3aedde88035c6873ada5ceaf41b7f15a6259ed034e0eaca72ccfa937798f0", [:mix], [], "hexpm", "316af0f388fac824782d944f54efe78e7c9691bbbdb0afd5cccdd0510adf559d"}, "mox": {:hex, :mox, "1.0.2", "dc2057289ac478b35760ba74165b4b3f402f68803dd5aecd3bfd19c183815d64", [:mix], [], "hexpm", "f9864921b3aaf763c8741b5b8e6f908f44566f1e427b2630e89e9a73b981fef2"}, "nanoid": {:hex, :nanoid, "2.0.5", "1d2948d8967ef2d948a58c3fef02385040bd9823fc6394bd604b8d98e5516b22", [:mix], [], "hexpm", "956e8876321104da72aa48770539ff26b36b744cd26753ec8e7a8a37e53d5f58"}, "nimble_options": {:hex, :nimble_options, "0.5.1", "5c166f7669e40333191bea38e3bd3811cc13f459f1e4be49e89128a21b5d8c4d", [:mix], [], "hexpm", "d176cf7baa4fef0ceb301ca3eb8b55bd7de3e45f489c4f8b4f2849f1f114ef3e"}, diff --git a/test/plausible_web/controllers/api/external_controller_test.exs b/test/plausible_web/controllers/api/external_controller_test.exs index 061be8b57..e63fb40b4 100644 --- a/test/plausible_web/controllers/api/external_controller_test.exs +++ b/test/plausible_web/controllers/api/external_controller_test.exs @@ -651,7 +651,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do assert event.referrer == "" end - # Fake data is set up in config/test.exs + # Fake geo is loaded from test/priv/GeoLite2-City-Test.mmdb test "looks up location data from the ip address", %{conn: conn, domain: domain} do params = %{ name: "pageview", @@ -660,15 +660,15 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do } conn - |> put_req_header("x-forwarded-for", "2.2.2.2") + |> put_req_header("x-forwarded-for", "2.125.160.216") |> post("/api/event", params) pageview = get_event(domain) - assert pageview.country_code == "FR" - assert pageview.subdivision1_code == "FR-IDF" - assert pageview.subdivision2_code == "FR-75" - assert pageview.city_geoname_id == 2_988_507 + assert pageview.country_code == "GB" + assert pageview.subdivision1_code == "GB-ENG" + assert pageview.subdivision2_code == "GB-WBK" + assert pageview.city_geoname_id == 2_655_045 end test "ignores unknown country code ZZ", %{conn: conn, domain: domain} do @@ -736,7 +736,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do } conn - |> put_req_header("x-forwarded-for", "1.1.1.1:123") + |> put_req_header("x-forwarded-for", "216.160.83.56:123") |> post("/api/event", params) pageview = get_event(domain) @@ -752,12 +752,12 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do } conn - |> put_req_header("x-forwarded-for", "1:1:1:1:1:1:1:1") + |> put_req_header("x-forwarded-for", "2001:218:1:1:1:1:1:1") |> post("/api/event", params) pageview = get_event(domain) - assert pageview.country_code == "US" + assert pageview.country_code == "JP" end test "works with ipv6 with a port number in x-forwarded-for", %{conn: conn, domain: domain} do @@ -768,12 +768,12 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do } conn - |> put_req_header("x-forwarded-for", "[1:1:1:1:1:1:1:1]:123") + |> put_req_header("x-forwarded-for", "[2001:218:1:1:1:1:1:1]:123") |> post("/api/event", params) pageview = get_event(domain) - assert pageview.country_code == "US" + assert pageview.country_code == "JP" end test "uses cloudflare's special header for client IP address if present", %{ @@ -788,7 +788,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do conn |> put_req_header("x-forwarded-for", "0.0.0.0") - |> put_req_header("cf-connecting-ip", "1.1.1.1") + |> put_req_header("cf-connecting-ip", "216.160.83.56") |> post("/api/event", params) pageview = get_event(domain) @@ -808,7 +808,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do conn |> put_req_header("x-forwarded-for", "0.0.0.0") - |> put_req_header("b-forwarded-for", "1.1.1.1,9.9.9.9") + |> put_req_header("b-forwarded-for", "216.160.83.56,9.9.9.9") |> post("/api/event", params) pageview = get_event(domain) @@ -827,7 +827,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do } conn - |> put_req_header("forwarded", "by=0.0.0.0;for=1.1.1.1;host=somehost.com;proto=https") + |> put_req_header("forwarded", "by=0.0.0.0;for=216.160.83.56;host=somehost.com;proto=https") |> post("/api/event", params) pageview = get_event(domain) @@ -845,13 +845,13 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do conn |> put_req_header( "forwarded", - "by=0.0.0.0;for=\"[1:1:1:1:1:1:1:1]\",for=0.0.0.0;host=somehost.com;proto=https" + "by=0.0.0.0;for=\"[2001:218:1:1:1:1:1:1]\",for=0.0.0.0;host=somehost.com;proto=https" ) |> post("/api/event", params) pageview = get_event(domain) - assert pageview.country_code == "US" + assert pageview.country_code == "JP" end test "URL is decoded", %{conn: conn, domain: domain} do @@ -1042,7 +1042,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do conn |> put_req_header("user-agent", @user_agent) - |> put_req_header("x-forwarded-for", "982.32.12.1") + |> put_req_header("x-forwarded-for", "82.32.12.1") |> post("/api/event", params) [one, two] = get_events(domain) diff --git a/test/priv/GeoLite2-City-Test.mmdb b/test/priv/GeoLite2-City-Test.mmdb new file mode 100644 index 000000000..ed9adc654 Binary files /dev/null and b/test/priv/GeoLite2-City-Test.mmdb differ diff --git a/test/priv/README.md b/test/priv/README.md new file mode 100644 index 000000000..eb9993723 --- /dev/null +++ b/test/priv/README.md @@ -0,0 +1 @@ +`GeoLite2-City-Test.mmdb` is downloaded from https://github.com/maxmind/MaxMind-DB