X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fweb%2Fmastodon_api%2Fcontrollers%2Faccount_controller.ex;h=d4532258c4ea37e68cab2d5c2fd0e55be7f29508;hb=dd2b3a8da92e0b823ac338657588339fbf2e9c86;hp=75512442de1c352cda4aeb60fc075566aa8cfb6c;hpb=283fb1e05bcd34294ea7bdedab62b0cc6f88d742;p=akkoma diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 75512442d..784fdc975 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -14,17 +14,21 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do json_response: 3 ] - alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.RateLimiter + alias Pleroma.Maps alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.Pipeline alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.ListView alias Pleroma.Web.MastodonAPI.MastodonAPI alias Pleroma.Web.MastodonAPI.MastodonAPIController alias Pleroma.Web.MastodonAPI.StatusView - alias Pleroma.Web.OAuth.Token + alias Pleroma.Web.OAuth.OAuthController + alias Pleroma.Web.OAuth.OAuthView + alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.RateLimiter alias Pleroma.Web.TwitterAPI.TwitterAPI plug(Pleroma.Web.ApiSpec.CastAndValidate) @@ -81,7 +85,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug( RateLimiter, - [name: :relation_id_action, params: ["id", "uri"]] when action in @relationship_actions + [name: :relation_id_action, params: [:id, :uri]] when action in @relationship_actions ) plug(RateLimiter, [name: :relations_actions] when action in @relationship_actions) @@ -96,16 +100,34 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do def create(%{assigns: %{app: app}, body_params: params} = conn, _params) do with :ok <- validate_email_param(params), :ok <- TwitterAPI.validate_captcha(app, params), - {:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true), - {:ok, token} <- Token.create_token(app, user, %{scopes: app.scopes}) do - json(conn, %{ - token_type: "Bearer", - access_token: token.token, - scope: app.scopes, - created_at: Token.Utils.format_created_at(token) - }) + {:ok, user} <- TwitterAPI.register_user(params), + {_, {:ok, token}} <- + {:login, OAuthController.login(user, app, app.scopes)} do + json(conn, OAuthView.render("token.json", %{user: user, token: token})) else - {:error, error} -> json_response(conn, :bad_request, %{error: error}) + {:login, {:account_status, :confirmation_pending}} -> + json_response(conn, :ok, %{ + message: "You have been registered. Please check your email for further instructions.", + identifier: "missing_confirmed_email" + }) + + {:login, {:account_status, :approval_pending}} -> + json_response(conn, :ok, %{ + message: + "You have been registered. You'll be able to log in once your account is approved.", + identifier: "awaiting_approval" + }) + + {:login, _} -> + json_response(conn, :ok, %{ + message: + "You have been registered. Some post-registration steps may be pending. " <> + "Please log in manually.", + identifier: "manual_login_required" + }) + + {:error, error} -> + json_response(conn, :bad_request, %{error: error}) end end @@ -139,18 +161,22 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "PATCH /api/v1/accounts/update_credentials" - def update_credentials(%{assigns: %{user: original_user}, body_params: params} = conn, _params) do - user = original_user - + def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _params) do params = params |> Enum.filter(fn {_, value} -> not is_nil(value) end) |> Enum.into(%{}) + # We use an empty string as a special value to reset + # avatars, banners, backgrounds + user_image_value = fn + "" -> {:ok, nil} + value -> {:ok, value} + end + user_params = [ :no_rich_text, - :locked, :hide_followers_count, :hide_follows_count, :hide_followers, @@ -159,46 +185,65 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do :show_role, :skip_thread_containment, :allow_following_move, - :discoverable + :accepts_chat_messages ] |> Enum.reduce(%{}, fn key, acc -> - add_if_present(acc, params, key, key, &{:ok, truthy_param?(&1)}) + Maps.put_if_present(acc, key, params[key], &{:ok, truthy_param?(&1)}) end) - |> add_if_present(params, :display_name, :name) - |> add_if_present(params, :note, :bio) - |> add_if_present(params, :avatar, :avatar) - |> add_if_present(params, :header, :banner) - |> add_if_present(params, :pleroma_background_image, :background) - |> add_if_present( - params, - :fields_attributes, + |> Maps.put_if_present(:name, params[:display_name]) + |> Maps.put_if_present(:bio, params[:note]) + |> Maps.put_if_present(:raw_bio, params[:note]) + |> Maps.put_if_present(:avatar, params[:avatar], user_image_value) + |> Maps.put_if_present(:banner, params[:header], user_image_value) + |> Maps.put_if_present(:background, params[:pleroma_background_image], user_image_value) + |> Maps.put_if_present( :raw_fields, + params[:fields_attributes], &{:ok, normalize_fields_attributes(&1)} ) - |> add_if_present(params, :pleroma_settings_store, :pleroma_settings_store) - |> add_if_present(params, :default_scope, :default_scope) - |> add_if_present(params["source"], "privacy", :default_scope) - |> add_if_present(params, :actor_type, :actor_type) - - changeset = User.update_changeset(user, user_params) - - with {:ok, user} <- User.update_and_set_cache(changeset) do - render(conn, "show.json", user: user, for: user, with_pleroma_settings: true) + |> Maps.put_if_present(:pleroma_settings_store, params[:pleroma_settings_store]) + |> Maps.put_if_present(:default_scope, params[:default_scope]) + |> Maps.put_if_present(:default_scope, params["source"]["privacy"]) + |> Maps.put_if_present(:actor_type, params[:bot], fn bot -> + if bot, do: {:ok, "Service"}, else: {:ok, "Person"} + end) + |> Maps.put_if_present(:actor_type, params[:actor_type]) + |> Maps.put_if_present(:is_locked, params[:locked]) + |> Maps.put_if_present(:is_discoverable, params[:discoverable]) + + # What happens here: + # + # We want to update the user through the pipeline, but the ActivityPub + # update information is not quite enough for this, because this also + # contains local settings that don't federate and don't even appear + # in the Update activity. + # + # So we first build the normal local changeset, then apply it to the + # user data, but don't persist it. With this, we generate the object + # data for our update activity. We feed this and the changeset as meta + # inforation into the pipeline, where they will be properly updated and + # federated. + with changeset <- User.update_changeset(user, user_params), + {:ok, unpersisted_user} <- Ecto.Changeset.apply_action(changeset, :update), + updated_object <- + Pleroma.Web.ActivityPub.UserView.render("user.json", user: unpersisted_user) + |> Map.delete("@context"), + {:ok, update_data, []} <- Builder.update(user, updated_object), + {:ok, _update, _} <- + Pipeline.common_pipeline(update_data, + local: true, + user_update_changeset: changeset + ) do + render(conn, "show.json", + user: unpersisted_user, + for: unpersisted_user, + with_pleroma_settings: true + ) else _e -> render_error(conn, :forbidden, "Invalid request") end end - defp add_if_present(map, params, params_field, map_field, value_function \\ &{:ok, &1}) do - with true <- is_map(params), - true <- Map.has_key?(params, params_field), - {:ok, new_value} <- value_function.(Map.get(params, params_field)) do - Map.put(map, map_field, new_value) - else - _ -> map - end - end - defp normalize_fields_attributes(fields) do if Enum.all?(fields, &is_tuple/1) do Enum.map(fields, fn {_, v} -> v end) @@ -223,23 +268,21 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "GET /api/v1/accounts/:id" def show(%{assigns: %{user: for_user}} = conn, %{id: nickname_or_id}) do with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user), - true <- User.visible_for?(user, for_user) do + :visible <- User.visible_for(user, for_user) do render(conn, "show.json", user: user, for: for_user) else - _e -> render_error(conn, :not_found, "Can't find user") + error -> user_visibility_error(conn, error) end end @doc "GET /api/v1/accounts/:id/statuses" def statuses(%{assigns: %{user: reading_user}} = conn, params) do with %User{} = user <- User.get_cached_by_nickname_or_id(params.id, for: reading_user), - true <- User.visible_for?(user, reading_user) do + :visible <- User.visible_for(user, reading_user) do params = params |> Map.delete(:tagged) - |> Enum.filter(&(not is_nil(&1))) - |> Map.new(fn {key, value} -> {to_string(key), value} end) - |> Map.put("tag", params[:tagged]) + |> Map.put(:tag, params[:tagged]) activities = ActivityPub.fetch_user_activities(user, reading_user, params) @@ -252,7 +295,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do as: :activity ) else - _e -> render_error(conn, :not_found, "Can't find user") + error -> user_visibility_error(conn, error) + end + end + + defp user_visibility_error(conn, error) do + case error do + :restrict_unauthenticated -> + render_error(conn, :unauthorized, "This API requires an authenticated user") + + _ -> + render_error(conn, :not_found, "Can't find user") end end @@ -320,7 +373,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do {:error, "Can not follow yourself"} end - def follow(%{assigns: %{user: follower, account: followed}} = conn, params) do + def follow(%{body_params: params, assigns: %{user: follower, account: followed}} = conn, _) do with {:ok, follower} <- MastodonAPI.follow(follower, followed, params) do render(conn, "relationship.json", user: follower, target: followed) else @@ -341,7 +394,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "POST /api/v1/accounts/:id/mute" def mute(%{assigns: %{user: muter, account: muted}, body_params: params} = conn, _params) do - with {:ok, _user_relationships} <- User.mute(muter, muted, params.notifications) do + with {:ok, _user_relationships} <- User.mute(muter, muted, params) do render(conn, "relationship.json", user: muter, target: muted) else {:error, message} -> json_response(conn, :forbidden, %{error: message}) @@ -359,8 +412,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "POST /api/v1/accounts/:id/block" def block(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do - with {:ok, _user_block} <- User.block(blocker, blocked), - {:ok, _activity} <- ActivityPub.block(blocker, blocked) do + with {:ok, _activity} <- CommonAPI.block(blocker, blocked) do render(conn, "relationship.json", user: blocker, target: blocked) else {:error, message} -> json_response(conn, :forbidden, %{error: message}) @@ -390,15 +442,27 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "GET /api/v1/mutes" - def mutes(%{assigns: %{user: user}} = conn, _) do - users = User.muted_users(user, _restrict_deactivated = true) - render(conn, "index.json", users: users, for: user, as: :user) + def mutes(%{assigns: %{user: user}} = conn, params) do + users = + user + |> User.muted_users_relation(_restrict_deactivated = true) + |> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true)) + + conn + |> add_link_headers(users) + |> render("index.json", users: users, for: user, as: :user) end @doc "GET /api/v1/blocks" - def blocks(%{assigns: %{user: user}} = conn, _) do - users = User.blocked_users(user, _restrict_deactivated = true) - render(conn, "index.json", users: users, for: user, as: :user) + def blocks(%{assigns: %{user: user}} = conn, params) do + users = + user + |> User.blocked_users_relation(_restrict_deactivated = true) + |> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true)) + + conn + |> add_link_headers(users) + |> render("index.json", users: users, for: user, as: :user) end @doc "GET /api/v1/endorsements"