From 13d7f832fe4dd5d160d256eb9e4c3c6820eb30b3 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Wed, 2 Oct 2019 17:25:35 -0400 Subject: [PATCH] Add authentication for web frontend --- lib/clacks/actor.ex | 4 +- lib/clacks/user.ex | 5 +++ .../controllers/login_controller.ex | 42 +++++++++++++++++++ lib/clacks_web/plug/web_authenticate.ex | 41 ++++++++++++++++++ lib/clacks_web/router.ex | 14 +++++++ lib/clacks_web/templates/login/login.html.eex | 13 ++++++ lib/clacks_web/views/login_view.ex | 3 ++ lib/clacks_web/views/page_view.ex | 2 +- 8 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 lib/clacks_web/controllers/login_controller.ex create mode 100644 lib/clacks_web/plug/web_authenticate.ex create mode 100644 lib/clacks_web/templates/login/login.html.eex create mode 100644 lib/clacks_web/views/login_view.ex diff --git a/lib/clacks/actor.ex b/lib/clacks/actor.ex index 0402431..f2a0b32 100644 --- a/lib/clacks/actor.ex +++ b/lib/clacks/actor.ex @@ -29,7 +29,7 @@ defmodule Clacks.Actor do @spec get_by_nickname(nickname :: String.t()) :: t() | nil def get_by_nickname(nickname) do - Repo.one(from a in __MODULE__, where: a.nickname == ^nickname) + Repo.get_by(__MODULE__, nickname: nickname) end @spec get_by_ap_id(ap_id :: String.t(), force_refetch :: boolean()) :: t() | nil @@ -43,7 +43,7 @@ defmodule Clacks.Actor do @spec get_cached_by_ap_id(ap_id :: String.t()) :: t() | nil def get_cached_by_ap_id(ap_id) do - Repo.one(from a in __MODULE__, where: a.ap_id == ^ap_id) + Repo.get_by(__MODULE__, ap_id: ap_id) end @spec fetch(ap_id :: String.t()) :: t() | nil diff --git a/lib/clacks/user.ex b/lib/clacks/user.ex index 576286f..73b75c7 100644 --- a/lib/clacks/user.ex +++ b/lib/clacks/user.ex @@ -1,6 +1,7 @@ defmodule Clacks.User do use Ecto.Schema import Ecto.Changeset + alias Clacks.Repo @type t() :: %__MODULE__{} @@ -40,4 +41,8 @@ defmodule Clacks.User do ) do change(changeset, Bcrypt.add_hash(password)) end + + def get_by_username(username) do + Repo.get_by(__MODULE__, username: username) + end end diff --git a/lib/clacks_web/controllers/login_controller.ex b/lib/clacks_web/controllers/login_controller.ex new file mode 100644 index 0000000..b00a194 --- /dev/null +++ b/lib/clacks_web/controllers/login_controller.ex @@ -0,0 +1,42 @@ +defmodule ClacksWeb.LoginController do + use ClacksWeb, :controller + alias Clacks.User + alias ClacksWeb.Router.Helpers, as: Routes + alias ClacksWeb.Endpoint + + def login(conn, params) do + render(conn, "login.html", %{ + continue: Map.get(params, "continue") + }) + end + + def login_post(conn, %{"username" => username, "password" => password} = params) do + user = User.get_by_username(username) + + case Bcrypt.check_pass(user, password) do + {:ok, user} -> + user_token = Phoenix.Token.sign(Endpoint, "user token", user.id) + redirect_uri = Map.get(params, "continue") || "/" + + conn + |> put_session(:user_token, user_token) + |> redirect(to: redirect_uri) + + {:error, _reason} -> + conn + |> put_flash(:error, "Invalid username or password.") + |> redirect(to: Routes.login_path(Endpoint, :login)) + end + end + + def login_post(conn, _params) do + redirect(conn, to: Routes.login_path(Endpoint, :login)) + end + + def logout(conn, _params) do + conn + |> clear_session() + |> put_flash(:info, "Logged out.") + |> redirect(to: "/") + end +end diff --git a/lib/clacks_web/plug/web_authenticate.ex b/lib/clacks_web/plug/web_authenticate.ex new file mode 100644 index 0000000..79e84a3 --- /dev/null +++ b/lib/clacks_web/plug/web_authenticate.ex @@ -0,0 +1,41 @@ +defmodule ClacksWeb.Plug.WebAuthenticate do + import Plug.Conn + alias Clacks.{Repo, User} + alias ClacksWeb.Router.Helpers, as: Routes + alias ClacksWeb.Endpoint + + def init(%{on_failure: on_failure_action} = opts) + when on_failure_action in [:redirect_to_login, :pass], + do: opts + + def init(_), do: %{on_failure: :redirect_to_login} + + def call(conn, %{on_failure: on_failure_action}) do + user_token = get_session(conn, :user_token) + + case Phoenix.Token.verify(Endpoint, "user token", user_token, max_age: 7 * 24 * 60 * 60) do + {:error, _reason} -> + on_failure(conn, on_failure_action) + + {:ok, user_id} -> + case Repo.get(User, user_id) do + nil -> + on_failure(conn, on_failure_action) + + user -> + user = Repo.preload(user, :actor) + assign(conn, :user, user) + end + end + end + + defp on_failure(conn, :redirect_to_login) do + conn + |> Phoenix.Controller.redirect(to: Routes.login_path(Endpoint, :login)) + |> halt() + end + + defp on_failure(conn, :pass) do + conn + end +end diff --git a/lib/clacks_web/router.ex b/lib/clacks_web/router.ex index 761ec8d..dd0a8e1 100644 --- a/lib/clacks_web/router.ex +++ b/lib/clacks_web/router.ex @@ -7,6 +7,11 @@ defmodule ClacksWeb.Router do plug :fetch_flash plug :protect_from_forgery plug :put_secure_browser_headers + plug ClacksWeb.Plug.WebAuthenticate, on_failure: :pass + end + + pipeline :browser_authenticated do + plug ClacksWeb.Plug.WebAuthenticate, on_failure: :redirect_to_login end pipeline :activitypub do @@ -15,6 +20,15 @@ defmodule ClacksWeb.Router do scope "/", ClacksWeb do pipe_through :browser + + get "/login", LoginController, :login + post "/login", LoginController, :login_post + post "/logout", LoginController, :logout_post + end + + scope "/", ClacksWeb do + pipe_through :browser + pipe_through :browser_authenticated end scope "/", ClacksWeb do diff --git a/lib/clacks_web/templates/login/login.html.eex b/lib/clacks_web/templates/login/login.html.eex new file mode 100644 index 0000000..46db376 --- /dev/null +++ b/lib/clacks_web/templates/login/login.html.eex @@ -0,0 +1,13 @@ +<%= form_tag Routes.login_path(@conn, :login_post), method: :post do %> + <%= if @continue do %> + + <% end %> + + + +
+ + +
+ <%= submit "Log In" %> +<% end %> diff --git a/lib/clacks_web/views/login_view.ex b/lib/clacks_web/views/login_view.ex new file mode 100644 index 0000000..33453a4 --- /dev/null +++ b/lib/clacks_web/views/login_view.ex @@ -0,0 +1,3 @@ +defmodule ClacksWeb.PageView do + use ClacksWeb, :view +end diff --git a/lib/clacks_web/views/page_view.ex b/lib/clacks_web/views/page_view.ex index 33453a4..9d58594 100644 --- a/lib/clacks_web/views/page_view.ex +++ b/lib/clacks_web/views/page_view.ex @@ -1,3 +1,3 @@ -defmodule ClacksWeb.PageView do +defmodule ClacksWeb.LoginView do use ClacksWeb, :view end