Add account aliases
This commit is contained in:
parent
3a2b2cb6f2
commit
d0eb43b58b
13 changed files with 220 additions and 4 deletions
|
@ -570,3 +570,23 @@ Emoji reactions work a lot like favourites do. They make it possible to react to
|
|||
{"name": "😀", "count": 2, "me": true, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]}
|
||||
]
|
||||
```
|
||||
|
||||
# Account aliases
|
||||
|
||||
Set and delete ActivityPub aliases for follower move.
|
||||
|
||||
## `POST /api/v1/pleroma/accounts/ap_aliases`
|
||||
### Add account aliases
|
||||
* Method: `POST`
|
||||
* Authentication: required
|
||||
* Params:
|
||||
* `aliases`: array of ActivityPub IDs to add
|
||||
* Response: JSON, the user's account
|
||||
|
||||
## `DELETE /api/v1/pleroma/accounts/ap_aliases`
|
||||
### Delete account aliases
|
||||
* Method: `DELETE`
|
||||
* Authentication: required
|
||||
* Params:
|
||||
* `aliases`: array of ActivityPub IDs to delete
|
||||
* Response: JSON, the user's account
|
||||
|
|
|
@ -89,6 +89,7 @@ defmodule Pleroma.User do
|
|||
field(:keys, :string)
|
||||
field(:public_key, :string)
|
||||
field(:ap_id, :string)
|
||||
field(:ap_aliases, {:array, :string}, default: [])
|
||||
field(:avatar, :map, default: %{})
|
||||
field(:local, :boolean, default: true)
|
||||
field(:follower_address, :string)
|
||||
|
@ -2268,4 +2269,27 @@ def sanitize_html(%User{} = user, filter) do
|
|||
|> Map.put(:bio, HTML.filter_tags(user.bio, filter))
|
||||
|> Map.put(:fields, fields)
|
||||
end
|
||||
|
||||
def add_aliases(%User{} = user, aliases) when is_list(aliases) do
|
||||
alias_set =
|
||||
(user.ap_aliases ++ aliases)
|
||||
|> MapSet.new()
|
||||
|> MapSet.to_list()
|
||||
|
||||
user
|
||||
|> change(%{ap_aliases: alias_set})
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def delete_aliases(%User{} = user, aliases) when is_list(aliases) do
|
||||
alias_set =
|
||||
user.ap_aliases
|
||||
|> MapSet.new()
|
||||
|> MapSet.difference(MapSet.new(aliases))
|
||||
|> MapSet.to_list()
|
||||
|
||||
user
|
||||
|> change(%{ap_aliases: alias_set})
|
||||
|> Repo.update()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
|
@ -87,10 +89,54 @@ def unsubscribe_operation do
|
|||
}
|
||||
end
|
||||
|
||||
def add_aliases_operation do
|
||||
%Operation{
|
||||
tags: ["Accounts"],
|
||||
summary: "Add ActivityPub aliases",
|
||||
operationId: "PleromaAPI.AccountController.add_aliases",
|
||||
requestBody: request_body("Parameters", alias_request(), required: true),
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Account", "application/json", Account),
|
||||
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def delete_aliases_operation do
|
||||
%Operation{
|
||||
tags: ["Accounts"],
|
||||
summary: "Delete ActivityPub aliases",
|
||||
operationId: "PleromaAPI.AccountController.delete_aliases",
|
||||
requestBody: request_body("Parameters", alias_request(), required: true),
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Account", "application/json", Account)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp id_param do
|
||||
Operation.parameter(:id, :path, FlakeID, "Account ID",
|
||||
example: "9umDrYheeY451cQnEe",
|
||||
required: true
|
||||
)
|
||||
end
|
||||
|
||||
defp alias_request do
|
||||
%Schema{
|
||||
title: "AccountAliasRequest",
|
||||
description: "POST body for adding/deleting AP aliases",
|
||||
type: :object,
|
||||
properties: %{
|
||||
aliases: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{type: :string}
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"aliases" => ["https://beepboop.social/users/beep", "https://mushroom.kingdom/users/toad"]
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,6 +40,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
|||
pleroma: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
ap_id: %Schema{type: :string},
|
||||
ap_aliases: %Schema{type: :array, items: %Schema{type: :string}},
|
||||
allow_following_move: %Schema{
|
||||
type: :boolean,
|
||||
description: "whether the user allows automatically follow moved following accounts"
|
||||
|
|
|
@ -248,6 +248,7 @@ defp do_render("show.json", %{user: user} = opts) do
|
|||
# Pleroma extension
|
||||
pleroma: %{
|
||||
ap_id: user.ap_id,
|
||||
ap_aliases: user.ap_aliases,
|
||||
confirmation_pending: user.confirmation_pending,
|
||||
tags: user.tags,
|
||||
hide_followers_count: user.hide_followers_count,
|
||||
|
|
|
@ -39,6 +39,11 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
|||
%{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:accounts"]} when action in [:add_aliases, :delete_aliases]
|
||||
)
|
||||
|
||||
plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend)
|
||||
|
||||
plug(:assign_account_by_id when action in [:favourites, :subscribe, :unsubscribe])
|
||||
|
@ -107,4 +112,24 @@ def unsubscribe(%{assigns: %{user: user, account: subscription_target}} = conn,
|
|||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/pleroma/accounts/ap_aliases"
|
||||
def add_aliases(%{assigns: %{user: user}, body_params: %{aliases: aliases}} = conn, _params)
|
||||
when is_list(aliases) do
|
||||
with {:ok, user} <- User.add_aliases(user, aliases) do
|
||||
render(conn, "show.json", user: user)
|
||||
else
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
@doc "DELETE /api/v1/pleroma/accounts/ap_aliases"
|
||||
def delete_aliases(%{assigns: %{user: user}, body_params: %{aliases: aliases}} = conn, _params)
|
||||
when is_list(aliases) do
|
||||
with {:ok, user} <- User.delete_aliases(user, aliases) do
|
||||
render(conn, "show.json", user: user)
|
||||
else
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -344,6 +344,9 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
post("/accounts/:id/subscribe", AccountController, :subscribe)
|
||||
post("/accounts/:id/unsubscribe", AccountController, :unsubscribe)
|
||||
|
||||
post("/accounts/ap_aliases", AccountController, :add_aliases)
|
||||
delete("/accounts/ap_aliases", AccountController, :delete_aliases)
|
||||
end
|
||||
|
||||
post("/accounts/confirmation_resend", AccountController, :confirmation_resend)
|
||||
|
|
|
@ -58,12 +58,19 @@ defp gather_links(%User{} = user) do
|
|||
] ++ Publisher.gather_webfinger_links(user)
|
||||
end
|
||||
|
||||
defp gather_aliases(%User{} = user) do
|
||||
user.ap_aliases
|
||||
|> MapSet.new()
|
||||
|> MapSet.put(user.ap_id)
|
||||
|> MapSet.to_list()
|
||||
end
|
||||
|
||||
def represent_user(user, "JSON") do
|
||||
{:ok, user} = User.ensure_keys_present(user)
|
||||
|
||||
%{
|
||||
"subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}",
|
||||
"aliases" => [user.ap_id],
|
||||
"aliases" => gather_aliases(user),
|
||||
"links" => gather_links(user)
|
||||
}
|
||||
end
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
defmodule Pleroma.Repo.Migrations.AddAliasesToUsers do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:users) do
|
||||
add(:ap_aliases, {:array, :string}, default: [])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1858,4 +1858,41 @@ test "avatar fallback" do
|
|||
|
||||
assert User.avatar_url(user, no_default: true) == nil
|
||||
end
|
||||
|
||||
test "add_aliases/2" do
|
||||
user = insert(:user)
|
||||
|
||||
aliases = [
|
||||
"https://gleasonator.com/users/alex",
|
||||
"https://gleasonator.com/users/alex",
|
||||
"https://animalliberation.social/users/alex"
|
||||
]
|
||||
|
||||
{:ok, user} = User.add_aliases(user, aliases)
|
||||
|
||||
assert user.ap_aliases == [
|
||||
"https://animalliberation.social/users/alex",
|
||||
"https://gleasonator.com/users/alex"
|
||||
]
|
||||
end
|
||||
|
||||
test "delete_aliases/2" do
|
||||
user =
|
||||
insert(:user,
|
||||
ap_aliases: [
|
||||
"https://animalliberation.social/users/alex",
|
||||
"https://benis.social/users/benis",
|
||||
"https://gleasonator.com/users/alex"
|
||||
]
|
||||
)
|
||||
|
||||
aliases = ["https://benis.social/users/benis"]
|
||||
|
||||
{:ok, user} = User.delete_aliases(user, aliases)
|
||||
|
||||
assert user.ap_aliases == [
|
||||
"https://animalliberation.social/users/alex",
|
||||
"https://gleasonator.com/users/alex"
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -37,7 +37,8 @@ test "Represent a user account" do
|
|||
"<script src=\"invalid-html\"></script><span>valid html</span>. a<br>b<br/>c<br >d<br />f '&<>\"",
|
||||
inserted_at: ~N[2017-08-15 15:47:06.597036],
|
||||
emoji: %{"karjalanpiirakka" => "/file.png"},
|
||||
raw_bio: "valid html. a\nb\nc\nd\nf '&<>\""
|
||||
raw_bio: "valid html. a\nb\nc\nd\nf '&<>\"",
|
||||
ap_aliases: ["https://shitposter.zone/users/shp"]
|
||||
})
|
||||
|
||||
expected = %{
|
||||
|
@ -77,6 +78,7 @@ test "Represent a user account" do
|
|||
},
|
||||
pleroma: %{
|
||||
ap_id: user.ap_id,
|
||||
ap_aliases: ["https://shitposter.zone/users/shp"],
|
||||
background_image: "https://example.com/images/asuka_hospital.png",
|
||||
favicon:
|
||||
"https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png",
|
||||
|
@ -171,6 +173,7 @@ test "Represent a Service(bot) account" do
|
|||
},
|
||||
pleroma: %{
|
||||
ap_id: user.ap_id,
|
||||
ap_aliases: [],
|
||||
background_image: nil,
|
||||
favicon:
|
||||
"https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png",
|
||||
|
|
|
@ -281,4 +281,33 @@ test "returns 404 when subscription_target not found" do
|
|||
assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404)
|
||||
end
|
||||
end
|
||||
|
||||
describe "aliases controllers" do
|
||||
setup do: oauth_access(["write:accounts"])
|
||||
|
||||
test "adds aliases", %{conn: conn} do
|
||||
aliases = ["https://gleasonator.com/users/alex"]
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/pleroma/accounts/ap_aliases", %{"aliases" => aliases})
|
||||
|
||||
assert %{"pleroma" => %{"ap_aliases" => res}} = json_response_and_validate_schema(conn, 200)
|
||||
assert Enum.count(res) == 1
|
||||
end
|
||||
|
||||
test "deletes aliases", %{conn: conn, user: user} do
|
||||
aliases = ["https://gleasonator.com/users/alex"]
|
||||
User.add_aliases(user, aliases)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> delete("/api/v1/pleroma/accounts/ap_aliases", %{"aliases" => aliases})
|
||||
|
||||
assert %{"pleroma" => %{"ap_aliases" => res}} = json_response_and_validate_schema(conn, 200)
|
||||
assert Enum.count(res) == 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,14 +30,24 @@ test "GET host-meta" do
|
|||
end
|
||||
|
||||
test "Webfinger JRD" do
|
||||
user = insert(:user)
|
||||
user =
|
||||
insert(:user,
|
||||
ap_id: "https://hyrule.world/users/zelda",
|
||||
ap_aliases: ["https://mushroom.kingdom/users/toad"]
|
||||
)
|
||||
|
||||
response =
|
||||
build_conn()
|
||||
|> put_req_header("accept", "application/jrd+json")
|
||||
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
|
||||
|> json_response(200)
|
||||
|
||||
assert json_response(response, 200)["subject"] == "acct:#{user.nickname}@localhost"
|
||||
assert response["subject"] == "acct:#{user.nickname}@localhost"
|
||||
|
||||
assert response["aliases"] == [
|
||||
"https://hyrule.world/users/zelda",
|
||||
"https://mushroom.kingdom/users/toad"
|
||||
]
|
||||
end
|
||||
|
||||
test "it returns 404 when user isn't found (JSON)" do
|
||||
|
|
Loading…
Reference in a new issue