240 lines
12 KiB
Elixir
240 lines
12 KiB
Elixir
defmodule SukaatoWeb.PageController do
|
|
alias SukaatoWeb.Theme
|
|
alias Sukaato.{Auth, StandardQueries, User}
|
|
alias QRCode.Render
|
|
import Ecto.Query
|
|
use SukaatoWeb, :controller
|
|
|
|
@rel_proj_root "../../.."
|
|
@site_config_file Path.expand(@rel_proj_root <> "/site.toml", __DIR__)
|
|
@site_config Toml.decode_file(@site_config_file)
|
|
@admin_username elem(@site_config, 1)["site"]["username"]
|
|
|
|
@page_links [
|
|
%PageLink{name: "Infernus", uri: "/"},
|
|
%PageLink{name: "Talisman", uri: "/pubkey"},
|
|
%PageLink{name: "Evokation", uri: "/contact"},
|
|
%PageLink{name: "Prophets", uri: "/thinkers"}
|
|
# @TODO place link as anchor element href attribute value inside template element for ALL pages
|
|
# %PageLink{name: "Passage", uri: "/login"}
|
|
]
|
|
|
|
@tables %{
|
|
user: User
|
|
}
|
|
|
|
def index(conn, _params) do
|
|
Theme.update(elem(@site_config, 1)["site"]["theme"])
|
|
render(conn, :index, layout: false, config: elem(@site_config, 1), page_name: "index", pages: @page_links)
|
|
end
|
|
|
|
def pubkey(conn, params) do
|
|
Theme.update(elem(@site_config, 1)["site"]["theme"])
|
|
user = if Map.has_key?(params, "user"), do: params["user"], else: @admin_username
|
|
id = if Map.has_key?(params, "id"), do: params["id"], else: ""
|
|
result = StandardQueries.get_pubkeys(user, id)
|
|
data = if tuple_size(result) >= 2, do: elem(result, 1), else: []
|
|
gpg_id = if length(data) > 0, do: Enum.map(data, fn d -> elem(d, 0) end), else: [""]
|
|
content = if length(data) > 0, do: Enum.map(data, fn d -> elem(d, 1) end), else: [""]
|
|
qr_embed = {Path.expand("../../../priv/static/images/sigil.svg", __DIR__), 200}
|
|
qr_settings = %Render.SvgSettings{image: qr_embed, structure: :readable, background_opacity: 0.0, scale: 4}
|
|
|
|
# @TODO account for case wherein pubkey_content is a list
|
|
if length(content) == 1 do
|
|
qr = if Enum.at(content, 0) != "", do: QRCode.create(Enum.at(content, 0)), else: ""
|
|
qr = QRCode.render(qr, :svg, qr_settings)
|
|
id_qr = Enum.zip([gpg_id, [qr], content])
|
|
|
|
render(conn, :pubkey, layout: false, config: elem(@site_config, 1), pages: @page_links, user: user, qr: id_qr)
|
|
else
|
|
# content = if length(content) > 0, do: Enum.filter(content, fn c -> c != "" end), else: []
|
|
qr = if length(content) > 1, do: Enum.map(content, fn c -> QRCode.create(c) end), else: [""]
|
|
qr = Enum.map(qr, fn q -> QRCode.render(q, :svg, qr_settings) end)
|
|
id_qr = Enum.zip([gpg_id, qr, content])
|
|
id_qr = Enum.filter(id_qr, fn q -> elem(q, 1) != "" end)
|
|
|
|
render(conn, :pubkey, layout: false, config: elem(@site_config, 1), pages: @page_links, user: user, qr: id_qr)
|
|
end
|
|
end
|
|
|
|
def think(conn, params) do
|
|
Theme.update(elem(@site_config, 1)["site"]["theme"])
|
|
username = if Map.has_key?(params, "user"), do: params["user"], else: @admin_username
|
|
thinker_data = File.read(Path.expand(@rel_proj_root <> "/priv/static/files/users/" <> username <> "/thinkers.json", __DIR__))
|
|
thinker_data = if elem(thinker_data, 0) == :ok, do: elem(thinker_data, 1)
|
|
thinker_data = elem(Jason.decode(thinker_data, keys: :atoms), 1)
|
|
|
|
render(conn, :thinkers, layout: false, config: elem(@site_config, 1), pages: @page_links, page_name: "thinkers", thinkers: thinker_data)
|
|
end
|
|
|
|
def contact(conn, params) do
|
|
Theme.update(elem(@site_config, 1)["site"]["theme"])
|
|
affil = if Map.has_key?(params, "user"), do: StandardQueries.get_inboxes(params["user"]), else: StandardQueries.get_inboxes(@admin_username)
|
|
affil = elem(affil, 1)
|
|
|
|
render(conn, :contact, layout: false, config: elem(@site_config, 1), pages: @page_links, affil: affil)
|
|
end
|
|
|
|
def two_fa(conn, _params) do
|
|
pass_token = get_session(conn, "pass_token")
|
|
twofa_token = get_session(conn, "2fa_token")
|
|
# IO.inspect(conn)
|
|
|
|
if is_nil(pass_token) do
|
|
redirect(conn, to: ~p"/login")
|
|
# status = put_status(conn, 404)
|
|
# ext = ".heex"
|
|
# render(status, ErrorView, "404.html" <> ext)
|
|
else
|
|
user_id = Enum.at(String.split(pass_token, ":", parts: 2), 0)
|
|
|
|
if is_nil(twofa_token) do
|
|
twofact = Auth.get_credentials(conn, ["code", "chall_resp"])
|
|
query = from u in @tables.user, select: {u.totp_active, u.totp_enabled}, where: u.username == ^user_id or u.email == ^user_id
|
|
totp_requisite = Auth.is_authflow_required?(user_id, :all?, (query))
|
|
query = from u in @tables.user, select: {u.fido_active, u.fido_enabled}, where: u.username == ^user_id or u.email == ^user_id
|
|
fido_requisite = Auth.is_authflow_required?(user_id, :all?, (query))
|
|
|
|
if is_nil(twofact) do
|
|
render(conn, :twofact, layout: false, config: elem(@site_config, 1), pages: @page_links, attempted: false, totp: totp_requisite, fido: fido_requisite)
|
|
else
|
|
result = if Map.has_key?(twofact, "code"), do: Auth.totp_bouncer(conn, user_id, twofact["code"]), else: {conn, !totp_requisite, nil}
|
|
conn = elem(result, 0)
|
|
authenticated = elem(result, 1)
|
|
IO.inspect(authenticated)
|
|
result = if Map.has_key?(twofact, "chall_resp"), do: Auth.fido_bouncer(conn, user_id, twofact["chall_resp"]), else: {conn, !fido_requisite, nil}
|
|
conn = elem(result, 0)
|
|
IO.inspect(elem(result, 1))
|
|
authenticated = authenticated and elem(result, 1)
|
|
IO.inspect(authenticated)
|
|
|
|
if authenticated do
|
|
# conn = elem(result, 0)
|
|
conn = if is_nil(twofa_token), do: conn, else: delete_session(conn, "2fa_token")
|
|
conn = if is_nil(twofa_token), do: conn, else: delete_session(conn, "pass_token")
|
|
conn = Auth.generate_user_session(conn, user_id, true, "login_token")
|
|
redirect(conn, to: ~p"/login")
|
|
else
|
|
render(conn, :twofact, layout: false, config: elem(@site_config, 1), pages: @page_links, attempted: true, totp: totp_requisite, fido: fido_requisite)
|
|
end
|
|
end
|
|
else
|
|
conn = if is_nil(twofa_token), do: conn, else: delete_session(conn, "2fa_token")
|
|
conn = if is_nil(twofa_token), do: conn, else: delete_session(conn, "pass_token")
|
|
conn = Auth.generate_user_session(conn, user_id, true, "login_token")
|
|
redirect(conn, to: ~p"/login")
|
|
end
|
|
end
|
|
end
|
|
|
|
def login(conn, _params) do
|
|
Theme.update(elem(@site_config, 1)["site"]["theme"])
|
|
|
|
demand = Auth.get_credentials(conn, ["logout"])
|
|
want_logout = if is_nil(demand), do: "false", else: demand["logout"]
|
|
current_login_token = get_session(conn, "login_token")
|
|
result = if want_logout == "true" and !is_nil(current_login_token), do: StandardQueries.dump_login_token(current_login_token), else: {:ok, %{}}
|
|
conn = if want_logout == "true" and elem(result, 0) == :ok, do: delete_session(conn, "login_token"), else: conn
|
|
|
|
pass_token = get_session(conn, "pass_token")
|
|
twofa_token = get_session(conn, "2fa_token")
|
|
login_token = get_session(conn, "login_token")
|
|
|
|
submission_names = [
|
|
"Act as Clergy", # @NOTE logging in
|
|
"Return to Laity" # @NOTE logging out
|
|
]
|
|
button_name_options = %{
|
|
names: submission_names
|
|
}
|
|
form_fields = %{
|
|
username: "Clerical Title",
|
|
password: "Incantation",
|
|
code: "Sacred Number"
|
|
}
|
|
|
|
if is_nil(login_token) do
|
|
if is_nil(pass_token) do
|
|
# PSEUDOCODE
|
|
# - Create empty map
|
|
# - Attempt to acquire username from conn's body parameters
|
|
# - Attempt to place non-nil username in key added to empty map
|
|
# - Attempt to acquire password from conn's body parameters
|
|
# - Attempt to place non-nil password in key added to empty map
|
|
# - If these keys exist in map or their values are not nil, call password authentication function
|
|
# -- When password authentication succeeds
|
|
# --- Place non-nil value in session key pass_token
|
|
# --- When 2fa step is necessary (can be left to redirect route)
|
|
# ---- Redirect to 2fa route and associated controller
|
|
# --- Otherwise when 2fa step is unnecessary (can be left to redirect route)
|
|
# ---- Place non-nil value in session key login_token
|
|
# ---- Redirect to this controller's route
|
|
# -- When password authentication fails, redirect to this controller's router
|
|
|
|
user = Auth.get_credentials(conn, ["username", "password"])
|
|
result = if is_nil(user), do: nil, else: Auth.password_bouncer(conn, user["username"], user["password"])
|
|
|
|
if is_nil(result) do
|
|
button_choice = Map.put(button_name_options, :choice, 0)
|
|
render(conn, :login, layout: false, config: elem(@site_config, 1), pages: @page_links, form_fields: form_fields, button_choice: button_choice, attempted: false)
|
|
else
|
|
authenticated = elem(result, 1)
|
|
|
|
if authenticated do
|
|
conn = elem(result, 0)
|
|
redirect(conn, to: ~p"/login") # -> should result in either logout or two-factor page
|
|
else
|
|
button_choice = Map.put(button_name_options, :choice, 0)
|
|
render(conn, :login, layout: false, config: elem(@site_config, 1), pages: @page_links, form_fields: form_fields, button_choice: button_choice, attempted: true)
|
|
end
|
|
end
|
|
else
|
|
if is_nil(twofa_token) do
|
|
# PSEUDOCODE
|
|
# - When 2fa step is necessary (unless left to 2fa route)
|
|
# -- Redirect to 2fa route and associated controller
|
|
# - Otherwise when 2fa step is unnecessary (unless left to 2fa route)
|
|
# -- Place non-nil value in session key login_token
|
|
# -- Redirect to this controller's router
|
|
|
|
user_id = Enum.at(String.split(pass_token, ":", parts: 2), 0)
|
|
twofact_required = Auth.is_authflow_required?(user_id, :any?)
|
|
# IO.inspect(twofact_required)
|
|
|
|
if twofact_required do
|
|
redirect(conn, to: ~p"/second_factor")
|
|
else
|
|
conn = Auth.generate_user_session(conn, user_id, true, "login_token")
|
|
conn = delete_session(conn, "pass_token")
|
|
# redirect(conn, to: ~p"/login") # -> should result in logout page
|
|
login_token = get_session(conn, "login_token")
|
|
StandardQueries.absorb_login_token(login_token)
|
|
button_choice = Map.put(button_name_options, :choice, 1)
|
|
render(conn, :logout, layout: false, config: elem(@site_config, 1), pages: @page_links, form_fields: form_fields, button_choice: button_choice)
|
|
end
|
|
else
|
|
# PSEUDOCODE
|
|
# - Remove non-nil value in session key pass_token
|
|
# - Place non-nil value in session key login_token
|
|
# - Redirect to this controller's router
|
|
|
|
conn = delete_session(conn, "2fa_token")
|
|
conn = delete_session(conn, "pass_token")
|
|
user_id = Enum.at(String.split(pass_token, ":", parts: 2), 0)
|
|
conn = Auth.generate_user_session(conn, user_id, true, "login_token")
|
|
# redirect(conn, to: ~p"/login") # -> should result in logout page
|
|
StandardQueries.absorb_login_token(login_token)
|
|
button_choice = Map.put(button_name_options, :choice, 1)
|
|
render(conn, :logout, layout: false, config: elem(@site_config, 1), pages: @page_links, form_fields: form_fields, button_choice: button_choice)
|
|
end
|
|
end
|
|
else
|
|
conn = if is_nil(twofa_token), do: conn, else: delete_session(conn, "2fa_token")
|
|
conn = if is_nil(twofa_token), do: conn, else: delete_session(conn, "pass_token")
|
|
StandardQueries.absorb_login_token(login_token)
|
|
button_choice = Map.put(button_name_options, :choice, 1)
|
|
render(conn, :logout, layout: false, config: elem(@site_config, 1), pages: @page_links, form_fields: form_fields, button_choice: button_choice)
|
|
end
|
|
end
|
|
end
|