Packages

This is a CMS written in Elixir. Aims to be the great open-source ecommerce and/or startup solution for those who are searching for a performance and stability on top of modern technologies like React and Elixir.

Current section

Files

Jump to
lyn web auth user_from_auth.ex
Raw

web/auth/user_from_auth.ex

defmodule Lyn.UserFromAuth do
alias Lyn.User
alias Lyn.Authorization
alias Ueberauth.Auth
def get_or_insert(auth, current_user, repo) do
case auth_and_validate(auth, repo) do
{:error, :not_found} -> register_user_from_auth(auth, current_user, repo)
{:error, reason} -> {:error, reason}
authorization ->
if authorization.expires_at && authorization.expires_at < Guardian.Utils.timestamp do
replace_authorization(authorization, auth, current_user, repo)
else
user_from_authorization(authorization, current_user, repo)
end
end
end
# We need to check the pw for the identity provider
defp validate_auth_for_registration(%Auth{provider: :identity} = auth) do
pw = Map.get(auth.credentials.other, :password)
pwc = Map.get(auth.credentials.other, :password_confirmation)
if pw == nil or pw == "" or pw == pwc, do: :ok, else: {:error, :password_confirmation_does_not_match}
end
# All the other providers are oauth so should be good
defp validate_auth_for_registration(_auth), do: :ok
defp register_user_from_auth(auth, current_user, repo) do
case validate_auth_for_registration(auth) do
:ok ->
case repo.transaction(fn -> create_user_from_auth(auth, current_user, repo) end) do
{:ok, response} -> response
{:error, reason} -> {:error, reason}
end
{:error, reason} -> {:error, reason}
end
end
defp replace_authorization(authorization, auth, current_user, repo) do
case validate_auth_for_registration(auth) do
:ok ->
case user_from_authorization(authorization, current_user, repo) do
{:ok, user} ->
case repo.transaction(fn ->
repo.delete(authorization)
authorization_from_auth(user, auth, repo)
user
end) do
{:ok, user} -> {:ok, user}
{:error, reason} -> {:error, reason}
end
{:error, reason} -> {:error, reason}
end
{:error, reason} -> {:error, reason}
end
end
defp user_from_authorization(authorization, current_user, repo) do
case repo.one(Ecto.Model.assoc(authorization, :user)) do
nil -> {:error, :user_not_found}
user ->
if current_user && current_user.id != user.id do
{:error, :user_does_not_match}
else
{:ok, user}
end
end
end
defp create_user_from_auth(auth, current_user, repo) do
user = current_user
if !user, do: user = repo.get_by(User, email: auth.info.email)
if !user, do: user = create_user(auth, repo)
authorization_from_auth(user, auth, repo)
{:ok, user}
end
defp create_user(auth, repo) do
name = name_from_auth(auth)
result = User.registration_changeset(%User{}, scrub(%{email: auth.info.email, name: name}))
|> repo.insert
case result do
{:ok, user} -> user
{:error, reason} -> repo.rollback(reason)
end
end
defp auth_and_validate(%{provider: :identity} = auth, repo) do
case repo.get_by(Authorization, uid: uid_from_auth(auth), provider: to_string(auth.provider)) do
nil -> {:error, :not_found}
authorization ->
case auth.credentials.other.password do
pass when is_binary(pass) ->
if Comeonin.Bcrypt.checkpw(auth.credentials.other.password, authorization.token) do
authorization
else
{:error, :password_does_not_match}
end
_ -> {:error, :password_required}
end
end
end
defp auth_and_validate(auth, repo) do
case repo.get_by(Authorization, uid: uid_from_auth(auth), provider: to_string(auth.provider)) do
nil -> {:error, :not_found}
authorization ->
if authorization.token == auth.credentials.token do
authorization
else
{:error, :token_mismatch}
end
end
end
defp authorization_from_auth(user, auth, repo) do
authorization = Ecto.Model.build(user, :authorizations)
result = Authorization.changeset(
authorization,
scrub(
%{
provider: to_string(auth.provider),
uid: uid_from_auth(auth),
token: token_from_auth(auth),
refresh_token: auth.credentials.refresh_token,
expires_at: auth.credentials.expires_at,
password: password_from_auth(auth),
password_confirmation: password_confirmation_from_auth(auth)
}
)
) |> repo.insert
case result do
{:ok, the_auth} -> the_auth
{:error, reason} -> repo.rollback(reason)
end
end
defp name_from_auth(auth) do
if auth.info.name do
auth.info.name
else
[auth.info.first_name, auth.info.last_name]
|> Enum.filter(&(&1 != nil and String.strip(&1) != ""))
|> Enum.join(" ")
end
end
defp token_from_auth(%{provider: :identity} = auth) do
case auth do
%{ credentials: %{ other: %{ password: pass } } } when not is_nil(pass) ->
Comeonin.Bcrypt.hashpwsalt(pass)
_ -> nil
end
end
defp token_from_auth(auth), do: auth.credentials.token
defp uid_from_auth(%{ provider: :slack } = auth), do: auth.credentials.other.user_id
defp uid_from_auth(auth), do: auth.uid
defp password_from_auth(%{provider: :identity} = auth), do: auth.credentials.other.password
defp password_from_auth(_), do: nil
defp password_confirmation_from_auth(%{provider: :identity} = auth) do
auth.credentials.other.password_confirmation
end
defp password_confirmation_from_auth(_), do: nil
# We don't have any nested structures in our params that we are using scrub with so this is a very simple scrub
defp scrub(params) do
result = Enum.filter(params, fn
{_key, val} when is_binary(val) -> String.strip(val) != ""
{_key, val} when is_nil(val) -> false
_ -> true
end)
|> Enum.into(%{})
result
end
end