Edit

kmx.io/kmxgit/lib/kmxgit_web/controllers/user_controller.ex

Branch :

  • lib/kmxgit_web/controllers/user_controller.ex
  • ## 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)
        else
          not_found(conn)
        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) ||
           current_user.is_admin 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) ||
           current_user.is_admin 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) ||
           current_user.is_admin 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