Introduce support for 3rd-party auth (OAuth2 & LDAP)

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2020-06-27 19:12:45 +02:00
parent 59a538feba
commit 9a080c1f10
48 changed files with 1380 additions and 240 deletions

View File

@@ -40,14 +40,18 @@ defmodule Mobilizon.Users.User do
:confirmation_token,
:reset_password_sent_at,
:reset_password_token,
:default_actor_id,
:locale,
:unconfirmed_email,
:disabled
:disabled,
:provider
]
@attrs @required_attrs ++ @optional_attrs
@registration_required_attrs @required_attrs ++ [:password]
@auth_provider_required_attrs @required_attrs ++ [:provider]
@password_change_required_attrs [:password]
@password_reset_required_attrs @password_change_required_attrs ++
[:reset_password_token, :reset_password_sent_at]
@@ -67,6 +71,7 @@ defmodule Mobilizon.Users.User do
field(:unconfirmed_email, :string)
field(:locale, :string, default: "en")
field(:disabled, :boolean, default: false)
field(:provider, :string)
belongs_to(:default_actor, Actor)
has_many(:actors, Actor)
@@ -116,6 +121,16 @@ defmodule Mobilizon.Users.User do
)
end
@doc false
@spec auth_provider_changeset(t, map) :: Ecto.Changeset.t()
def auth_provider_changeset(%__MODULE__{} = user, attrs) do
user
|> changeset(attrs)
|> cast_assoc(:default_actor)
|> put_change(:confirmed_at, DateTime.utc_now() |> DateTime.truncate(:second))
|> validate_required(@auth_provider_required_attrs)
end
@doc false
@spec send_password_reset_changeset(t, map) :: Ecto.Changeset.t()
def send_password_reset_changeset(%__MODULE__{} = user, attrs) do

View File

@@ -15,13 +15,6 @@ defmodule Mobilizon.Users do
alias Mobilizon.Storage.{Page, Repo}
alias Mobilizon.Users.{Setting, User}
alias Mobilizon.Web.Auth
@type tokens :: %{
required(:access_token) => String.t(),
required(:refresh_token) => String.t()
}
defenum(UserRole, :user_role, [:administrator, :moderator, :user])
defenum(NotificationPendingNotificationDelay, none: 0, direct: 1, one_hour: 5, one_day: 10)
@@ -41,6 +34,18 @@ defmodule Mobilizon.Users do
end
end
@spec create_external(String.t(), String.t()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
def create_external(email, provider) do
with {:ok, %User{} = user} <-
%User{}
|> User.auth_provider_changeset(%{email: email, provider: provider})
|> Repo.insert() do
Events.create_feed_token(%{user_id: user.id})
{:ok, user}
end
end
@doc """
Gets a single user.
Raises `Ecto.NoResultsError` if the user does not exist.
@@ -75,6 +80,16 @@ defmodule Mobilizon.Users do
end
end
@doc """
Gets an user by its email.
"""
@spec get_user_by_email!(String.t(), boolean | nil) :: User.t()
def get_user_by_email!(email, activated \\ nil) do
email
|> user_by_email_query(activated)
|> Repo.one!()
end
@doc """
Get an user by its activation token.
"""
@@ -267,52 +282,6 @@ defmodule Mobilizon.Users do
@spec count_users :: integer
def count_users, do: Repo.one(from(u in User, select: count(u.id)))
@doc """
Authenticate an user.
"""
@spec authenticate(User.t()) :: {:ok, tokens} | {:error, :unauthorized}
def authenticate(%{user: %User{password_hash: password_hash} = user, password: password}) do
# Does password match the one stored in the database?
if Argon2.verify_pass(password, password_hash) do
{:ok, _tokens} = generate_tokens(user)
else
{:error, :unauthorized}
end
end
@doc """
Generates access token and refresh token for an user.
"""
@spec generate_tokens(User.t()) :: {:ok, tokens}
def generate_tokens(user) do
with {:ok, access_token} <- generate_access_token(user),
{:ok, refresh_token} <- generate_refresh_token(user) do
{:ok, %{access_token: access_token, refresh_token: refresh_token}}
end
end
@doc """
Generates access token for an user.
"""
@spec generate_access_token(User.t()) :: {:ok, String.t()}
def generate_access_token(user) do
with {:ok, access_token, _claims} <-
Auth.Guardian.encode_and_sign(user, %{}, token_type: "access") do
{:ok, access_token}
end
end
@doc """
Generates refresh token for an user.
"""
@spec generate_refresh_token(User.t()) :: {:ok, String.t()}
def generate_refresh_token(user) do
with {:ok, refresh_token, _claims} <-
Auth.Guardian.encode_and_sign(user, %{}, token_type: "refresh") do
{:ok, refresh_token}
end
end
@doc """
Gets a settings for an user.