[#923] Support for multiple (external) registrations per user via Registration.
This commit is contained in:
parent
2a96283efb
commit
26b6354095
|
@ -381,7 +381,7 @@
|
||||||
base: System.get_env("LDAP_BASE") || "dc=example,dc=com",
|
base: System.get_env("LDAP_BASE") || "dc=example,dc=com",
|
||||||
uid: System.get_env("LDAP_UID") || "cn"
|
uid: System.get_env("LDAP_UID") || "cn"
|
||||||
|
|
||||||
config :pleroma, :auth, oauth_consumer_enabled: false
|
config :pleroma, :auth, oauth_consumer_enabled: System.get_env("OAUTH_CONSUMER_ENABLED") == "true"
|
||||||
|
|
||||||
config :ueberauth,
|
config :ueberauth,
|
||||||
Ueberauth,
|
Ueberauth,
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Registration do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
alias Pleroma.Registration
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
schema "registrations" do
|
||||||
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
|
field(:provider, :string)
|
||||||
|
field(:uid, :string)
|
||||||
|
field(:info, :map, default: %{})
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
def changeset(registration, params \\ %{}) do
|
||||||
|
registration
|
||||||
|
|> cast(params, [:user_id, :provider, :uid, :info])
|
||||||
|
|> foreign_key_constraint(:user_id)
|
||||||
|
|> unique_constraint(:uid, name: :registrations_provider_uid_index)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_by_provider_uid(provider, uid) do
|
||||||
|
Repo.get_by(Registration,
|
||||||
|
provider: to_string(provider),
|
||||||
|
uid: to_string(uid)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -13,6 +13,7 @@ defmodule Pleroma.User do
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Registration
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
|
@ -41,8 +42,6 @@ defmodule Pleroma.User do
|
||||||
field(:email, :string)
|
field(:email, :string)
|
||||||
field(:name, :string)
|
field(:name, :string)
|
||||||
field(:nickname, :string)
|
field(:nickname, :string)
|
||||||
field(:auth_provider, :string)
|
|
||||||
field(:auth_provider_uid, :string)
|
|
||||||
field(:password_hash, :string)
|
field(:password_hash, :string)
|
||||||
field(:password, :string, virtual: true)
|
field(:password, :string, virtual: true)
|
||||||
field(:password_confirmation, :string, virtual: true)
|
field(:password_confirmation, :string, virtual: true)
|
||||||
|
@ -56,6 +55,7 @@ defmodule Pleroma.User do
|
||||||
field(:bookmarks, {:array, :string}, default: [])
|
field(:bookmarks, {:array, :string}, default: [])
|
||||||
field(:last_refreshed_at, :naive_datetime)
|
field(:last_refreshed_at, :naive_datetime)
|
||||||
has_many(:notifications, Notification)
|
has_many(:notifications, Notification)
|
||||||
|
has_many(:registrations, Registration)
|
||||||
embeds_one(:info, Pleroma.User.Info)
|
embeds_one(:info, Pleroma.User.Info)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
|
@ -210,13 +210,12 @@ def reset_password(user, data) do
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: FIXME (WIP):
|
# TODO: FIXME (WIP):
|
||||||
def oauth_register_changeset(struct, params \\ %{}) do
|
def external_registration_changeset(struct, params \\ %{}) do
|
||||||
info_change = User.Info.confirmation_changeset(%User.Info{}, :confirmed)
|
info_change = User.Info.confirmation_changeset(%User.Info{}, :confirmed)
|
||||||
|
|
||||||
changeset =
|
changeset =
|
||||||
struct
|
struct
|
||||||
|> cast(params, [:email, :nickname, :name, :bio, :auth_provider, :auth_provider_uid])
|
|> cast(params, [:email, :nickname, :name, :bio])
|
||||||
|> validate_required([:auth_provider, :auth_provider_uid])
|
|
||||||
|> unique_constraint(:email)
|
|> unique_constraint(:email)
|
||||||
|> unique_constraint(:nickname)
|
|> unique_constraint(:nickname)
|
||||||
|> validate_exclusion(:nickname, Pleroma.Config.get([Pleroma.User, :restricted_nicknames]))
|
|> validate_exclusion(:nickname, Pleroma.Config.get([Pleroma.User, :restricted_nicknames]))
|
||||||
|
@ -544,13 +543,6 @@ def get_by_nickname_or_email(nickname_or_email) do
|
||||||
get_by_nickname(nickname_or_email) || get_by_email(nickname_or_email)
|
get_by_nickname(nickname_or_email) || get_by_email(nickname_or_email)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_by_auth_provider_uid(auth_provider, auth_provider_uid),
|
|
||||||
do:
|
|
||||||
Repo.get_by(User,
|
|
||||||
auth_provider: to_string(auth_provider),
|
|
||||||
auth_provider_uid: to_string(auth_provider_uid)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_cached_user_info(user) do
|
def get_cached_user_info(user) do
|
||||||
key = "user_info:#{user.id}"
|
key = "user_info:#{user.id}"
|
||||||
Cachex.fetch!(:user_cache, key, fn _ -> user_info(user) end)
|
Cachex.fetch!(:user_cache, key, fn _ -> user_info(user) end)
|
||||||
|
|
|
@ -15,10 +15,10 @@ def implementation do
|
||||||
@callback get_user(Plug.Conn.t(), Map.t()) :: {:ok, User.t()} | {:error, any()}
|
@callback get_user(Plug.Conn.t(), Map.t()) :: {:ok, User.t()} | {:error, any()}
|
||||||
def get_user(plug, params), do: implementation().get_user(plug, params)
|
def get_user(plug, params), do: implementation().get_user(plug, params)
|
||||||
|
|
||||||
@callback get_or_create_user_by_oauth(Plug.Conn.t(), Map.t()) ::
|
@callback get_by_external_registration(Plug.Conn.t(), Map.t()) ::
|
||||||
{:ok, User.t()} | {:error, any()}
|
{:ok, User.t()} | {:error, any()}
|
||||||
def get_or_create_user_by_oauth(plug, params),
|
def get_by_external_registration(plug, params),
|
||||||
do: implementation().get_or_create_user_by_oauth(plug, params)
|
do: implementation().get_by_external_registration(plug, params)
|
||||||
|
|
||||||
@callback handle_error(Plug.Conn.t(), any()) :: any()
|
@callback handle_error(Plug.Conn.t(), any()) :: any()
|
||||||
def handle_error(plug, error), do: implementation().handle_error(plug, error)
|
def handle_error(plug, error), do: implementation().handle_error(plug, error)
|
||||||
|
|
|
@ -40,7 +40,7 @@ def get_user(%Plug.Conn{} = conn, params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_or_create_user_by_oauth(conn, params), do: get_user(conn, params)
|
def get_by_external_registration(conn, params), do: get_user(conn, params)
|
||||||
|
|
||||||
def handle_error(%Plug.Conn{} = _conn, error) do
|
def handle_error(%Plug.Conn{} = _conn, error) do
|
||||||
error
|
error
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Registration
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
@behaviour Pleroma.Web.Auth.Authenticator
|
@behaviour Pleroma.Web.Auth.Authenticator
|
||||||
|
|
||||||
|
@ -27,20 +29,21 @@ def get_user(%Plug.Conn{} = _conn, params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_or_create_user_by_oauth(
|
def get_by_external_registration(
|
||||||
%Plug.Conn{assigns: %{ueberauth_auth: %{provider: provider, uid: uid} = auth}},
|
%Plug.Conn{assigns: %{ueberauth_auth: %{provider: provider, uid: uid} = auth}},
|
||||||
_params
|
_params
|
||||||
) do
|
) do
|
||||||
user = User.get_by_auth_provider_uid(provider, uid)
|
registration = Registration.get_by_provider_uid(provider, uid)
|
||||||
|
|
||||||
if user do
|
if registration do
|
||||||
|
user = Repo.preload(registration, :user).user
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
else
|
else
|
||||||
info = auth.info
|
info = auth.info
|
||||||
email = info.email
|
email = info.email
|
||||||
nickname = info.nickname
|
nickname = info.nickname
|
||||||
|
|
||||||
# TODO: FIXME: connect to existing (non-oauth) account (need a UI flow for that) / generate a random nickname?
|
# Note: nullifying email in case this email is already taken
|
||||||
email =
|
email =
|
||||||
if email && User.get_by_email(email) do
|
if email && User.get_by_email(email) do
|
||||||
nil
|
nil
|
||||||
|
@ -48,31 +51,39 @@ def get_or_create_user_by_oauth(
|
||||||
email
|
email
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Note: generating a random numeric suffix to nickname in case this nickname is already taken
|
||||||
nickname =
|
nickname =
|
||||||
if nickname && User.get_by_nickname(nickname) do
|
if nickname && User.get_by_nickname(nickname) do
|
||||||
nil
|
"#{nickname}_#{:os.system_time()}"
|
||||||
else
|
else
|
||||||
nickname
|
nickname
|
||||||
end
|
end
|
||||||
|
|
||||||
new_user =
|
with {:ok, new_user} <-
|
||||||
User.oauth_register_changeset(
|
User.external_registration_changeset(
|
||||||
%User{},
|
%User{},
|
||||||
%{
|
%{
|
||||||
auth_provider: to_string(provider),
|
|
||||||
auth_provider_uid: to_string(uid),
|
|
||||||
name: info.name,
|
name: info.name,
|
||||||
bio: info.description,
|
bio: info.description,
|
||||||
email: email,
|
email: email,
|
||||||
nickname: nickname
|
nickname: nickname
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|> Repo.insert(),
|
||||||
Pleroma.Repo.insert(new_user)
|
{:ok, _} <-
|
||||||
|
Registration.changeset(%Registration{}, %{
|
||||||
|
user_id: new_user.id,
|
||||||
|
provider: to_string(provider),
|
||||||
|
uid: to_string(uid),
|
||||||
|
info: %{nickname: info.nickname, email: info.email}
|
||||||
|
})
|
||||||
|
|> Repo.insert() do
|
||||||
|
{:ok, new_user}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_or_create_user_by_oauth(%Plug.Conn{} = _conn, _params),
|
def get_by_external_registration(%Plug.Conn{} = _conn, _params),
|
||||||
do: {:error, :missing_credentials}
|
do: {:error, :missing_credentials}
|
||||||
|
|
||||||
def handle_error(%Plug.Conn{} = _conn, error) do
|
def handle_error(%Plug.Conn{} = _conn, error) do
|
||||||
|
|
|
@ -47,7 +47,7 @@ def callback(
|
||||||
conn,
|
conn,
|
||||||
%{"client_id" => client_id, "redirect_uri" => redirect_uri} = params
|
%{"client_id" => client_id, "redirect_uri" => redirect_uri} = params
|
||||||
) do
|
) do
|
||||||
with {:ok, user} <- Authenticator.get_or_create_user_by_oauth(conn, params) do
|
with {:ok, user} <- Authenticator.get_by_external_registration(conn, params) do
|
||||||
do_create_authorization(
|
do_create_authorization(
|
||||||
conn,
|
conn,
|
||||||
%{
|
%{
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
defmodule Pleroma.Repo.Migrations.AddAuthProviderAndAuthProviderUidToUsers do
|
|
||||||
use Ecto.Migration
|
|
||||||
|
|
||||||
def change do
|
|
||||||
alter table(:users) do
|
|
||||||
add :auth_provider, :string
|
|
||||||
add :auth_provider_uid, :string
|
|
||||||
end
|
|
||||||
|
|
||||||
create unique_index(:users, [:auth_provider, :auth_provider_uid])
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.CreateRegistrations do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create table(:registrations) do
|
||||||
|
add :user_id, references(:users, type: :uuid, on_delete: :delete_all)
|
||||||
|
add :provider, :string
|
||||||
|
add :uid, :string
|
||||||
|
add :info, :map, default: %{}
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
create unique_index(:registrations, [:provider, :uid])
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue