Introduce support for 3rd-party auth (OAuth2 & LDAP)
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
@@ -13,6 +13,7 @@ defmodule Mobilizon.Actors do
|
||||
alias Mobilizon.Media.File
|
||||
alias Mobilizon.Service.Workers
|
||||
alias Mobilizon.Storage.{Page, Repo}
|
||||
alias Mobilizon.Users
|
||||
|
||||
alias Mobilizon.Federation.ActivityPub
|
||||
|
||||
@@ -189,14 +190,19 @@ defmodule Mobilizon.Actors do
|
||||
Creates a new person actor.
|
||||
"""
|
||||
@spec new_person(map) :: {:ok, Actor.t()} | {:error, Ecto.Changeset.t()}
|
||||
def new_person(args) do
|
||||
def new_person(args, default_actor \\ false) do
|
||||
args = Map.put(args, :keys, Crypto.generate_rsa_2048_private_key())
|
||||
|
||||
with {:ok, %Actor{} = person} <-
|
||||
with {:ok, %Actor{id: person_id} = person} <-
|
||||
%Actor{}
|
||||
|> Actor.registration_changeset(args)
|
||||
|> Repo.insert() do
|
||||
Events.create_feed_token(%{user_id: args["user_id"], actor_id: person.id})
|
||||
Events.create_feed_token(%{user_id: args.user_id, actor_id: person.id})
|
||||
|
||||
if default_actor do
|
||||
user = Users.get_user!(args.user_id)
|
||||
Users.update_user(user, %{default_actor_id: person_id})
|
||||
end
|
||||
|
||||
{:ok, person}
|
||||
end
|
||||
|
||||
@@ -186,6 +186,24 @@ defmodule Mobilizon.Config do
|
||||
def anonymous_reporting?,
|
||||
do: Application.get_env(:mobilizon, :anonymous)[:reports][:allowed]
|
||||
|
||||
@spec oauth_consumer_strategies() :: list({atom(), String.t()})
|
||||
def oauth_consumer_strategies do
|
||||
[:auth, :oauth_consumer_strategies]
|
||||
|> get([])
|
||||
|> Enum.map(fn strategy ->
|
||||
case strategy do
|
||||
{id, label} when is_atom(id) -> %{id: id, label: label}
|
||||
id when is_atom(id) -> %{id: id, label: nil}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
@spec oauth_consumer_enabled? :: boolean()
|
||||
def oauth_consumer_enabled?, do: oauth_consumer_strategies() != []
|
||||
|
||||
@spec ldap_enabled? :: boolean()
|
||||
def ldap_enabled?, do: get([:ldap, :enabled], false)
|
||||
|
||||
def instance_resource_providers do
|
||||
types = get_in(Application.get_env(:mobilizon, Mobilizon.Service.ResourceProviders), [:types])
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user