Branch :
## kmxgit
## Copyright 2022 kmx.io <contact@kmx.io>
##
## Permission is hereby granted to use this software granted
## the above copyright notice and this permission paragraph
## are included in all copies and substantial portions of this
## software.
##
## THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
## PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
## AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
## THIS SOFTWARE.
defmodule KmxgitWeb.UserController do
use KmxgitWeb, :controller
alias Kmxgit.GitAuth
alias Kmxgit.GitManager
alias Kmxgit.Repo
alias Kmxgit.SlugManager
alias Kmxgit.UserManager
alias Kmxgit.UserManager.{Avatar, User}
alias KmxgitWeb.ErrorView
def avatar(conn, %{"login" => login,
"size" => size}) do
user = UserManager.get_user_by_login(login)
if user do
path = Avatar.path(user, size)
conn
|> put_resp_content_type("image/png")
|> send_file(200, path)
end
end
def avatar(conn, _) do
not_found(conn)
end
def edit(conn, params) do
current_user = conn.assigns.current_user
if params["login"] == User.login(current_user) do
user = current_user
changeset = UserManager.change_user(user)
email_changeset = UserManager.change_user_email(user)
password_changeset = UserManager.change_user_password(user)
conn
|> assign(:changeset, changeset)
|> assign(:email_changeset, email_changeset)
|> assign(:page_title, gettext("Edit user %{login}", login: User.login(user)))
|> assign(:password_changeset, password_changeset)
|> assign(:user, user)
|> render("edit.html")
else
not_found(conn)
end
end
def update(conn, params) do
current_user = conn.assigns.current_user
if params["login"] == User.login(current_user) do
user = current_user
case Repo.transaction(fn ->
case UserManager.update_user(user, params["user"]) do
{:ok, user1} ->
if User.login(user) != User.login(user1) do
case SlugManager.rename_slug(User.login(user), User.login(user1)) do
{:ok, _slug} ->
case GitManager.rename_dir(User.login(user), User.login(user1)) do
:ok -> user1
{:error, err} -> Repo.rollback(err)
end
{:error, changeset} ->
Repo.rollback(changeset)
end
else
user
end
{:error, changeset} -> Repo.rollback(changeset)
end
end) do
{:ok, user} ->
GitAuth.update()
conn
|> redirect(to: Routes.slug_path(conn, :show, User.login(user)))
{:error, changeset} ->
conn
|> assign(:page_title, gettext("Edit user %{login}", login: User.login(user)))
|> assign(:changeset, changeset)
|> assign(:user, user)
|> render("edit.html")
end
else
not_found(conn)
end
end
defp img_src_data(data, type) do
"data:#{type};base64,#{Base.encode64(data)}"
end
def ssh_keys(conn, params) do
user = UserManager.get_user_by_login(params["login"])
if user do
conn
|> put_resp_content_type("text/plain")
|> resp(200, user.ssh_keys)
else
not_found(conn)
end
end
@doc """
Returns a URL that be rendered with a QR code.
It meets the Google Authenticator specification
at https://github.com/google/google-authenticator/wiki/Key-Uri-Format.
## Examples
iex> totp_enrolment_url(user)
"""
def totp_enrolment_url(%User{email: email, totp_secret: secret}) do
"otpauth://totp/kmxgit:#{email}?secret=#{secret}&issuer=kmxgit&algorithm=SHA1&digits=6&period=30"
end
defp totp_enrolment_qrcode_src(user) do
totp_enrolment_url(user)
|> QRCodeEx.encode()
|> QRCodeEx.svg()
|> img_src_data("image/svg+xml")
end
def totp(conn, params) do
current_user = conn.assigns.current_user
if params["login"] == User.login(current_user) do
user = current_user
changeset = UserManager.change_user(user)
|> Ecto.Changeset.put_change(:totp_last, "")
totp_enrolment_qrcode_src = totp_enrolment_qrcode_src(user)
conn
|> assign(:changeset, changeset)
|> assign(:page_title, gettext("Enrol TOTP for user %{login}", login: User.login(user)))
|> assign(:totp_enrolment_qrcode_src, totp_enrolment_qrcode_src)
|> assign(:user, user)
|> render("totp.html")
else
not_found(conn)
end
end
def totp_update(conn, params) do
current_user = conn.assigns.current_user
if params["login"] == User.login(current_user) do
user = current_user
IO.inspect(params)
case UserManager.totp_update(user, params["user"]) do
{:ok, user} ->
conn
|> put_flash(:info, "Enroled 2FA (TOTP) successfuly.")
|> redirect(to: Routes.slug_path(conn, :show, User.login(user)))
{:error, changeset} ->
totp_enrolment_qrcode_src = totp_enrolment_qrcode_src(user)
conn
|> assign(:changeset, changeset)
|> assign(:page_title, gettext("Enrol TOTP for user %{login}", login: User.login(user)))
|> assign(:totp_enrolment_qrcode_src, totp_enrolment_qrcode_src)
|> assign(:user, user)
|> render("totp.html")
end
else
not_found(conn)
end
end
def totp_delete(conn, params) do
current_user = conn.assigns.current_user
if params["login"] == User.login(current_user) do
user = current_user
case UserManager.totp_delete(user) do
{:ok, user} ->
conn
|> put_flash(:info, "Removed 2FA (TOTP) successfuly.")
|> redirect(to: Routes.slug_path(conn, :show, User.login(user)))
{:error, changeset} ->
IO.inspect(changeset)
conn
|> put_flash(:error, "Failed to remove 2FA (TOTP).")
|> redirect(to: Routes.user_path(conn, :edit, User.login(user)))
end
else
not_found(conn)
end
end
def delete(conn, params) do
current_user = conn.assigns.current_user
if params["login"] == User.login(current_user) do
case Repo.transaction(fn ->
case UserManager.delete_user(current_user) do
{:ok, _} ->
case GitManager.delete_dir(User.login(current_user)) do
:ok -> :ok
{:error, out} -> Repo.rollback(status: out)
end
{:error, e} -> Repo.rollback(e)
end
end) do
{:ok, _} ->
GitAuth.update()
conn
|> redirect(to: "/")
{:error, changeset} ->
conn
|> assign(:changeset, changeset)
|> assign(:page_title, gettext("Edit user %{login}", login: User.login(current_user)))
|> render("edit.html")
end
else
not_found(conn)
end
end
end