Skip to content

Commit

Permalink
add LOG_FAILED_LOGIN_ATTEMPTS (#2936)
Browse files Browse the repository at this point in the history
* add failed login logs

* put failed login attempt logs behind a config option

* add changelog entry

* add config test

* add auth_controller tests

* move tests to separate non-async test module

---------

Co-authored-by: Uku Taht <Uku.taht@gmail.com>
  • Loading branch information
ruslandoga and ukutaht committed May 25, 2023
1 parent ce7401d commit 40e95ff
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
- 'Last updated X seconds ago' info to 'current visitors' tooltips
- Add support for more Bamboo adapters, i.e. `Bamboo.MailgunAdapter`, `Bamboo.MandrillAdapter`, `Bamboo.SendGridAdapter` plausible/analytics#2649
- Ability to change domain for existing site (requires numeric IDs data migration, instructions will be provided separately) UI + API (`PUT /api/v1/sites`)
- Add `LOG_FAILED_LOGIN_ATTEMPTS` environment variable to enable failed login attempts logs plausible/analytics#2936
- Add `MAILER_NAME` environment variable support plausible/analytics#2937
- Add `MAILGUN_BASE_URI` support for `Bamboo.MailgunAdapter` plausible/analytics#2935

Expand Down
8 changes: 7 additions & 1 deletion config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,20 @@ disable_cron =
|> get_var_from_path_or_env("DISABLE_CRON", "false")
|> String.to_existing_atom()

log_failed_login_attempts =
config_dir
|> get_var_from_path_or_env("LOG_FAILED_LOGIN_ATTEMPTS", "false")
|> String.to_existing_atom()

config :plausible,
environment: env,
mailer_email: mailer_email,
super_admin_user_ids: super_admin_user_ids,
site_limit: site_limit,
site_limit_exempt: site_limit_exempt,
is_selfhost: is_selfhost,
custom_script_name: custom_script_name
custom_script_name: custom_script_name,
log_failed_login_attempts: log_failed_login_attempts

config :plausible, :selfhost,
enable_email_verification: enable_email_verification,
Expand Down
11 changes: 11 additions & 0 deletions lib/plausible_web/controllers/auth_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -371,12 +371,15 @@ defmodule PlausibleWeb.AuthController do
|> redirect(to: login_dest)
else
:wrong_password ->
maybe_log_failed_login_attempts("wrong password for #{email}")

render(conn, "login_form.html",
error: "Wrong email or password. Please try again.",
layout: {PlausibleWeb.LayoutView, "focus.html"}
)

:user_not_found ->
maybe_log_failed_login_attempts("user not found for #{email}")
Plausible.Auth.Password.dummy_calculation()

render(conn, "login_form.html",
Expand All @@ -385,6 +388,8 @@ defmodule PlausibleWeb.AuthController do
)

{:rate_limit, _} ->
maybe_log_failed_login_attempts("too many logging attempts for #{email}")

render_error(
conn,
429,
Expand All @@ -393,6 +398,12 @@ defmodule PlausibleWeb.AuthController do
end
end

defp maybe_log_failed_login_attempts(message) do
if Application.get_env(:plausible, :log_failed_login_attempts) do
Logger.warning("[login] #{message}")
end
end

@login_interval 60_000
@login_limit 5
defp check_ip_rate_limit(conn) do
Expand Down
17 changes: 17 additions & 0 deletions test/plausible/config_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,23 @@ defmodule Plausible.ConfigTest do
end
end

describe "log_failed_login_attempts" do
test "can be true" do
env = {"LOG_FAILED_LOGIN_ATTEMPTS", "true"}
assert get_in(runtime_config(env), [:plausible, :log_failed_login_attempts]) == true
end

test "can be false" do
env = {"LOG_FAILED_LOGIN_ATTEMPTS", "false"}
assert get_in(runtime_config(env), [:plausible, :log_failed_login_attempts]) == false
end

test "is false by default" do
env = {"LOG_FAILED_LOGIN_ATTEMPTS", nil}
assert get_in(runtime_config(env), [:plausible, :log_failed_login_attempts]) == false
end
end

defp runtime_config(env) do
put_system_env_undo(env)
Config.Reader.read!("config/runtime.exs", env: :prod)
Expand Down
51 changes: 51 additions & 0 deletions test/plausible_web/controllers/auth_controller/logs_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
defmodule PlausibleWeb.AuthController.LogsTest do
use PlausibleWeb.ConnCase
import ExUnit.CaptureLog

describe "POST /login" do
setup do
patch_env(:log_failed_login_attempts, true)
end

test "logs on missing user", %{conn: conn} do
logs =
capture_log(fn ->
post(conn, "/login", email: "user@example.com", password: "password")
end)

assert logs =~ "[warning] [login] user not found for user@example.com"
end

test "logs on wrong password", %{conn: conn} do
user = insert(:user, password: "password")

logs =
capture_log(fn ->
post(conn, "/login", email: user.email, password: "wrong")
end)

assert logs =~ "[warning] [login] wrong password for #{user.email}"
end

test "logs on too many login attempts", %{conn: conn} do
user = insert(:user, password: "password")

capture_log(fn ->
for _ <- 1..5 do
build_conn()
|> put_req_header("x-forwarded-for", "1.1.1.1")
|> post("/login", email: user.email, password: "wrong")
end
end)

logs =
capture_log(fn ->
conn
|> put_req_header("x-forwarded-for", "1.1.1.1")
|> post("/login", email: user.email, password: "wrong")
end)

assert logs =~ "[warning] [login] too many logging attempts for #{user.email}"
end
end
end

0 comments on commit 40e95ff

Please sign in to comment.