Skip to content

Commit

Permalink
feat: block access to admin pages when 2fa is disabled
Browse files Browse the repository at this point in the history
  • Loading branch information
nlwstein committed Aug 28, 2023
1 parent fa9bf5a commit 2f6e846
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 3 deletions.
31 changes: 31 additions & 0 deletions apps/api_web/lib/api_web/plugs/require_2factor.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
defmodule ApiWeb.Plugs.Require2Factor do
@moduledoc """
Plug enforcing a user to have 2fa enabled
"""

# , only: [render: 3, put_view: 2]
import Phoenix.Controller

def init(opts), do: opts

def call(conn, _opts) do
conn
|> fetch_user()
|> authenticate(conn)
end

defp fetch_user(conn) do
conn.assigns[:user]
end

defp authenticate(%ApiAccounts.User{totp_enabled: true}, conn), do: conn

defp authenticate(_, conn) do
conn
|> put_flash(
:error,
"Account does not have 2-Factor Authentication enabled. Please enable before performing administrative tasks."
)
|> redirect(to: ApiWeb.Router.Helpers.user_path(conn, :configure_2fa))
end
end
1 change: 1 addition & 0 deletions apps/api_web/lib/api_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ defmodule ApiWeb.Router do

pipeline :admin do
plug(ApiWeb.Plugs.RequireAdmin)
plug(ApiWeb.Plugs.Require2Factor)
end

pipeline :portal_view do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@
<div class="footer-links">
<label>Portal</label>
<ul>
<li><%= link "Login", to: session_path(@conn, :new) %></li>
<li><%= link "Register", to: user_path(@conn, :new) %></li>
<%= if user = @conn.assigns[:user] do %>
<li><%= link user.email, to: user_path(@conn, :show) %></li>
<li><%= link "Logout", to: session_path(@conn, :delete), method: :delete %></li>
<% else %>
<li><%= link "Login", to: session_path(@conn, :new) %></li>
<li><%= link "Register", to: user_path(@conn, :new) %></li>
<% end %>
<li><%= link "Docs", to: "/docs/swagger" %></li>
</ul>
</div>
Expand Down
55 changes: 55 additions & 0 deletions apps/api_web/test/api_web/plugs/require_2factor_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
defmodule ApiWeb.Plugs.Require2FactorTest do
use ApiWeb.ConnCase, async: true

setup %{conn: conn} do
conn =
conn
|> conn_with_session()
|> bypass_through(ApiWeb.Router, [:browser, :admin])

{:ok, conn: conn}
end

test "opts" do
assert ApiWeb.Plugs.Require2Factor.init([]) == []
end

describe ":require_2factor plug" do
test "gives 404 with no authenicated user", %{conn: conn} do
conn = get(conn, "/")
assert conn.status == 404
assert html_response(conn, 404) =~ "not found"
end

test "gives 404 for user without administrator role", %{conn: conn} do
conn =
conn
|> user_with_role(nil, true)
|> get("/")

assert html_response(conn, 404) =~ "not found"
end

test "redirects on missing 2fa, but valid admin account", %{conn: conn} do
conn =
conn
|> user_with_role("administrator", false)
|> get("/")

assert html_response(conn, 302)
end

test "allows user with administrator role and 2fa to proceed", %{conn: conn} do
conn =
conn
|> user_with_role("administrator", true)
|> get("/")

refute conn.status
end
end

defp user_with_role(conn, role, totp_enabled) do
Plug.Conn.assign(conn, :user, %ApiAccounts.User{role: role, totp_enabled: totp_enabled})
end
end
2 changes: 1 addition & 1 deletion apps/api_web/test/api_web/plugs/require_admin_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ defmodule ApiWeb.Plugs.RequireAdminTest do
end

defp user_with_role(conn, role) do
Plug.Conn.assign(conn, :user, %ApiAccounts.User{role: role})
Plug.Conn.assign(conn, :user, %ApiAccounts.User{role: role, totp_enabled: true})
end
end

0 comments on commit 2f6e846

Please sign in to comment.