From bd99555cd89948d35d5e9f03058fb429c54d8aa8 Mon Sep 17 00:00:00 2001 From: optclblast Date: Wed, 18 Sep 2024 00:13:39 +0300 Subject: [PATCH] almost done with auth. initialized sessions stuff --- lib/draincloud_core/auth/auth.ex | 6 +++- lib/draincloud_core/auth/sessions.ex | 21 +++++++++++ lib/draincloud_core/auth/sessions_store.ex | 21 +++++++++++ lib/draincloud_core/auth/users.ex | 11 ++++++ lib/draincloud_core/repo/user_repo.ex | 9 ----- .../auth_controller/auth_controller.ex | 36 ++++++++++--------- .../auth_controller/login_request.ex | 4 +-- .../auth_controller/register_request.ex | 8 ++--- .../controllers/errors/errors.ex | 11 ++++++ lib/draincloud_core_web/error_handler.ex | 10 ++++++ lib/draincloud_core_web/router.ex | 11 ++++++ .../migrations/20240917204624_sessions.exs | 15 ++++++++ 12 files changed, 131 insertions(+), 32 deletions(-) create mode 100644 lib/draincloud_core/auth/sessions.ex create mode 100644 lib/draincloud_core/auth/sessions_store.ex delete mode 100644 lib/draincloud_core/repo/user_repo.ex create mode 100644 lib/draincloud_core_web/error_handler.ex create mode 100644 priv/repo/migrations/20240917204624_sessions.exs diff --git a/lib/draincloud_core/auth/auth.ex b/lib/draincloud_core/auth/auth.ex index abf9db2..f963afa 100644 --- a/lib/draincloud_core/auth/auth.ex +++ b/lib/draincloud_core/auth/auth.ex @@ -1,6 +1,10 @@ defmodule DraincloudCore.Auth do def is_logon(_token) do # TODO here - true + false + end + + def auth_cookies(conn = %Plug.Conn{}, user = %DrainCloudCore.Auth.Users{}) do + end end diff --git a/lib/draincloud_core/auth/sessions.ex b/lib/draincloud_core/auth/sessions.ex new file mode 100644 index 0000000..0c65998 --- /dev/null +++ b/lib/draincloud_core/auth/sessions.ex @@ -0,0 +1,21 @@ +defmodule DrainCloudCore.Auth.SessionsRepo do + use Ecto.Schema + import Ecto.Changeset + import Ecto.Query + + schema "sessions" do + field :token, :string + field :user_id, :id + field :user_agent, :string + field :created_at, :utc_datetime + field :expires_at, :utc_datetime + end + + def changeset(session, params \\ %{}) do + session + |> cast(params, [:id, :token, :user_id, :user_agent, :created_at, :expires_at]) + |> validate_required([:id, :token, :user_id, :user_agent, :created_at, :expires_at]) + end + + +end diff --git a/lib/draincloud_core/auth/sessions_store.ex b/lib/draincloud_core/auth/sessions_store.ex new file mode 100644 index 0000000..fdffe7c --- /dev/null +++ b/lib/draincloud_core/auth/sessions_store.ex @@ -0,0 +1,21 @@ +defmodule DrainCloudCore.Auth.SessionsStore do + @behaviour Plug.Session.Store + + alias DrainCloudCore.Repo, as: Repo + + def init(_opts), do: :ok + + def put(conn, sid, any, opts) do + + end + + def get(conn, cookie, opts) do + + end + + def delete(conn, sid, opts) do + + end + + +end diff --git a/lib/draincloud_core/auth/users.ex b/lib/draincloud_core/auth/users.ex index de1d1d3..5e243cb 100644 --- a/lib/draincloud_core/auth/users.ex +++ b/lib/draincloud_core/auth/users.ex @@ -1,6 +1,9 @@ defmodule DrainCloudCore.Auth.Users do use Ecto.Schema import Ecto.Changeset + import Ecto.Query + + alias DrainCloudCore.Repo, as: Repo schema "users" do field :login, :string @@ -15,4 +18,12 @@ defmodule DrainCloudCore.Auth.Users do |> cast(params, [:id, :login, :password, :updated_at, :deleted_at]) |> validate_required([:id]) end + + def add_user(user) do + Repo.insert(user) + end + + def password_by_login(login) do + Repo.all(from u in "users", where: u.login == ^login, select: u.password, limit: 1) + end end diff --git a/lib/draincloud_core/repo/user_repo.ex b/lib/draincloud_core/repo/user_repo.ex deleted file mode 100644 index d7e3147..0000000 --- a/lib/draincloud_core/repo/user_repo.ex +++ /dev/null @@ -1,9 +0,0 @@ -defmodule DrainCloudCore.UsersRepo do - alias DrainCloudCore.Repo, as: Repo - import Ecto.Query - - def password_by_login(login) do - query = from u in :users, where: u.login = login, select: u.password, limit: 1 - Repo.all(query) - end -end diff --git a/lib/draincloud_core_web/controllers/auth_controller/auth_controller.ex b/lib/draincloud_core_web/controllers/auth_controller/auth_controller.ex index 5979750..4411127 100644 --- a/lib/draincloud_core_web/controllers/auth_controller/auth_controller.ex +++ b/lib/draincloud_core_web/controllers/auth_controller/auth_controller.ex @@ -1,32 +1,36 @@ defmodule DrainCloudCoreWeb.AuthController do use DrainCloudCoreWeb, :controller + import Plug.Conn + alias DrainCloudCoreWeb.AuthController.RegisterRequest, as: RegisterRequest alias DrainCloudCoreWeb.AuthController.LoginRequest, as: LoginRequest alias :logger, as: Log - - alias DrainCloudCore.Repo, as: Repo + alias DrainCloudCore.Auth.Users , as: Repo def register(conn, _params) do - req = RegisterRequest.from_request(conn) - - Log.debug("new register request: #{req}") - - model = RegisterRequest.to_model(req) - try do - Repo.insert(model) + RegisterRequest.from_request(conn) + |> RegisterRequest.to_model + |> Repo.add_user + # TODO send cookies and tokens + send_resp(conn, 200, Jason.encode! %{ok: true}) rescue - e in RuntimeError -> Log.error("failed to insert new user: #{e}") + e in RuntimeError -> + Log.error("failed to insert new user: #{e}") + raise e end - - json(conn, req) end def login(conn, _params) do - req = LoginRequest.from_request(conn) - - Log.debug("new login request: #{req}") - + try do + LoginRequest.from_request(conn) + # TODO send cookies and tokens + send_resp(conn, 200, Jason.encode! %{ok: true}) + rescue + e in RuntimeError -> + Log.error("failed to insert new user: #{e}") + raise e + end end end diff --git a/lib/draincloud_core_web/controllers/auth_controller/login_request.ex b/lib/draincloud_core_web/controllers/auth_controller/login_request.ex index 32d9b56..90ed84a 100644 --- a/lib/draincloud_core_web/controllers/auth_controller/login_request.ex +++ b/lib/draincloud_core_web/controllers/auth_controller/login_request.ex @@ -56,11 +56,11 @@ defmodule DraincloudCoreWeb.AuthController.LoginRequest do defp build(req) do %Request{ login: Map.get(req, "login"), - password: Map.get(req, "password") + password: Map.get(req, "password") # TODO hash password } end - defimpl String.Chars, for: DrainCloudCoreWeb.AuthController.LoginRequest do + defimpl String.Chars, for: __MODULE__ do def to_string(req) do case Jason.encode(req) do {:ok, str} -> str diff --git a/lib/draincloud_core_web/controllers/auth_controller/register_request.ex b/lib/draincloud_core_web/controllers/auth_controller/register_request.ex index 8392288..ea8d168 100644 --- a/lib/draincloud_core_web/controllers/auth_controller/register_request.ex +++ b/lib/draincloud_core_web/controllers/auth_controller/register_request.ex @@ -43,11 +43,11 @@ defmodule DrainCloudCoreWeb.AuthController.RegisterRequest do end defp validate_password(req) do - if String.length(Map.get(req, "password")) >= 8 do + if String.length(Map.get(req, "password")) >= 4 do req else raise InvalidArgumentException, %{ - message: "password must be 8 symbols at minimum", + message: "password must be 4 symbols at minimum", args: ["password"] } end @@ -56,11 +56,11 @@ defmodule DrainCloudCoreWeb.AuthController.RegisterRequest do defp build(req) do %Request{ login: Map.get(req, "login"), - password: Map.get(req, "password") + password: Map.get(req, "password") # TODO hash password } end - defimpl String.Chars, for: DrainCloudCoreWeb.AuthController.RegisterRequest do + defimpl String.Chars, for: __MODULE__ do def to_string(req) do case Jason.encode(req) do {:ok, str} -> str diff --git a/lib/draincloud_core_web/controllers/errors/errors.ex b/lib/draincloud_core_web/controllers/errors/errors.ex index 7fd488a..626a7a3 100644 --- a/lib/draincloud_core_web/controllers/errors/errors.ex +++ b/lib/draincloud_core_web/controllers/errors/errors.ex @@ -1,4 +1,6 @@ defmodule DrainCloudCoreWeb.Errors.InvalidArgumentException do + @derive [Jason.Encoder] + alias __MODULE__, as: InvalidArgumentException defexception [:message, :args] @@ -14,4 +16,13 @@ defmodule DrainCloudCoreWeb.Errors.InvalidArgumentException do _ -> %InvalidArgumentException{message: "Error: " <> term} end end + + defimpl String.Chars, for: __MODULE__ do + def to_string(err) do + case Jason.encode(err) do + {:ok, str} -> str + {:error, msg} -> raise "failed to encode register request to string: #{msg}" + end + end + end end diff --git a/lib/draincloud_core_web/error_handler.ex b/lib/draincloud_core_web/error_handler.ex new file mode 100644 index 0000000..f47ee71 --- /dev/null +++ b/lib/draincloud_core_web/error_handler.ex @@ -0,0 +1,10 @@ +defmodule DrainCloudCoreWeb.ErrorHandler do + alias DrainCloudCoreWeb.Errors.InvalidArgumentException, as: InvalidArgumentException + def handle_reason(reason = %InvalidArgumentException{}), do: Jason.encode!(reason) + + def handle_reason(reason) do + :logger.error(inspect(reason)) + + Jason.encode!(%{error: "Oops! Something went wrong!"}) + end +end diff --git a/lib/draincloud_core_web/router.ex b/lib/draincloud_core_web/router.ex index b787062..2481daa 100644 --- a/lib/draincloud_core_web/router.ex +++ b/lib/draincloud_core_web/router.ex @@ -1,5 +1,10 @@ defmodule DrainCloudCoreWeb.Router do use DrainCloudCoreWeb, :router + use Plug.ErrorHandler + import Plug.Conn + + alias :logger, as: Log + alias DrainCloudCoreWeb.ErrorHandler, as: ErrorHandler pipeline :browser do plug :accepts, ["html"] @@ -20,6 +25,12 @@ defmodule DrainCloudCoreWeb.Router do post "/register", AuthController, :register end + @impl Plug.ErrorHandler + def handle_errors(conn, %{kind: _kind, reason: reason, stack: _stack}) do + + send_resp(conn, 500, ErrorHandler.handle_reason(reason)) + end + # Enable LiveDashboard in development if Application.compile_env(:draincloud_core, :dev_routes) do # If you want to use the LiveDashboard in production, you should put diff --git a/priv/repo/migrations/20240917204624_sessions.exs b/priv/repo/migrations/20240917204624_sessions.exs new file mode 100644 index 0000000..626af6a --- /dev/null +++ b/priv/repo/migrations/20240917204624_sessions.exs @@ -0,0 +1,15 @@ +defmodule DrainCloudCore.Repo.Migrations.Sessions do + use Ecto.Migration + + def change do + create table(:sessions) do + add :token, :string + add :user_id, references("users", on_delete: :nothing) + add :user_agent, :string + add :created_at, :utc_datetime + add :expires_at, :utc_datetime + end + + create unique_index(:sessions, :token) + end +end