aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorkaniini <nenolod@gmail.com>2018-12-20 13:28:36 +0000
committerkaniini <nenolod@gmail.com>2018-12-20 13:28:36 +0000
commit2e2030ada8b9aa00936057d47260cd03d01d92be (patch)
tree9fb353e3eaccb1dbd82738cf44ae5015100f6711 /lib
parent8de3138de07c0447ea3bc015ff71db25f8f9c931 (diff)
parent851db74f1ca533f27f72f1341571948b15d2f561 (diff)
downloadpleroma-2e2030ada8b9aa00936057d47260cd03d01d92be.tar.gz
Merge branch '114_email_confirmation' into 'develop'
[#114] Email confirmation See merge request pleroma/pleroma!546
Diffstat (limited to 'lib')
-rw-r--r--lib/mix/tasks/pleroma/user.ex4
-rw-r--r--lib/pleroma/emails/user_email.ex25
-rw-r--r--lib/pleroma/user.ex40
-rw-r--r--lib/pleroma/user/info.ex22
-rw-r--r--lib/pleroma/web/admin_api/admin_api_controller.ex10
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex3
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex1
-rw-r--r--lib/pleroma/web/nodeinfo/nodeinfo_controller.ex1
-rw-r--r--lib/pleroma/web/oauth/oauth_controller.ex16
-rw-r--r--lib/pleroma/web/router.ex9
-rw-r--r--lib/pleroma/web/twitter_api/controllers/util_controller.ex2
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api.ex11
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api_controller.ex39
-rw-r--r--lib/pleroma/web/twitter_api/views/user_view.ex1
14 files changed, 166 insertions, 18 deletions
diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex
index 3d30e3a81..51086a443 100644
--- a/lib/mix/tasks/pleroma/user.ex
+++ b/lib/mix/tasks/pleroma/user.ex
@@ -103,8 +103,8 @@ defmodule Mix.Tasks.Pleroma.User do
bio: bio
}
- user = User.register_changeset(%User{}, params)
- Repo.insert!(user)
+ changeset = User.register_changeset(%User{}, params, confirmed: true)
+ {:ok, _user} = User.register(changeset)
Mix.shell().info("User #{nickname} created")
diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex
index 7e3e9b020..8f916f470 100644
--- a/lib/pleroma/emails/user_email.ex
+++ b/lib/pleroma/emails/user_email.ex
@@ -15,6 +15,7 @@ defmodule Pleroma.UserEmail do
defp recipient(email, nil), do: email
defp recipient(email, name), do: {name, email}
+ defp recipient(%Pleroma.User{} = user), do: recipient(user.email, user.name)
def password_reset_email(user, password_reset_token) when is_binary(password_reset_token) do
password_reset_url =
@@ -32,7 +33,7 @@ defmodule Pleroma.UserEmail do
"""
new()
- |> to(recipient(user.email, user.name))
+ |> to(recipient(user))
|> from(sender())
|> subject("Password reset")
|> html_body(html_body)
@@ -63,4 +64,26 @@ defmodule Pleroma.UserEmail do
|> subject("Invitation to #{instance_name()}")
|> html_body(html_body)
end
+
+ def account_confirmation_email(user) do
+ confirmation_url =
+ Router.Helpers.confirm_email_url(
+ Endpoint,
+ :confirm_email,
+ user.id,
+ to_string(user.info.confirmation_token)
+ )
+
+ html_body = """
+ <h3>Welcome to #{instance_name()}!</h3>
+ <p>Email confirmation is required to activate the account.</p>
+ <p>Click the following link to proceed: <a href="#{confirmation_url}">activate your account</a>.</p>
+ """
+
+ new()
+ |> to(recipient(user))
+ |> from(sender())
+ |> subject("#{instance_name()} account confirmation")
+ |> html_body(html_body)
+ end
end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 3ad1ab87a..f8827abec 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -38,6 +38,13 @@ defmodule Pleroma.User do
timestamps()
end
+ def auth_active?(%User{} = user) do
+ (user.info && !user.info.confirmation_pending) ||
+ !Pleroma.Config.get([:instance, :account_activation_required])
+ end
+
+ def superuser?(%User{} = user), do: user.info && User.Info.superuser?(user.info)
+
def avatar_url(user) do
case user.avatar do
%{"url" => [%{"href" => href} | _]} -> href
@@ -78,6 +85,7 @@ defmodule Pleroma.User do
note_count: user.info.note_count,
follower_count: user.info.follower_count,
locked: user.info.locked,
+ confirmation_pending: user.info.confirmation_pending,
default_scope: user.info.default_scope
}
end
@@ -168,7 +176,16 @@ defmodule Pleroma.User do
update_and_set_cache(password_update_changeset(user, data))
end
- def register_changeset(struct, params \\ %{}) do
+ def register_changeset(struct, params \\ %{}, opts \\ []) do
+ confirmation_status =
+ if opts[:confirmed] || !Pleroma.Config.get([:instance, :account_activation_required]) do
+ :confirmed
+ else
+ :unconfirmed
+ end
+
+ info_change = User.Info.confirmation_changeset(%User.Info{}, confirmation_status)
+
changeset =
struct
|> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation])
@@ -180,7 +197,7 @@ defmodule Pleroma.User do
|> validate_format(:email, @email_regex)
|> validate_length(:bio, max: 1000)
|> validate_length(:name, min: 1, max: 100)
- |> put_change(:info, %Pleroma.User.Info{})
+ |> put_change(:info, info_change)
if changeset.valid? do
hashed = Pbkdf2.hashpwsalt(changeset.changes[:password])
@@ -197,6 +214,25 @@ defmodule Pleroma.User do
end
end
+ @doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
+ def register(%Ecto.Changeset{} = changeset) do
+ with {:ok, user} <- Repo.insert(changeset),
+ {:ok, _} = try_send_confirmation_email(user) do
+ {:ok, user}
+ end
+ end
+
+ def try_send_confirmation_email(%User{} = user) do
+ if user.info.confirmation_pending &&
+ Pleroma.Config.get([:instance, :account_activation_required]) do
+ user
+ |> Pleroma.UserEmail.account_confirmation_email()
+ |> Pleroma.Mailer.deliver()
+ else
+ {:ok, :noop}
+ end
+ end
+
def needs_update?(%User{local: true}), do: false
def needs_update?(%User{local: false, last_refreshed_at: nil}), do: true
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index a3785447c..3de4af56c 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -9,6 +9,8 @@ defmodule Pleroma.User.Info do
field(:note_count, :integer, default: 0)
field(:follower_count, :integer, default: 0)
field(:locked, :boolean, default: false)
+ field(:confirmation_pending, :boolean, default: false)
+ field(:confirmation_token, :string, default: nil)
field(:default_scope, :string, default: "public")
field(:blocks, {:array, :string}, default: [])
field(:domain_blocks, {:array, :string}, default: [])
@@ -35,6 +37,8 @@ defmodule Pleroma.User.Info do
# subject _> Where is this used?
end
+ def superuser?(info), do: info.is_admin || info.is_moderator
+
def set_activation_status(info, deactivated) do
params = %{deactivated: deactivated}
@@ -141,6 +145,24 @@ defmodule Pleroma.User.Info do
])
end
+ def confirmation_changeset(info, :confirmed) do
+ confirmation_changeset(info, %{
+ confirmation_pending: false,
+ confirmation_token: nil
+ })
+ end
+
+ def confirmation_changeset(info, :unconfirmed) do
+ confirmation_changeset(info, %{
+ confirmation_pending: true,
+ confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64()
+ })
+ end
+
+ def confirmation_changeset(info, params) do
+ cast(info, params, [:confirmation_pending, :confirmation_token])
+ end
+
def mastodon_profile_update(info, params) do
info
|> cast(params, [
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index 4d73cf219..683310168 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -1,6 +1,6 @@
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
use Pleroma.Web, :controller
- alias Pleroma.{User, Repo}
+ alias Pleroma.User
alias Pleroma.Web.ActivityPub.Relay
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
@@ -26,7 +26,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
conn,
%{"nickname" => nickname, "email" => email, "password" => password}
) do
- new_user = %{
+ user_data = %{
nickname: nickname,
name: nickname,
email: email,
@@ -35,11 +35,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
bio: "."
}
- User.register_changeset(%User{}, new_user)
- |> Repo.insert!()
+ changeset = User.register_changeset(%User{}, user_data, confirmed: true)
+ {:ok, user} = User.register(changeset)
conn
- |> json(new_user.nickname)
+ |> json(user.nickname)
end
def tag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 665b75437..c6db89442 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -110,7 +110,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def user(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
- with %User{} = user <- Repo.get(User, id) do
+ with %User{} = user <- Repo.get(User, id),
+ true <- User.auth_active?(user) || user.id == for_user.id || User.superuser?(for_user) do
account = AccountView.render("account.json", %{user: user, for: for_user})
json(conn, account)
else
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index ebcf9230b..50df88aca 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -62,6 +62,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
# Pleroma extension
pleroma: %{
+ confirmation_pending: user_info.confirmation_pending,
tags: user.tags
}
}
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
index 44c11f40a..70921605d 100644
--- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
+++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
@@ -132,6 +132,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
banner: Keyword.get(instance, :banner_upload_limit),
background: Keyword.get(instance, :background_upload_limit)
},
+ accountActivationRequired: Keyword.get(instance, :account_activation_required, false),
invitesEnabled: Keyword.get(instance, :invites_enabled, false),
features: features
}
diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex
index 20c2e799b..9a972ee47 100644
--- a/lib/pleroma/web/oauth/oauth_controller.ex
+++ b/lib/pleroma/web/oauth/oauth_controller.ex
@@ -31,6 +31,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
}) do
with %User{} = user <- User.get_by_nickname_or_email(name),
true <- Pbkdf2.checkpw(password, user.password_hash),
+ {:auth_active, true} <- {:auth_active, User.auth_active?(user)},
%App{} = app <- Repo.get_by(App, client_id: client_id),
{:ok, auth} <- Authorization.create_authorization(app, user) do
# Special case: Local MastodonFE.
@@ -63,6 +64,15 @@ defmodule Pleroma.Web.OAuth.OAuthController do
redirect(conn, external: url)
end
+ else
+ {:auth_active, false} ->
+ conn
+ |> put_flash(:error, "Account confirmation pending")
+ |> put_status(:forbidden)
+ |> authorize(params)
+
+ error ->
+ error
end
end
@@ -101,6 +111,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
with %App{} = app <- get_app_from_request(conn, params),
%User{} = user <- User.get_by_nickname_or_email(name),
true <- Pbkdf2.checkpw(password, user.password_hash),
+ {:auth_active, true} <- {:auth_active, User.auth_active?(user)},
{:ok, auth} <- Authorization.create_authorization(app, user),
{:ok, token} <- Token.exchange_token(app, auth) do
response = %{
@@ -113,6 +124,11 @@ defmodule Pleroma.Web.OAuth.OAuthController do
json(conn, response)
else
+ {:auth_active, false} ->
+ conn
+ |> put_status(:forbidden)
+ |> json(%{error: "Account confirmation pending"})
+
_error ->
put_status(conn, 400)
|> json(%{error: "Invalid credentials"})
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index e988f1088..2c62cdf2f 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -283,6 +283,15 @@ defmodule Pleroma.Web.Router do
post("/account/register", TwitterAPI.Controller, :register)
post("/account/password_reset", TwitterAPI.Controller, :password_reset)
+ get(
+ "/account/confirm_email/:user_id/:token",
+ TwitterAPI.Controller,
+ :confirm_email,
+ as: :confirm_email
+ )
+
+ post("/account/resend_confirmation_email", TwitterAPI.Controller, :resend_confirmation_email)
+
get("/search", TwitterAPI.Controller, :search)
get("/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline)
end
diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
index 38653f0b8..3baeba619 100644
--- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
@@ -174,6 +174,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"),
private: if(Keyword.get(instance, :public, true), do: "0", else: "1"),
vapidPublicKey: vapid_public_key,
+ accountActivationRequired:
+ if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"),
invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0")
}
diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex
index 90b8345c5..d816dc3bc 100644
--- a/lib/pleroma/web/twitter_api/twitter_api.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api.ex
@@ -1,8 +1,10 @@
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
alias Pleroma.{UserInviteToken, User, Activity, Repo, Object}
+ alias Pleroma.{UserEmail, Mailer}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.TwitterAPI.UserView
alias Pleroma.Web.CommonAPI
+
import Ecto.Query
def create_status(%User{} = user, %{"status" => _} = data) do
@@ -161,10 +163,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
cond do
registrations_open || (!is_nil(token) && !token.used) ->
- changeset = User.register_changeset(%User{info: %{}}, params)
+ changeset = User.register_changeset(%User{}, params)
- with {:ok, user} <- Repo.insert(changeset) do
+ with {:ok, user} <- User.register(changeset) do
!registrations_open && UserInviteToken.mark_as_used(token.token)
+
{:ok, user}
else
{:error, changeset} ->
@@ -189,8 +192,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
%User{local: true} = user <- User.get_by_nickname_or_email(nickname_or_email),
{:ok, token_record} <- Pleroma.PasswordResetToken.create_token(user) do
user
- |> Pleroma.UserEmail.password_reset_email(token_record.token)
- |> Pleroma.Mailer.deliver()
+ |> UserEmail.password_reset_email(token_record.token)
+ |> Mailer.deliver()
else
false ->
{:error, "bad user identifier"}
diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
index 327620302..c644681b0 100644
--- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
@@ -96,10 +96,15 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
end
def show_user(conn, params) do
- with {:ok, shown} <- TwitterAPI.get_user(params) do
+ for_user = conn.assigns.user
+
+ with {:ok, shown} <- TwitterAPI.get_user(params),
+ true <-
+ User.auth_active?(shown) ||
+ (for_user && (for_user.id == shown.id || User.superuser?(for_user))) do
params =
- if user = conn.assigns.user do
- %{user: shown, for: user}
+ if for_user do
+ %{user: shown, for: for_user}
else
%{user: shown}
end
@@ -110,6 +115,11 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
else
{:error, msg} ->
bad_request_reply(conn, msg)
+
+ false ->
+ conn
+ |> put_status(404)
+ |> json(%{error: "Unconfirmed user"})
end
end
@@ -372,6 +382,29 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
end
end
+ def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
+ with %User{} = user <- Repo.get(User, uid),
+ true <- user.local,
+ true <- user.info.confirmation_pending,
+ true <- user.info.confirmation_token == token,
+ info_change <- User.Info.confirmation_changeset(user.info, :confirmed),
+ changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_change),
+ {:ok, _} <- User.update_and_set_cache(changeset) do
+ conn
+ |> redirect(to: "/")
+ end
+ end
+
+ def resend_confirmation_email(conn, params) do
+ nickname_or_email = params["email"] || params["nickname"]
+
+ with %User{} = user <- User.get_by_nickname_or_email(nickname_or_email),
+ {:ok, _} <- User.try_send_confirmation_email(user) do
+ conn
+ |> json_response(:no_content, "")
+ end
+ end
+
def update_avatar(%{assigns: %{user: user}} = conn, params) do
{:ok, object} = ActivityPub.upload(params, type: :avatar)
change = Changeset.change(user, %{avatar: object.data})
diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex
index 8a88d72b1..45b893eda 100644
--- a/lib/pleroma/web/twitter_api/views/user_view.ex
+++ b/lib/pleroma/web/twitter_api/views/user_view.ex
@@ -81,6 +81,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
# Pleroma extension
"pleroma" => %{
+ "confirmation_pending" => user_info.confirmation_pending,
"tags" => user.tags
}
}