diff --git a/apps/fz_common/test/fz_common_test.exs b/apps/fz_common/test/fz_common_test.exs index c61cd8ff0..7bf9cacc4 100644 --- a/apps/fz_common/test/fz_common_test.exs +++ b/apps/fz_common/test/fz_common_test.exs @@ -1,4 +1,6 @@ defmodule FzCommonTest do use ExUnit.Case doctest FzCommon + + # XXX: Ensure command injection is NOT POSSIBLE end diff --git a/apps/fz_http/lib/fz_http_web.ex b/apps/fz_http/lib/fz_http_web.ex index d428fa555..21cea4a68 100644 --- a/apps/fz_http/lib/fz_http_web.ex +++ b/apps/fz_http/lib/fz_http_web.ex @@ -24,6 +24,7 @@ defmodule FzHttpWeb do import Plug.Conn import FzHttpWeb.Gettext import Phoenix.LiveView.Controller + import FzHttpWeb.ControllerHelpers alias FzHttpWeb.Router.Helpers, as: Routes end end diff --git a/apps/fz_http/lib/fz_http_web/controller_helpers.ex b/apps/fz_http/lib/fz_http_web/controller_helpers.ex new file mode 100644 index 000000000..dcf66b6ca --- /dev/null +++ b/apps/fz_http/lib/fz_http_web/controller_helpers.ex @@ -0,0 +1,44 @@ +defmodule FzHttpWeb.ControllerHelpers do + @moduledoc """ + Useful helpers for controllers + """ + + import Plug.Conn, + only: [ + get_session: 2, + put_resp_content_type: 2, + send_resp: 3, + halt: 1 + ] + + import Phoenix.Controller, + only: [ + redirect: 2 + ] + + alias FzHttpWeb.Router.Helpers, as: Routes + + def redirect_unauthenticated(conn, _options) do + case get_session(conn, :user_id) do + nil -> + conn + |> redirect(to: Routes.session_path(conn, :new)) + + _ -> + conn + end + end + + def require_authenticated(conn, _options) do + case get_session(conn, :user_id) do + nil -> + conn + |> put_resp_content_type("text/plain") + |> send_resp(403, "Forbidden") + |> halt() + + _ -> + conn + end + end +end diff --git a/apps/fz_http/lib/fz_http_web/controllers/account_controller.ex b/apps/fz_http/lib/fz_http_web/controllers/account_controller.ex new file mode 100644 index 000000000..7cf200ad7 --- /dev/null +++ b/apps/fz_http/lib/fz_http_web/controllers/account_controller.ex @@ -0,0 +1,8 @@ +defmodule FzHttpWeb.AccountController do + @moduledoc """ + Handles account-related operations + """ + use FzHttpWeb, :controller + + plug :redirect_unauthenticated +end diff --git a/apps/fz_http/lib/fz_http_web/controllers/device_controller.ex b/apps/fz_http/lib/fz_http_web/controllers/device_controller.ex index 0eac6aec1..059af1ad9 100644 --- a/apps/fz_http/lib/fz_http_web/controllers/device_controller.ex +++ b/apps/fz_http/lib/fz_http_web/controllers/device_controller.ex @@ -4,6 +4,8 @@ defmodule FzHttpWeb.DeviceController do """ use FzHttpWeb, :controller + plug :redirect_unauthenticated + def index(conn, _params) do render(conn, "index.html", nav_active: "Devices", page_heading: "Devices") end diff --git a/apps/fz_http/lib/fz_http_web/controllers/rule_controller.ex b/apps/fz_http/lib/fz_http_web/controllers/rule_controller.ex index 0fc55b4da..3b9dd202e 100644 --- a/apps/fz_http/lib/fz_http_web/controllers/rule_controller.ex +++ b/apps/fz_http/lib/fz_http_web/controllers/rule_controller.ex @@ -4,6 +4,8 @@ defmodule FzHttpWeb.RuleController do """ use FzHttpWeb, :controller + plug :redirect_unauthenticated + def index(conn, _params) do render(conn, "index.html", page_heading: "Rules") end diff --git a/apps/fz_http/lib/fz_http_web/controllers/session_controller.ex b/apps/fz_http/lib/fz_http_web/controllers/session_controller.ex index 48ecf0de6..db7e0656c 100644 --- a/apps/fz_http/lib/fz_http_web/controllers/session_controller.ex +++ b/apps/fz_http/lib/fz_http_web/controllers/session_controller.ex @@ -3,13 +3,40 @@ defmodule FzHttpWeb.SessionController do Implements the CRUD for a Session """ - alias FzHttp.Users + alias FzHttp.{Sessions, Users} use FzHttpWeb, :controller plug :put_root_layout, "auth.html" def new(conn, _params) do - render(conn, "new.html") + changeset = Sessions.new_session() + render(conn, "new.html", changeset: changeset) + end + + # POST /sessions + def create(conn, %{"session" => %{"email" => email, "password" => password}}) do + case Sessions.get_session(email: email) do + nil -> + conn + |> put_flash(:error, "Email not found.") + |> assign(:changeset, Sessions.new_session()) + |> render("new.html") + + record -> + case Sessions.create_session(record, %{email: email, password: password}) do + {:ok, session} -> + conn + |> clear_session() + |> put_session(:user_id, session.id) + |> redirect(to: Routes.device_path(conn, :index)) + + {:error, _changeset} -> + conn + |> put_flash(:error, "Error signing in. Ensure email and password are correct.") + |> assign(:changeset, Sessions.new_session()) + |> render("new.html") + end + end end # GET /sign_in/:token @@ -19,13 +46,12 @@ defmodule FzHttpWeb.SessionController do conn |> clear_session() |> put_session(:user_id, user.id) - |> put_flash(:info, "Signed in successfully.") - |> redirect(to: Routes.root_index_path(conn, :index)) + |> redirect(to: Routes.device_path(conn, :index)) {:error, error_msg} -> conn |> put_flash(:error, error_msg) - |> redirect(to: Routes.session_new_path(conn, :new)) + |> redirect(to: Routes.session_path(conn, :new)) end end diff --git a/apps/fz_http/lib/fz_http_web/controllers/user_controller.ex b/apps/fz_http/lib/fz_http_web/controllers/user_controller.ex index a0a5b6eaf..536d7829e 100644 --- a/apps/fz_http/lib/fz_http_web/controllers/user_controller.ex +++ b/apps/fz_http/lib/fz_http_web/controllers/user_controller.ex @@ -6,7 +6,7 @@ defmodule FzHttpWeb.UserController do alias FzHttp.Users use FzHttpWeb, :controller - plug :redirect_unauthenticated + plug :require_authenticated def delete(conn, _params) do user_id = get_session(conn, :user_id) @@ -18,27 +18,13 @@ defmodule FzHttpWeb.UserController do conn |> clear_session() |> put_flash(:info, "Account deleted successfully.") - |> redirect(to: Routes.root_index_path(conn, :index)) + |> redirect(to: Routes.session_path(conn, :new)) - # delete_user is unlikely to fail, if so write a test for it and uncomment this - # {:error, msg} -> - # conn - # |> clear_session() - # |> put_flash(:error, "Error deleting account: #{msg}") - # |> redirect(to: Routes.root_index_path(conn, :index)) - end - end - - def redirect_unauthenticated(conn, _options) do - case get_session(conn, :user_id) do - nil -> - conn - |> put_resp_content_type("text/plain") - |> send_resp(403, "Forbidden") - |> halt() - - _ -> + {:error, msg} -> conn + |> clear_session() + |> put_flash(:error, "Error deleting account: #{msg}") + |> redirect(to: Routes.session_path(conn, :new)) end end end diff --git a/apps/fz_http/lib/fz_http_web/live/account_live/show_live.ex b/apps/fz_http/lib/fz_http_web/live/account_live/show_live.ex index 7ab468943..7663f2f0a 100644 --- a/apps/fz_http/lib/fz_http_web/live/account_live/show_live.ex +++ b/apps/fz_http/lib/fz_http_web/live/account_live/show_live.ex @@ -8,7 +8,9 @@ defmodule FzHttpWeb.AccountLive.Show do @impl true def mount(params, session, socket) do - {:ok, assign_defaults(params, session, socket, &load_data/2)} + {:ok, + socket + |> assign_defaults(params, session, &load_data/2)} end @impl true diff --git a/apps/fz_http/lib/fz_http_web/live/device_live/index_live.ex b/apps/fz_http/lib/fz_http_web/live/device_live/index_live.ex index 5812d82bd..77e87da4c 100644 --- a/apps/fz_http/lib/fz_http_web/live/device_live/index_live.ex +++ b/apps/fz_http/lib/fz_http_web/live/device_live/index_live.ex @@ -7,7 +7,10 @@ defmodule FzHttpWeb.DeviceLive.Index do alias FzHttp.Devices def mount(params, session, socket) do - {:ok, assign_defaults(params, session, socket, &load_data/2)} + {:ok, + socket + |> assign_defaults(params, session, &load_data/2) + |> assign(:page_heading, "Devices")} end def handle_event("create_device", _params, socket) do diff --git a/apps/fz_http/lib/fz_http_web/live/device_live/show_live.ex b/apps/fz_http/lib/fz_http_web/live/device_live/show_live.ex index 7e0be4484..49b43bd09 100644 --- a/apps/fz_http/lib/fz_http_web/live/device_live/show_live.ex +++ b/apps/fz_http/lib/fz_http_web/live/device_live/show_live.ex @@ -8,7 +8,9 @@ defmodule FzHttpWeb.DeviceLive.Show do @impl true def mount(params, session, socket) do - {:ok, assign_defaults(params, session, socket, &load_data/2)} + {:ok, + socket + |> assign_defaults(params, session, &load_data/2)} end @impl true diff --git a/apps/fz_http/lib/fz_http_web/live/rule_live/index_live.ex b/apps/fz_http/lib/fz_http_web/live/rule_live/index_live.ex index a6e970d3c..fa91be4d5 100644 --- a/apps/fz_http/lib/fz_http_web/live/rule_live/index_live.ex +++ b/apps/fz_http/lib/fz_http_web/live/rule_live/index_live.ex @@ -5,6 +5,8 @@ defmodule FzHttpWeb.RuleLive.Index do use FzHttpWeb, :live_view def mount(params, session, socket) do - {:ok, assign_defaults(params, session, socket)} + {:ok, + socket + |> assign_defaults(params, session)} end end diff --git a/apps/fz_http/lib/fz_http_web/live/session_live/new_live.ex b/apps/fz_http/lib/fz_http_web/live/session_live/new_live.ex index dc5010d85..220bce2f9 100644 --- a/apps/fz_http/lib/fz_http_web/live/session_live/new_live.ex +++ b/apps/fz_http/lib/fz_http_web/live/session_live/new_live.ex @@ -34,15 +34,6 @@ defmodule FzHttpWeb.SessionLive.New do end end - # Guess email if signups are disabled and only one user exists - def email_field_opts(opts \\ []) do - if Users.single_user?() and signups_disabled?() do - opts ++ [value: Users.admin_email()] - else - opts - end - end - defp redirect_to_sign_in(socket, session) do case create_sign_in_token(session) do {:ok, token} -> @@ -68,10 +59,6 @@ defmodule FzHttpWeb.SessionLive.New do end end - defp signups_disabled? do - Application.fetch_env!(:fz_http, :disable_signup) - end - defp sign_in_params do FzMap.stringify_keys(Users.sign_in_keys()) end diff --git a/apps/fz_http/lib/fz_http_web/router.ex b/apps/fz_http/lib/fz_http_web/router.ex index 1843a904c..24eb73a64 100644 --- a/apps/fz_http/lib/fz_http_web/router.ex +++ b/apps/fz_http/lib/fz_http_web/router.ex @@ -5,11 +5,6 @@ defmodule FzHttpWeb.Router do use FzHttpWeb, :router - # View emails locally in development - if Mix.env() == :dev do - forward "/sent_emails", Bamboo.SentEmailViewerPlug - end - pipeline :browser do plug :accepts, ["html"] plug :fetch_session @@ -27,23 +22,18 @@ defmodule FzHttpWeb.Router do pipe_through :browser get "/", DeviceController, :index - get "/devices", DeviceController, :index - get "/rules", RuleController, :index - get "/session/new", SessionController, :new + resources "/session", SessionController, only: [:new, :create, :delete] - # live "/sign_in", SessionLive.New, :new - # live "/sign_up", UserLive.New, :new - # live "/account", AccountLive.Show, :show - # live "/account/edit", AccountLive.Show, :edit + live "/account", AccountLive.Show, :show + live "/account/edit", AccountLive.Show, :edit - # live "/rules", RuleLive.Index, :index + live "/rules", RuleLive.Index, :index - # live "/devices", DeviceLive.Index, :index - # live "/devices/:id", DeviceLive.Show, :show - # live "/devices/:id/edit", DeviceLive.Show, :edit + live "/devices", DeviceLive.Index, :index + live "/devices/:id", DeviceLive.Show, :show + live "/devices/:id/edit", DeviceLive.Show, :edit get "/sign_in/:token", SessionController, :create - post "/sign_out", SessionController, :delete delete "/user", UserController, :delete end end diff --git a/apps/fz_http/lib/fz_http_web/templates/device/index.html.eex b/apps/fz_http/lib/fz_http_web/templates/device/index.html.eex index 2d776d9c4..e69de29bb 100644 --- a/apps/fz_http/lib/fz_http_web/templates/device/index.html.eex +++ b/apps/fz_http/lib/fz_http_web/templates/device/index.html.eex @@ -1 +0,0 @@ -