defmodule Blossom.Controllers.Users do alias Blossom.Controllers.Users alias Blossom.Passwords alias KilnCore.Utils def word_from_stream(stream) do stream |> Enum.random() |> String.trim() |> String.downcase() end def make_username(params) do if File.exists?("/usr/share/dict/words") do stream = File.stream!("/usr/share/dict/words") word_one = Users.word_from_stream(stream) word_two = Users.word_from_stream(stream) "#{word_one}-#{word_two}-#{Enum.random(1..100)}" else params["email"] |> String.split("@") |> List.first() end end def new_user_params(params) do params |> Map.replace("email", String.downcase(params["email"])) |> Map.replace("password", Passwords.hash(params["password"])) |> Map.put_new("username", Users.make_username(params)) end def make_user(outcome, params, model) do case outcome do {:ok, _} -> params |> Users.new_user_params() |> model.new(:public) error -> error end end def autojoin_orgs(outcome, ctx) do case outcome do {:ok, user} -> ctx[:organizations].process_autojoins(user) _ -> nil end outcome end def queue(outcome, kind, ctx) do case outcome do {:ok, user} -> ctx[:worker].queue(user.id, kind) _ -> nil end outcome end def new(params, ctx) do if ctx[:settings].is_enabled?("allow_new_users") do case params do %{"email" => _, "password" => password} -> password |> ctx[:auth].validate_new_password() |> Users.make_user(params, ctx[:model]) |> Users.autojoin_orgs(ctx) |> Users.queue("email_verification", ctx) _ -> {:error, :missing_fields} end else {:error, :forbidden} end end def login(params, ctx) do case params do %{"email" => email, "password" => password, "realm" => realm} -> %{"email" => String.downcase(email), "realm" => realm} |> ctx[:model].get(:system) |> List.first() |> ctx[:auth].login(password) _ -> {:error, :missing_fields} end end def verify_email(code, user, ctx) do params = %{"code" => code, "kind" => "email_verification"} case ctx[:codes].verify(params, user) do {:ok, code} -> ctx[:model].edit(%{"email_verified" => true}, code.user, :system) error -> error end end def start_reset_password(params, ctx) do case params do %{"email" => email, "realm" => realm} -> case ctx[:model].get(%{"email" => email, "realm" => realm}, :system) do [user] -> Users.queue({:ok, user}, "password_reset", ctx) [] -> [] end _ -> {:error, :missing_fields} end end def make_new_password(params, ctx) do new_params = Map.put(params, "kind", "password_reset") case ctx[:codes].verify(new_params, :system) do {:ok, code} -> %{"password" => Passwords.hash(params["password"])} |> ctx[:model].edit(code.user, :system) _error -> {:error, :forbidden} end end def confirm_reset_password(params, ctx) do if params["password"] != nil do case ctx[:auth].validate_new_password(params["password"]) do {:ok, _} -> Users.make_new_password(params, ctx) e -> e end else {:error, :missing_fields} end end defmacro __using__(opts \\ []) do quote do use KilnCore.Controller, unquote(opts) @auth Utils.option_or_mod_replace( @caller, unquote(opts), :auth, Auth, 1, 0..1 ) @organizations Utils.option_or_mod_replace( @caller, unquote(opts), :organizations, Organizations, 2 ) @settings Utils.option_or_mod_replace( @caller, unquote(opts), :settings, Settings, 2 ) @codes Utils.option_or_mod_replace( @caller, unquote(opts), :otp_codes, OtpCodes, 2 ) @worker Utils.option_or_mod_replace( Utils.replace_module(@caller, Workers), unquote(opts), :worker, Mailer, 2 ) @ctx [ auth: @auth, codes: @codes, model: @model, organizations: @organizations, settings: @settings, worker: @worker ] def new(params, _user, _opts) do Users.new(params, @ctx) end def login(params, _user, _opts), do: Users.login(params, @ctx) def verify_email(code, user, _opts) do Users.verify_email(code, user, @ctx) end def start_reset_password(params, _user, _opts) do Users.start_reset_password(params, @ctx) end def confirm_reset_password(params, _user, _opts) do Users.confirm_reset_password(params, @ctx) end defoverridable new: 3, login: 3, verify_email: 3, start_reset_password: 3, confirm_reset_password: 3 end end end