diff --git a/lib/recycledcloud/LDAP.ex b/lib/recycledcloud/LDAP.ex
new file mode 100644
index 0000000..de0a165
--- /dev/null
+++ b/lib/recycledcloud/LDAP.ex
@@ -0,0 +1,81 @@
+defmodule RecycledCloud.LDAP do
+ require Logger
+ require Exldap
+ use GenServer
+
+ # Every request to the LDAP backend go through this GenServer.
+
+ def start_link(_) do
+ GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
+ end
+
+ def init(_) do
+ {:ok, connect()}
+ end
+
+ def handle_call({:pool, query}, _from, state) do
+ case state do
+ %{status: :ok, conn: ldap_conn} ->
+ result = internal_execute_query(query, ldap_conn)
+ if (result == {:error, :ldap_closed}) do
+ {:reply, result, connect()}
+ else
+ {:reply, result, state}
+ end
+ %{status: :error, conn: nil} ->
+ state = connect()
+ if (state.status == :ok) do
+ result = internal_execute_query(query, state.conn)
+ {:reply, result, state}
+ else
+ error = {:error, "Unable to contact backend"}
+ {:reply, error, state}
+ end
+ end
+ end
+
+ def handle_call({:single, query}, _from, state) do
+ single_state = connect()
+ case single_state do
+ %{status: :ok, conn: ldap_conn} ->
+ result = internal_execute_query(query, ldap_conn)
+ close(ldap_conn)
+ {:reply, result, state}
+ %{status: :error, conn: nil} ->
+ error = {:error, "Unable to contact backend"}
+ {:reply, error, state}
+ end
+ end
+
+ def terminate(reason, state) do
+ if (state.status == :ok), do: close(state.conn)
+ IO.inspect(reason)
+ #Logger.info "Terminating LDAP backend: #{reason}."
+ end
+
+ ###
+
+ def execute(query), do: GenServer.call(__MODULE__, {:pool, query})
+ def execute_single(query), do: GenServer.call(__MODULE__, {:single, query})
+
+ ###
+
+ defp internal_execute_query(query, ldap_conn), do: query.(ldap_conn)
+
+ defp connect do
+ case Exldap.connect do
+ {:ok, ldap_conn} ->
+ Logger.info "Successfuly connected to LDAP server."
+ %{status: :ok, conn: ldap_conn}
+ {:error, err} ->
+ Logger.warning "Failed to connect to LDAP server: #{err}. Authentication will not be possible."
+ %{status: :error, conn: nil}
+ end
+ end
+
+ defp close(ldap_conn) do
+ ldap_conn |> Exldap.close
+ Logger.debug("An LDAP connection was closed.")
+ end
+end
+
diff --git a/lib/recycledcloud/accounts.ex b/lib/recycledcloud/accounts.ex
index b4f6dc0..75454c1 100644
--- a/lib/recycledcloud/accounts.ex
+++ b/lib/recycledcloud/accounts.ex
@@ -6,40 +6,41 @@ defmodule RecycledCloud.Accounts do
import Ecto.Query, warn: false
alias RecycledCloud.Repo
alias RecycledCloud.Accounts.{User, UserToken, UserNotifier}
+ alias RecycledCloud.LDAP
## Database getters
@doc """
- Gets a user by email.
+ Gets a user by username.
## Examples
- iex> get_user_by_email("foo@example.com")
+ iex> get_user_by_username("foo")
%User{}
- iex> get_user_by_email("unknown@example.com")
+ iex> get_user_by_username("unknown")
nil
"""
- def get_user_by_email(email) when is_binary(email) do
- Repo.get_by(User, email: email)
+ def get_user_by_username(username) when is_binary(username) do
+ User.get_by_username(username)
end
@doc """
- Gets a user by email and password.
+ Gets a user by username and password.
## Examples
- iex> get_user_by_email_and_password("foo@example.com", "correct_password")
+ iex> get_user_by_username_and_password("foo", "correct_password")
%User{}
- iex> get_user_by_email_and_password("foo@example.com", "invalid_password")
+ iex> get_user_by_username_and_password("foo", "invalid_password")
nil
"""
- def get_user_by_email_and_password(email, password)
- when is_binary(email) and is_binary(password) do
- user = Repo.get_by(User, email: email)
+ def get_user_by_username_and_password(username, password)
+ when is_binary(username) and is_binary(password) do
+ user = get_user_by_username(username)
if User.valid_password?(user, password), do: user
end
@@ -180,7 +181,7 @@ defmodule RecycledCloud.Accounts do
"""
def change_user_password(user, attrs \\ %{}) do
- User.password_changeset(user, attrs, hash_password: false)
+ User.password_changeset(user, attrs)
end
@doc """
@@ -195,19 +196,24 @@ defmodule RecycledCloud.Accounts do
{:error, %Ecto.Changeset{}}
"""
- def update_user_password(user, password, attrs) do
- changeset =
- user
- |> User.password_changeset(attrs)
- |> User.validate_current_password(password)
-
- Ecto.Multi.new()
- |> Ecto.Multi.update(:user, changeset)
- |> Ecto.Multi.delete_all(:tokens, UserToken.user_and_contexts_query(user, :all))
- |> Repo.transaction()
- |> case do
- {:ok, %{user: user}} -> {:ok, user}
- {:error, :user, changeset, _} -> {:error, changeset}
+ def update_user_password(user, current_password, attrs) do
+ changeset = user
+ |> User.password_changeset(attrs)
+ |> User.validate_current_password(current_password)
+ case Ecto.Changeset.apply_action(changeset, :update) do
+ {:ok, _} ->
+ new_password = attrs["password"]
+ case User.set_password(user, new_password) do
+ {:ok, _} -> {:ok, user}
+ {:error, err} ->
+ changeset_errors = [current_password: {"Unknown error.", []}]
+ updated_changeset = changeset
+ |> Map.put(:action, :update)
+ |> Map.put(:errors, changeset_errors)
+ {:error, updated_changeset}
+ end
+ err ->
+ err
end
end
diff --git a/lib/recycledcloud/accounts/user.ex b/lib/recycledcloud/accounts/user.ex
index b8add74..7363745 100644
--- a/lib/recycledcloud/accounts/user.ex
+++ b/lib/recycledcloud/accounts/user.ex
@@ -1,17 +1,63 @@
defmodule RecycledCloud.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
+ require Logger
+ require Exldap
+ alias RecycledCloud.{LDAP,Accounts}
+ alias RecycledCloud.Accounts.{User, UserToken, UserNotifier}
+ alias RecycledCloud.Repo
@derive {Inspect, except: [:password]}
schema "users" do
- field :email, :string
+ field :username, :string
field :password, :string, virtual: true
- field :hashed_password, :string
+ field :email, :string, virtual: true
field :confirmed_at, :naive_datetime
timestamps()
end
+ defp get_dn_for(%User{username: uid}) do
+ # FIXME
+ "uid=#{uid},ou=users,dc=recycled,dc=cloud"
+ end
+
+ def get_by_username(username) when is_binary(username) do
+ local_user = Repo.get_by(User, username: username)
+ if local_user do
+ Map.put(local_user, :email, "unknown@domain.tld")
+ else
+ query = fn ldap_conn -> Exldap.search_field(ldap_conn, :uid, username) end
+ case query |> LDAP.execute do
+ {:ok, []} -> nil
+ {:ok, result} ->
+ {:ok, entry} = result |> Enum.fetch(0)
+
+ Logger.info("Found #{entry.object_name} in directory. Syncing with \
+ local database.")
+
+ attributes = entry |> Map.get(:attributes) |> Enum.into(%{})
+ username = attributes
+ |> Map.get('uid')
+ |> Enum.at(0)
+ |> List.to_string
+
+ email = attributes
+ |> Map.get('email')
+ |> Enum.at(0)
+ |> List.to_string
+
+ case Accounts.register_user(%{username: username, email: email}) do
+ {:ok, user} -> user
+ {:error, _} -> nil
+ end
+ {:error, err} ->
+ Logger.warn("LDAP error: #{err}")
+ nil
+ end
+ end
+ end
+
@doc """
A user changeset for registration.
@@ -31,9 +77,9 @@ defmodule RecycledCloud.Accounts.User do
"""
def registration_changeset(user, attrs, opts \\ []) do
user
- |> cast(attrs, [:email, :password])
+ |> cast(attrs, [:username, :password])
|> validate_email()
- |> validate_password(opts)
+ #|> validate_password(opts)
end
defp validate_email(changeset) do
@@ -41,31 +87,15 @@ defmodule RecycledCloud.Accounts.User do
|> validate_required([:email])
|> validate_format(:email, ~r/^[^\s]+@[^\s]+$/, message: "must have the @ sign and no spaces")
|> validate_length(:email, max: 160)
- |> unsafe_validate_unique(:email, RecycledCloud.Repo)
- |> unique_constraint(:email)
end
- defp validate_password(changeset, opts) do
+ defp validate_password(changeset) do
changeset
|> validate_required([:password])
- |> validate_length(:password, min: 12, max: 80)
- # |> validate_format(:password, ~r/[a-z]/, message: "at least one lower case character")
- # |> validate_format(:password, ~r/[A-Z]/, message: "at least one upper case character")
- # |> validate_format(:password, ~r/[!?@#$%^&*_0-9]/, message: "at least one digit or punctuation character")
- |> maybe_hash_password(opts)
- end
-
- defp maybe_hash_password(changeset, opts) do
- hash_password? = Keyword.get(opts, :hash_password, true)
- password = get_change(changeset, :password)
-
- if hash_password? && password && changeset.valid? do
- changeset
- |> put_change(:hashed_password, Bcrypt.hash_pwd_salt(password))
- |> delete_change(:password)
- else
- changeset
- end
+ |> validate_length(:password, min: 6, max: 80)
+ #|> validate_format(:password, ~r/[a-z]/, message: "at least one lower case character")
+ #|> validate_format(:password, ~r/[A-Z]/, message: "at least one upper case character")
+ #|> validate_format(:password, ~r/[!?@#$%^&*_0-9]/, message: "at least one digit or punctuation character")
end
@doc """
@@ -95,11 +125,11 @@ defmodule RecycledCloud.Accounts.User do
validations on a LiveView form), this option can be set to `false`.
Defaults to `true`.
"""
- def password_changeset(user, attrs, opts \\ []) do
+ def password_changeset(user, attrs) do
user
|> cast(attrs, [:password])
|> validate_confirmation(:password, message: "does not match password")
- |> validate_password(opts)
+ |> validate_password()
end
@doc """
@@ -112,17 +142,21 @@ defmodule RecycledCloud.Accounts.User do
@doc """
Verifies the password.
-
- If there is no user or the user doesn't have a password, we call
- `Bcrypt.no_user_verify/0` to avoid timing attacks.
"""
- def valid_password?(%RecycledCloud.Accounts.User{hashed_password: hashed_password}, password)
- when is_binary(hashed_password) and byte_size(password) > 0 do
- Bcrypt.verify_pass(password, hashed_password)
+ def valid_password?(user = %User{username: uid}, password)
+ when byte_size(password) > 0 do
+ dn = get_dn_for(user)
+ query = fn ldap_conn ->
+ Exldap.verify_credentials(ldap_conn, dn, password)
+ end
+
+ case query |> LDAP.execute_single do
+ :ok -> true
+ {:error, _} -> false
+ end
end
def valid_password?(_, _) do
- Bcrypt.no_user_verify()
false
end
@@ -136,4 +170,21 @@ defmodule RecycledCloud.Accounts.User do
add_error(changeset, :current_password, "is not valid")
end
end
+
+ def set_password(user, new_password) do
+ # Exldap does not properly implement `change_password`, hence we have to
+ # fallback on erlang's `:eldap`.
+ # See http://erlang.org/doc/man/eldap.html#modify-4
+
+ user_dn = get_dn_for(user)
+ query = fn ldap_conn ->
+ :eldap.modify_password(
+ ldap_conn,
+ String.to_charlist(user_dn),
+ String.to_charlist(new_password)
+ )
+ end
+
+ query |> LDAP.execute_single
+ end
end
diff --git a/lib/recycledcloud/application.ex b/lib/recycledcloud/application.ex
index 268713e..2e4b846 100644
--- a/lib/recycledcloud/application.ex
+++ b/lib/recycledcloud/application.ex
@@ -14,7 +14,9 @@ defmodule RecycledCloud.Application do
# Start the PubSub system
{Phoenix.PubSub, name: RecycledCloud.PubSub},
# Start the Endpoint (http/https)
- RecycledCloudWeb.Endpoint
+ RecycledCloudWeb.Endpoint,
+ # Starts LDAP connection pool
+ RecycledCloud.LDAP
# Start a worker by calling: RecycledCloud.Worker.start_link(arg)
# {RecycledCloud.Worker, arg}
]
diff --git a/lib/recycledcloud_web/controllers/user_registration_controller.ex b/lib/recycledcloud_web/controllers/user_registration_controller.ex
index 7ec5af7..3a251ac 100644
--- a/lib/recycledcloud_web/controllers/user_registration_controller.ex
+++ b/lib/recycledcloud_web/controllers/user_registration_controller.ex
@@ -7,24 +7,31 @@ defmodule RecycledCloudWeb.UserRegistrationController do
def new(conn, _params) do
changeset = Accounts.change_user_registration(%User{})
- render(conn, "new.html", changeset: changeset)
+ conn
+ |> put_flash(:error, "Registration is disabled for the time being.")
+ |> render("new.html", changeset: changeset)
end
def create(conn, %{"user" => user_params}) do
- case Accounts.register_user(user_params) do
- {:ok, user} ->
- {:ok, _} =
- Accounts.deliver_user_confirmation_instructions(
- user,
- &Routes.user_confirmation_url(conn, :confirm, &1)
- )
+ changeset = Accounts.change_user_registration(%User{})
- conn
- |> put_flash(:info, "User created successfully.")
- |> UserAuth.log_in_user(user)
+ conn
+ |> redirect(to: Routes.user_registration_path(conn, :new))
- {:error, %Ecto.Changeset{} = changeset} ->
- render(conn, "new.html", changeset: changeset)
- end
+ #case Accounts.register_user(user_params) do
+ # {:ok, user} ->
+ # {:ok, _} =
+ # Accounts.deliver_user_confirmation_instructions(
+ # user,
+ # &Routes.user_confirmation_url(conn, :confirm, &1)
+ # )
+
+ # conn
+ # |> put_flash(:info, "User created successfully.")
+ # |> UserAuth.log_in_user(user)
+
+ # {:error, %Ecto.Changeset{} = changeset} ->
+ # render(conn, "new.html", changeset: changeset)
+ #end
end
end
diff --git a/lib/recycledcloud_web/controllers/user_reset_password_controller.ex b/lib/recycledcloud_web/controllers/user_reset_password_controller.ex
index 155c566..f364e86 100644
--- a/lib/recycledcloud_web/controllers/user_reset_password_controller.ex
+++ b/lib/recycledcloud_web/controllers/user_reset_password_controller.ex
@@ -9,8 +9,8 @@ defmodule RecycledCloudWeb.UserResetPasswordController do
render(conn, "new.html")
end
- def create(conn, %{"user" => %{"email" => email}}) do
- if user = Accounts.get_user_by_email(email) do
+ def create(conn, %{"user" => %{"username" => username}}) do
+ if user = Accounts.get_user_by_username(username) do
Accounts.deliver_user_reset_password_instructions(
user,
&Routes.user_reset_password_url(conn, :edit, &1)
diff --git a/lib/recycledcloud_web/controllers/user_session_controller.ex b/lib/recycledcloud_web/controllers/user_session_controller.ex
index 7e92d06..d91fd24 100644
--- a/lib/recycledcloud_web/controllers/user_session_controller.ex
+++ b/lib/recycledcloud_web/controllers/user_session_controller.ex
@@ -9,12 +9,12 @@ defmodule RecycledCloudWeb.UserSessionController do
end
def create(conn, %{"user" => user_params}) do
- %{"email" => email, "password" => password} = user_params
+ %{"username" => username, "password" => password} = user_params
- if user = Accounts.get_user_by_email_and_password(email, password) do
+ if user = Accounts.get_user_by_username_and_password(username, password) do
UserAuth.log_in_user(conn, user, user_params)
else
- render(conn, "new.html", error_message: "Invalid email or password")
+ render(conn, "new.html", error_message: "Invalid username or password")
end
end
diff --git a/lib/recycledcloud_web/templates/layout/_sidebar_nav.html.eex b/lib/recycledcloud_web/templates/layout/_sidebar_nav.html.eex
index 9c64679..edb3670 100644
--- a/lib/recycledcloud_web/templates/layout/_sidebar_nav.html.eex
+++ b/lib/recycledcloud_web/templates/layout/_sidebar_nav.html.eex
@@ -2,7 +2,7 @@
<%= link "Home", to: Routes.page_path(@conn, :index) %>
<%= if @current_user do %>
- <%= @current_user.email %>
+ Logged in as <%= @current_user.username %>
<%= link "Settings", to: Routes.user_settings_path(@conn, :edit) %>
<%= link "Log out", to: Routes.user_session_path(@conn, :delete), method: :delete %>
<% else %>
diff --git a/lib/recycledcloud_web/templates/layout/app.html.eex b/lib/recycledcloud_web/templates/layout/app.html.eex
index 581298e..854e6fc 100644
--- a/lib/recycledcloud_web/templates/layout/app.html.eex
+++ b/lib/recycledcloud_web/templates/layout/app.html.eex
@@ -12,8 +12,14 @@
<%= render "_sidebar_nav.html", assigns %>
+
">
+
+
+ management v<%= Mix.Project.config[:version] %> | <%= Mix.env %> | sources
+
+
-
+
<%= if get_flash(@conn, :info) do %>
<%= get_flash(@conn, :info) %>
diff --git a/lib/recycledcloud_web/templates/user_registration/new.html.eex b/lib/recycledcloud_web/templates/user_registration/new.html.eex
index 18f36d7..9d0c59d 100644
--- a/lib/recycledcloud_web/templates/user_registration/new.html.eex
+++ b/lib/recycledcloud_web/templates/user_registration/new.html.eex
@@ -7,20 +7,17 @@
<% end %>
- <%= label f, :email %>
- <%= email_input f, :email, required: true %>
- <%= error_tag f, :email %>
+ <%= text_input f, :username, required: true, placeholder: "Username" %>
+ <%= error_tag f, :username %>
- <%= label f, :password %>
- <%= password_input f, :password, required: true %>
+
+
+ <%= password_input f, :password, required: true, placeholder: "Password" %>
<%= error_tag f, :password %>
+
+
<%= submit "Register" %>
<% end %>
-
-
- <%= link "Log in", to: Routes.user_session_path(@conn, :new) %> |
- <%= link "Forgot your password?", to: Routes.user_reset_password_path(@conn, :new) %>
-
diff --git a/lib/recycledcloud_web/templates/user_reset_password/new.html.eex b/lib/recycledcloud_web/templates/user_reset_password/new.html.eex
index 619c535..b4608ff 100644
--- a/lib/recycledcloud_web/templates/user_reset_password/new.html.eex
+++ b/lib/recycledcloud_web/templates/user_reset_password/new.html.eex
@@ -1,8 +1,7 @@
Forgot your password?
<%= form_for :user, Routes.user_reset_password_path(@conn, :create), fn f -> %>
- <%= label f, :email %>
- <%= email_input f, :email, required: true %>
+ <%= text_input f, :username, required: true, placeholder: "Username" %>
<%= submit "Send instructions to reset password" %>
diff --git a/lib/recycledcloud_web/templates/user_session/new.html.eex b/lib/recycledcloud_web/templates/user_session/new.html.eex
index 5f48ef7..9291faa 100644
--- a/lib/recycledcloud_web/templates/user_session/new.html.eex
+++ b/lib/recycledcloud_web/templates/user_session/new.html.eex
@@ -11,7 +11,7 @@ if you do not already have one.
<% end %>
- <%= email_input f, :email, required: true, placeholder: "Email" %>
+ <%= text_input f, :username, required: true, placeholder: "Username" %>
<%= password_input f, :password, required: true, placeholder: "Password" %>
<%= label f, :remember_me, "Keep me logged in for 60 days" %>
<%= checkbox f, :remember_me %>
diff --git a/lib/recycledcloud_web/templates/user_settings/edit.html.eex b/lib/recycledcloud_web/templates/user_settings/edit.html.eex
index 6e8abf9..b1f1d90 100644
--- a/lib/recycledcloud_web/templates/user_settings/edit.html.eex
+++ b/lib/recycledcloud_web/templates/user_settings/edit.html.eex
@@ -1,4 +1,9 @@
-
Settings
+
Account settings
+
+
+You are currently logged in as <%= @current_user.username %>, using <%
+@current_user.email %> as primary contact method.
+
Change email
@@ -11,12 +16,12 @@
<%= hidden_input f, :action, name: "action", value: "update_email" %>
- <%= label f, :email %>
- <%= email_input f, :email, required: true %>
+ <%= email_input f, :email, required: true, placeholder: "Email" %>
<%= error_tag f, :email %>
- <%= label f, :current_password, for: "current_password_for_email" %>
- <%= password_input f, :current_password, required: true, name: "current_password", id: "current_password_for_email" %>
+
+
+ <%= password_input f, :current_password, required: true, name: "current_password", id: "current_password_for_email", placeholder: "Current password" %>
<%= error_tag f, :current_password %>
@@ -35,16 +40,17 @@
<%= hidden_input f, :action, name: "action", value: "update_password" %>
- <%= label f, :password, "New password" %>
- <%= password_input f, :password, required: true %>
+ <%= password_input f, :password, required: true, placeholder: "New password" %>
<%= error_tag f, :password %>
- <%= label f, :password_confirmation, "Confirm new password" %>
- <%= password_input f, :password_confirmation, required: true %>
+
+
+ <%= password_input f, :password_confirmation, required: true, placeholder: "New password confirmation" %>
<%= error_tag f, :password_confirmation %>
- <%= label f, :current_password, for: "current_password_for_password" %>
- <%= password_input f, :current_password, required: true, name: "current_password", id: "current_password_for_password" %>
+
+
+ <%= password_input f, :current_password, required: true, name: "current_password", id: "current_password_for_password", placeholder: "Current password" %>
<%= error_tag f, :current_password %>
diff --git a/mix.exs b/mix.exs
index 73f67c5..edfdacc 100644
--- a/mix.exs
+++ b/mix.exs
@@ -46,6 +46,7 @@ defmodule RecycledCloud.MixProject do
{:jason, "~> 1.0"},
{:plug_cowboy, "~> 2.0"},
{:phx_gen_auth, "~> 0.6", only: [:dev], runtime: false},
+ {:exldap, git: "https://github.com/Fnux/exldap.git"}
]
end
diff --git a/mix.lock b/mix.lock
index 69b3551..90555a7 100644
--- a/mix.lock
+++ b/mix.lock
@@ -10,6 +10,7 @@
"ecto": {:hex, :ecto, "3.5.5", "48219a991bb86daba6e38a1e64f8cea540cded58950ff38fbc8163e062281a07", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "98dd0e5e1de7f45beca6130d13116eae675db59adfa055fb79612406acf6f6f1"},
"ecto_sql": {:hex, :ecto_sql, "3.5.3", "1964df0305538364b97cc4661a2bd2b6c89d803e66e5655e4e55ff1571943efd", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.5.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d2f53592432ce17d3978feb8f43e8dc0705e288b0890caf06d449785f018061c"},
"elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
+ "exldap": {:git, "https://github.com/Fnux/exldap.git", "fd4b5c9928d2e6c4d4ba0fef95f9456ec8b088b9", []},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
diff --git a/priv/repo/migrations/20201215132402_create_users_auth_tables.exs b/priv/repo/migrations/20201215132402_create_users_auth_tables.exs
index ead58f9..eaa639f 100644
--- a/priv/repo/migrations/20201215132402_create_users_auth_tables.exs
+++ b/priv/repo/migrations/20201215132402_create_users_auth_tables.exs
@@ -5,13 +5,12 @@ defmodule RecycledCloud.Repo.Migrations.CreateUsersAuthTables do
execute "CREATE EXTENSION IF NOT EXISTS citext", ""
create table(:users) do
- add :email, :citext, null: false
- add :hashed_password, :string, null: false
+ add :username, :citext, null: false
add :confirmed_at, :naive_datetime
timestamps()
end
- create unique_index(:users, [:email])
+ create unique_index(:users, [:username])
create table(:users_tokens) do
add :user_id, references(:users, on_delete: :delete_all), null: false