Initial commit on Forgejo
Fresh repository history for elektrine/tarakan hosted at https://git.elektrine.com/elektrine/tarakan.
This commit is contained in:
commit
af6077b9c3
455 changed files with 78366 additions and 0 deletions
138
lib/tarakan_web/controllers/account_session_controller.ex
Normal file
138
lib/tarakan_web/controllers/account_session_controller.ex
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
defmodule TarakanWeb.AccountSessionController do
|
||||
use TarakanWeb, :controller
|
||||
|
||||
alias Tarakan.Accounts
|
||||
alias TarakanWeb.AccountAuth
|
||||
alias TarakanWeb.BrowserRateLimit
|
||||
|
||||
def create(conn, %{"_action" => "confirmed"} = params) do
|
||||
rate_limited_create(conn, params, "Account confirmed successfully.")
|
||||
end
|
||||
|
||||
def create(conn, params) do
|
||||
rate_limited_create(conn, params, "Welcome back!")
|
||||
end
|
||||
|
||||
defp rate_limited_create(conn, params, info) do
|
||||
account_params = Map.get(params, "account", %{})
|
||||
|
||||
identifier =
|
||||
account_params["identifier"] || account_params["email"] || account_params["token"] ||
|
||||
account_params["session_token"] || "missing"
|
||||
|
||||
remote_ip = remote_ip(conn)
|
||||
identifier_key = :crypto.hash(:sha256, identifier |> to_string() |> String.downcase())
|
||||
|
||||
checks = [
|
||||
BrowserRateLimit.allowed?(:login_ip, remote_ip),
|
||||
BrowserRateLimit.allowed?(:login_pair, {remote_ip, identifier_key})
|
||||
]
|
||||
|
||||
if Enum.all?(checks) do
|
||||
create(conn, params, info)
|
||||
else
|
||||
conn
|
||||
|> put_flash(:error, "Invalid credentials or too many attempts. Try again later.")
|
||||
|> redirect(to: ~p"/accounts/log-in")
|
||||
end
|
||||
end
|
||||
|
||||
# instant registration login (one-time session bootstrap token; does not
|
||||
# confirm the email address the way a magic-link login does)
|
||||
defp create(conn, %{"account" => %{"session_token" => encoded} = account_params}, info) do
|
||||
with {:ok, token} <- Base.url_decode64(encoded, padding: false),
|
||||
{account, _inserted_at} <- Accounts.get_account_by_session_token(token) do
|
||||
# One-time bootstrap: log_in_account mints the real session token.
|
||||
Accounts.delete_account_session_token(token)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, info)
|
||||
|> AccountAuth.log_in_account(account, account_params)
|
||||
else
|
||||
_other ->
|
||||
conn
|
||||
|> put_flash(:error, "The link is invalid or it has expired.")
|
||||
|> redirect(to: ~p"/accounts/log-in")
|
||||
end
|
||||
end
|
||||
|
||||
# magic link login
|
||||
defp create(conn, %{"account" => %{"token" => token} = account_params}, info) do
|
||||
case Accounts.login_account_by_magic_link(token) do
|
||||
{:ok, {account, disconnect_ref}} ->
|
||||
AccountAuth.disconnect_sessions(disconnect_ref)
|
||||
|
||||
conn
|
||||
|> put_flash(:info, info)
|
||||
|> AccountAuth.log_in_account(account, account_params)
|
||||
|
||||
_ ->
|
||||
conn
|
||||
|> put_flash(:error, "The link is invalid or it has expired.")
|
||||
|> redirect(to: ~p"/accounts/log-in")
|
||||
end
|
||||
end
|
||||
|
||||
# handle/email + password login
|
||||
defp create(conn, %{"account" => account_params}, info) do
|
||||
identifier = Map.get(account_params, "identifier", "")
|
||||
password = Map.get(account_params, "password", "")
|
||||
|
||||
if account = Accounts.get_account_by_identifier_and_password(identifier, password) do
|
||||
conn
|
||||
|> put_flash(:info, info)
|
||||
|> AccountAuth.log_in_account(account, account_params)
|
||||
else
|
||||
# Do not disclose whether the handle or email is registered.
|
||||
conn
|
||||
|> put_flash(:error, "Invalid handle, email, or password")
|
||||
|> put_flash(:identifier, String.slice(identifier, 0, 160))
|
||||
|> redirect(to: ~p"/accounts/log-in")
|
||||
end
|
||||
end
|
||||
|
||||
def update_password(conn, %{"account" => account_params}) do
|
||||
account = conn.assigns.current_scope.account
|
||||
|
||||
cond do
|
||||
not Accounts.sudo_mode?(account) ->
|
||||
# The sudo window can expire between rendering the form and POSTing it;
|
||||
# send the account through re-authentication instead of crashing.
|
||||
conn
|
||||
|> put_flash(
|
||||
:error,
|
||||
"Confirm it's you with a magic link before changing sensitive settings."
|
||||
)
|
||||
|> redirect(to: AccountAuth.reauth_path(~p"/accounts/settings"))
|
||||
|
||||
is_nil(account.confirmed_at) ->
|
||||
conn
|
||||
|> put_flash(
|
||||
:error,
|
||||
"Confirm your email address with a login link before setting a password."
|
||||
)
|
||||
|> redirect(to: ~p"/accounts/settings")
|
||||
|
||||
true ->
|
||||
{:ok, {account, disconnect_ref}} =
|
||||
Accounts.update_account_password(account, account_params)
|
||||
|
||||
# disconnect all existing LiveViews with old sessions
|
||||
AccountAuth.disconnect_sessions(disconnect_ref)
|
||||
|
||||
conn
|
||||
|> put_session(:account_return_to, ~p"/accounts/settings")
|
||||
|> put_flash(:info, "Password updated successfully!")
|
||||
|> AccountAuth.log_in_account(account)
|
||||
end
|
||||
end
|
||||
|
||||
def delete(conn, _params) do
|
||||
conn
|
||||
|> put_flash(:info, "Logged out successfully.")
|
||||
|> AccountAuth.log_out_account()
|
||||
end
|
||||
|
||||
# Bucket by /64 for IPv6 so prefix rotation cannot mint fresh rate-limit buckets.
|
||||
defp remote_ip(conn), do: TarakanWeb.Plugs.ClientIp.remote_ip_bucket(conn)
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue