Compare commits

...

15 commits

Author SHA1 Message Date
FloatingGhost 16a31872fe document local_bubble 2022-10-21 10:23:07 +01:00
FloatingGhost 7bb6df2d5b Remove unused DATA arg 2022-10-20 13:26:00 +01:00
floatingghost f36d14818d Unilateral remove from followers (#232)
from https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3647/

Co-authored-by: marcin mikołajczak <git@mkljczk.pl>
Co-authored-by: Tusooa Zhu <tusooa@kazv.moe>
Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
Reviewed-on: AkkomaGang/akkoma#232
2022-10-19 10:01:14 +00:00
FloatingGhost 5231d436d1 Add docker migration guide 2022-10-18 16:16:55 +01:00
FloatingGhost deba1d25f5 add DB restart to docker file 2022-10-17 16:29:36 +01:00
floatingghost 66f913355a Docker builds (#231)
Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
Reviewed-on: AkkomaGang/akkoma#231
2022-10-16 19:25:54 +00:00
FloatingGhost 60b3c8d17b bump version 2022-10-14 12:49:35 +01:00
floatingghost edf7d5089f Merge pull request 'Check that the signature matches the creator' (#230) from domain-blocks into develop
Reviewed-on: AkkomaGang/akkoma#230
2022-10-14 11:41:34 +00:00
floatingghost d4bdd3ddb7 Merge pull request 'SQL optimisations' (#227) from i-hate-sql into develop
Reviewed-on: AkkomaGang/akkoma#227
2022-10-14 10:49:02 +00:00
FloatingGhost 03662501c3 Check that the signature matches the creator 2022-10-14 11:48:32 +01:00
FloatingGhost 856c57208b Ensure deletes are handled after everything else 2022-10-11 14:30:08 +01:00
FloatingGhost cb9b0d3720 optimise notifications query 2022-10-11 11:40:43 +01:00
FloatingGhost 8af50dea36 format 2022-10-10 17:13:42 +01:00
FloatingGhost ca9e6ffc55 Use inner lateral join to not get dropped in :total 2022-10-10 16:45:02 +01:00
FloatingGhost 574f010bc8 Extract deactivated users query to a join 2022-10-10 15:55:58 +01:00
36 changed files with 720 additions and 112 deletions

View file

@ -6,12 +6,12 @@ COPYING
*file
elixir_buildpack.config
test/
instance/
_build
deps
test
benchmarks
docs/site
docker-db
uploads
instance
# Required to get version
!.git

10
.gitignore vendored
View file

@ -17,6 +17,13 @@ secret
/instance
/priv/ssh_keys
vm.args
.cache/
.hex/
.mix/
.psql_history
docker-resources/Dockerfile
docker-resources/Caddyfile
pgdata
# Prevent committing custom emojis
/priv/static/emoji/custom/*
@ -65,3 +72,6 @@ pleroma.iml
# Generated documentation
docs/site
# docker stuff
docker-db

View file

@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased
## Added
- Officially supported docker release
- Ability to remove followers unilaterally without a block
## Changes
- Follows no longer override domain blocks, a domain block is final
- Deletes are now the lowest priority to publish and will be handled after creates
## 2022.10
### Added
@ -13,6 +23,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed
- Emoji updated to latest 15.0 draft
- **Breaking**: `/api/v1/pleroma/backups` endpoints now requires `read:backups` scope instead of `read:accounts`
- Verify that the signature on posts is not domain blocked, and belongs to the correct user
### Fixed
- OAuthPlug no longer joins with the database every call and uses the user cache

View file

@ -1,21 +1,8 @@
FROM elixir:1.13.4-alpine as build
COPY . .
FROM hexpm/elixir:1.13.4-erlang-24.3.4.5-alpine-3.15.6
ENV MIX_ENV=prod
RUN apk add git gcc g++ musl-dev make cmake file-dev &&\
echo "import Config" > config/prod.secret.exs &&\
mix local.hex --force &&\
mix local.rebar --force &&\
mix deps.get --only prod &&\
mkdir release &&\
mix release --path release
FROM alpine:3.16
ARG BUILD_DATE
ARG VCS_REF
ARG HOME=/opt/akkoma
LABEL org.opencontainers.image.title="akkoma" \
org.opencontainers.image.description="Akkoma for Docker" \
@ -26,25 +13,21 @@ LABEL org.opencontainers.image.title="akkoma" \
org.opencontainers.image.revision=$VCS_REF \
org.opencontainers.image.created=$BUILD_DATE
ARG HOME=/opt/akkoma
ARG DATA=/var/lib/akkoma
RUN apk update &&\
apk add exiftool ffmpeg imagemagick libmagic ncurses postgresql-client &&\
adduser --system --shell /bin/false --home ${HOME} akkoma &&\
mkdir -p ${DATA}/uploads &&\
mkdir -p ${DATA}/static &&\
chown -R akkoma ${DATA} &&\
mkdir -p /etc/akkoma &&\
chown -R akkoma /etc/akkoma
USER akkoma
COPY --from=build --chown=akkoma:0 /release ${HOME}
COPY ./config/docker.exs /etc/akkoma/config.exs
COPY ./docker-entrypoint.sh ${HOME}
RUN apk add git gcc g++ musl-dev make cmake file-dev exiftool ffmpeg imagemagick libmagic ncurses postgresql-client
EXPOSE 4000
ENTRYPOINT ["/opt/akkoma/docker-entrypoint.sh"]
ARG UID=1000
ARG GID=1000
ARG UNAME=akkoma
RUN addgroup -g $GID $UNAME
RUN adduser -u $UID -G $UNAME -D -h $HOME $UNAME
WORKDIR /opt/akkoma
USER $UNAME
RUN mix local.hex --force &&\
mix local.rebar --force
CMD ["/opt/akkoma/docker-entrypoint.sh"]

View file

@ -569,7 +569,10 @@ config :pleroma, Oban,
mute_expire: 5,
search_indexing: 10
],
plugins: [Oban.Plugins.Pruner],
plugins: [
Oban.Plugins.Pruner,
{Oban.Plugins.Reindexer, schedule: "@weekly"}
],
crontab: [
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}

View file

@ -24,11 +24,11 @@ config :pleroma, Pleroma.Repo,
config :web_push_encryption, :vapid_details, subject: "mailto:#{System.get_env("NOTIFY_EMAIL")}"
config :pleroma, :database, rum_enabled: false
config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
config :pleroma, :instance, static_dir: "/var/lib/akkoma/static"
config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/akkoma/uploads"
# We can't store the secrets in this file, since this is baked into the docker image
if not File.exists?("/var/lib/pleroma/secret.exs") do
if not File.exists?("/var/lib/akkoma/secret.exs") do
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
{web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
@ -52,16 +52,16 @@ if not File.exists?("/var/lib/pleroma/secret.exs") do
web_push_private_key: Base.url_encode64(web_push_private_key, padding: false)
)
File.write("/var/lib/pleroma/secret.exs", secret_file)
File.write("/var/lib/akkoma/secret.exs", secret_file)
end
import_config("/var/lib/pleroma/secret.exs")
import_config("/var/lib/akkoma/secret.exs")
# For additional user config
if File.exists?("/var/lib/pleroma/config.exs"),
do: import_config("/var/lib/pleroma/config.exs"),
if File.exists?("/var/lib/akkoma/config.exs"),
do: import_config("/var/lib/akkoma/config.exs"),
else:
File.write("/var/lib/pleroma/config.exs", """
File.write("/var/lib/akkoma/config.exs", """
import Config
# For additional configuration outside of environmental variables

61
docker-compose.yml Normal file
View file

@ -0,0 +1,61 @@
version: "3.7"
services:
db:
image: akkoma-db:latest
build: ./docker-resources/database
restart: unless-stopped
user: ${DOCKER_USER}
environment: {
# This might seem insecure but is usually not a problem.
# You should leave this at the "akkoma" default.
# The DB is only reachable by containers in the same docker network,
# and is not exposed to the open internet.
#
# If you do change this, remember to update "config.exs".
POSTGRES_DB: akkoma,
POSTGRES_USER: akkoma,
POSTGRES_PASSWORD: akkoma,
}
env_file:
- .env
volumes:
- type: bind
source: ./pgdata
target: /var/lib/postgresql/data
akkoma:
image: akkoma:latest
build: .
restart: unless-stopped
env_file:
- .env
links:
- db
ports: [
# Uncomment/Change port mappings below as needed.
# The left side is your host machine, the right one is the akkoma container.
# You can prefix the left side with an ip.
# Webserver (for reverse-proxies outside of docker)
# If you use a dockerized proxy, you can leave this commented
# and use a container link instead.
"127.0.0.1:4000:4000",
]
volumes:
- .:/opt/akkoma
# Uncomment the following if you want to use a reverse proxy
#proxy:
# image: caddy:2-alpine
# restart: unless-stopped
# links:
# - akkoma
# ports: [
# "443:443",
# "80:80"
# ]
# volumes:
# - ./docker-resources/Caddyfile:/etc/caddy/Caddyfile
# - ./caddy-data:/data
# - ./caddy-config:/config

View file

@ -8,7 +8,7 @@ while ! pg_isready -U ${DB_USER:-pleroma} -d postgres://${DB_HOST:-db}:5432/${DB
done
echo "-- Running migrations..."
$HOME/bin/pleroma_ctl migrate
mix ecto.migrate
echo "-- Starting!"
exec $HOME/bin/pleroma start
mix phx.server

View file

@ -0,0 +1,14 @@
# default docker Caddyfile config for Akkoma
#
# Simple installation instructions:
# 1. Replace 'example.tld' with your instance's domain wherever it appears.
example.tld {
log {
output file /var/log/caddy/akkoma.log
}
encode gzip
reverse_proxy akkoma:4000
}

4
docker-resources/build.sh Executable file
View file

@ -0,0 +1,4 @@
#!/bin/sh
docker-compose build --build-arg UID=$(id -u) --build-arg GID=$(id -g) akkoma
docker-compose build --build-arg UID=$(id -u) --build-arg GID=$(id -g) db

View file

@ -0,0 +1,10 @@
FROM postgres:14-alpine
ARG UID=1000
ARG GID=1000
ARG UNAME=akkoma
RUN addgroup -g $GID $UNAME
RUN adduser -u $UID -G $UNAME -D -h $HOME $UNAME
USER akkoma

View file

@ -0,0 +1,4 @@
MIX_ENV=prod
DB_NAME=akkoma
DB_USER=akkoma
DB_PASS=akkoma

3
docker-resources/manage.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
docker-compose run --rm akkoma $@

View file

@ -59,6 +59,7 @@ To add configuration to your config file, you can copy it from the base config.
* `cleanup_attachments`: Remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances.
* `show_reactions`: Let favourites and emoji reactions be viewed through the API (default: `true`).
* `password_reset_token_validity`: The time after which reset tokens aren't accepted anymore, in seconds (default: one day).
* `local_bubble`: Array of domains representing instances closely related to yours. Used to populate the `bubble` timeline. e.g `['example.com']`, (default: `[]`)
## :database
* `improved_hashtag_timeline`: Setting to force toggle / force disable improved hashtags timeline. `:enabled` forces hashtags to be fetched from `hashtags` table for hashtags timeline. `:disabled` forces object-embedded hashtags to be used (slower). Keep it `:auto` for automatic behaviour (it is auto-set to `:enabled` [unless overridden] when HashtagsTableMigrator completes).

View file

@ -0,0 +1,161 @@
# Installing in Docker
## Installation
This guide will show you how to get akkoma working in a docker container,
if you want isolation, or if you run a distribution not supported by the OTP
releases.
If you want to migrate from or OTP to docker, check out [the migration guide](./migrating_to_docker_en.md).
### Prepare the system
* Install docker and docker-compose
* [Docker](https://docs.docker.com/engine/install/)
* [Docker-compose](https://docs.docker.com/compose/install/)
* This will usually just be a repository installation and a package manager invocation.
* Clone the akkoma repository
* `git clone https://akkoma.dev/AkkomaGang/akkoma.git -b stable`
* `cd akkoma`
### Set up basic configuration
```bash
cp docker-resources/env.example .env
echo "DOCKER_USER=$(id -u):$(id -g)" >> .env
```
This probably won't need to be changed, it's only there to set basic environment
variables for the docker-compose file.
### Building the container
The container provided is a thin wrapper around akkoma's dependencies,
it does not contain the code itself. This is to allow for easy updates
and debugging if required.
```bash
./docker-resources/build.sh
```
This will generate a container called `akkoma` which we can use
in our compose environment.
### Generating your instance
```bash
mkdir pgdata
./docker-resources/manage.sh mix deps.get
./docker-resources/manage.sh mix compile
./docker-resources/manage.sh mix pleroma.instance gen
```
This will ask you a few questions - the defaults are fine for most things,
the database hostname is `db`, and you will want to set the ip to `0.0.0.0`.
Now we'll want to copy over the config it just created
```bash
cp config/generated_config.exs config/prod.secret.exs
```
### Setting up the database
We need to run a few commands on the database container, this isn't too bad
```bash
docker-compose run --rm --user akkoma -d db
# Note down the name it gives here, it will be something like akkoma_db_run
docker-compose run --rm akkoma psql -h db -U akkoma -f config/setup_db.psql
docker stop akkoma_db_run # Replace with the name you noted down
```
Now we can actually run our migrations
```bash
./docker-resources/manage.sh mix ecto.migrate
# this will recompile your files at the same time, since we changed the config
```
### Start the server
We're going to run it in the foreground on the first run, just to make sure
everything start up.
```bash
docker-compose up
```
If everything went well, you should be able to access your instance at http://localhost:4000
You can `ctrl-c` out of the docker-compose now to shutdown the server.
### Running in the background
```bash
docker-compose up -d
```
### Create your first user
If your instance is up and running, you can create your first user with administrative rights with the following task:
```shell
./docker-resources/manage.sh mix pleroma.user new MY_USERNAME MY_EMAIL@SOMEWHERE --admin
```
And follow the prompts
### Reverse proxies
This is a tad more complex in docker than on the host itself. It
You've got two options.
#### Running caddy in a container
This is by far the easiest option. It'll handle HTTPS and all that for you.
```bash
mkdir caddy-data
mkdir caddy-config
cp docker-resources/Caddyfile.example docker-resources/Caddyfile
```
Then edit the TLD in your caddyfile to the domain you're serving on.
Uncomment the `caddy` section in the docker-compose file,
then run `docker-compose up -d` again.
#### Running a reverse proxy on the host
If you want, you can also run the reverse proxy on the host. This is a bit more complex, but it's also more flexible.
Follow the guides for source install for your distribution of choice, or adapt
as needed. Your standard setup can be found in the [Debian Guide](../debian_based_en/#nginx)
### You're done!
All that's left is to set up your frontends.
The standard from-source commands will apply to you, just make sure you
prefix them with `./docker-resources/manage.sh`!
{! installation/frontends.include !}
### Updating Docker Installs
```bash
git pull
./docker-resources/build.sh
./docker-resources/manage.sh mix deps.get
./docker-resources/manage.sh mix compile
./docker-resources/manage.sh mix ecto.migrate
docker-compose restart akkoma db
```
#### Further reading
{! installation/further_reading.include !}
{! support.include !}

View file

@ -21,5 +21,11 @@ For most installations, the following will suffice:
mix pleroma.frontend install admin-fe --ref stable
```
=== "Docker"
```sh
./docker-resources/manage.sh mix pleroma.frontend install pleroma-fe --ref stable
./docker-resources/manage.sh mix pleroma.frontend install admin-fe --ref stable
```
For more customised installations, refer to [Frontend Management](../../configuration/frontend_management)

View file

@ -0,0 +1,158 @@
# Migrating to a Docker Installation
If you for any reason wish to migrate a source or OTP install to a docker one,
this guide is for you.
You have a few options - your major one will be whether you want to keep your
reverse-proxy setup from before.
You probably should, in the first instance.
### Prepare the system
* Install docker and docker-compose
* [Docker](https://docs.docker.com/engine/install/)
* [Docker-compose](https://docs.docker.com/compose/install/)
* This will usually just be a repository installation and a package manager invocation.
=== "Source"
```bash
git pull
```
=== "OTP"
Clone the akkoma repository
```bash
git clone https://akkoma.dev/AkkomaGang/akkoma.git -b stable
cd akkoma
```
### Back up your old database
Change the database name as needed
```bash
pg_dump -d akkoma_prod --format c > akkoma_backup.sql
```
### Getting your static files in the right place
This will vary by every installation. Copy your `instance` directory to `instance/` in
the akkoma source directory - this is where the docker container will look for it.
For *most* from-source installs it'll already be there.
And the same with `uploads`, make sure your uploads (if you have them on disk) are
located at `uploads/` in the akkoma source directory.
If you have them on a different disk, you will need to mount that disk into the docker-compose file,
with an entry that looks like this:
```yaml
akkoma:
volumes:
- .:/opt/akkoma # This should already be there
- type: bind
source: /path/to/your/uploads
target: /opt/akkoma/uploads
```
### Set up basic configuration
```bash
cp docker-resources/env.example .env
echo "DOCKER_USER=$(id -u):$(id -g)" >> .env
```
This probably won't need to be changed, it's only there to set basic environment
variables for the docker-compose file.
=== "From source"
You probably won't need to change your config. Provided your `config/prod.secret.exs` file
is still there, you're all good.
=== "OTP"
```bash
cp /etc/akkoma/config.exs config/prod.secret.exs
```
**BOTH**
Set the following config in `config/prod.secret.exs`:
```elixir
config :pleroma, Pleroma.Web.Endpoint,
...,
http: [ip: {0, 0, 0, 0}, port: 4000]
config :pleroma, Pleroma.Repo,
...,
username: "akkoma",
password: "akkoma",
database: "akkoma",
hostname: "db"
```
### Building the container
The container provided is a thin wrapper around akkoma's dependencies,
it does not contain the code itself. This is to allow for easy updates
and debugging if required.
```bash
./docker-resources/build.sh
```
This will generate a container called `akkoma` which we can use
in our compose environment.
### Setting up the docker resources
```bash
# These won't exist if you're migrating from OTP
rm -rf deps
rm -rf _build
```
```bash
mkdir pgdata
./docker-resources/manage.sh mix deps.get
./docker-resources/manage.sh mix compile
```
### Setting up the database
Now we can import our database to the container.
```bash
docker-compose run --rm --user akkoma -d db
docker-compose run --rm akkoma pg_restore -v -U akkoma -j $(grep -c ^processor /proc/cpuinfo) -d akkoma -h db akkoma_backup.sql
```
### Reverse proxies
If you're just reusing your old proxy, you may have to uncomment the line in
the docker-compose file under `ports`. You'll find it.
Otherwise, you can use the same setup as the [docker installation guide](./docker_en.md#reverse-proxies).
### Let's go
```bash
docker-compose up -d
```
You should now be at the same point as you were before, but with a docker install.
{! installation/frontends.include !}
See the [docker installation guide](./docker_en.md) for more information on how to
update.
#### Further reading
{! installation/further_reading.include !}
{! support.include !}

View file

@ -538,6 +538,12 @@ defmodule Mix.Tasks.Pleroma.User do
end
end
def run(["convert_id", id]) do
{:ok, uuid} = FlakeId.Ecto.Type.dump(id)
{:ok, raw_id} = Ecto.UUID.load(uuid)
shell_info(raw_id)
end
defp refetch_public_keys(query) do
query
|> Pleroma.Repo.chunk_stream(50, :batches)

View file

@ -368,9 +368,15 @@ defmodule Pleroma.Activity do
end
def restrict_deactivated_users(query) do
deactivated_users_query = from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
from(activity in query, where: activity.actor not in subquery(deactivated_users_query))
query
|> join(
:inner_lateral,
[activity],
active in fragment(
"SELECT is_active from users WHERE ap_id = ? AND is_active = TRUE",
activity.actor
)
)
end
defdelegate search(user, query, options \\ []), to: Pleroma.Search.DatabaseSearch

View file

@ -240,30 +240,6 @@ defmodule Pleroma.FollowingRelationship do
end)
end
@doc """
For a query with joined activity,
keeps rows where activity's actor is followed by user -or- is NOT domain-blocked by user.
"""
def keep_following_or_not_domain_blocked(query, user) do
where(
query,
[_, activity],
fragment(
# "(actor's domain NOT in domain_blocks) OR (actor IS in followed AP IDs)"
"""
NOT (substring(? from '.*://([^/]*)') = ANY(?)) OR
? = ANY(SELECT ap_id FROM users AS u INNER JOIN following_relationships AS fr
ON u.id = fr.following_id WHERE fr.follower_id = ? AND fr.state = ?)
""",
activity.actor,
^user.domain_blocks,
activity.actor,
^User.binary_id(user.id),
^accept_state_code()
)
)
end
defp validate_not_self_relationship(%Changeset{} = changeset) do
changeset
|> validate_follower_id_following_id_inequality()

View file

@ -138,7 +138,24 @@ defmodule Pleroma.Notification do
query
|> where([n, a], a.actor not in ^blocked_ap_ids)
|> FollowingRelationship.keep_following_or_not_domain_blocked(user)
|> restrict_domain_blocked(user)
end
defp restrict_domain_blocked(query, user) do
where(
query,
[_, activity],
fragment(
# "(actor's domain NOT in domain_blocks)"
"""
NOT (
substring(? from '.*://([^/]*)') = ANY(?)
)
""",
activity.actor,
^user.domain_blocks
)
)
end
defp exclude_blockers(query, user) do

View file

@ -108,8 +108,8 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
Config.get([:mrf_simple, :reject], [])
end
defp should_federate?(inbox) do
%{host: host} = URI.parse(inbox)
def should_federate?(url) do
%{host: host} = URI.parse(url)
quarantined_instances =
blocked_instances()

View file

@ -323,8 +323,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
end
if result == :ok do
Notification.create_notifications(object)
# Only remove from index when deleting actual objects, not users or anything else
with %Pleroma.Object{} <- deleted_object do
Pleroma.Search.remove_from_index(deleted_object)

View file

@ -334,6 +334,22 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
}
end
def remove_from_followers_operation do
%Operation{
tags: ["Account actions"],
summary: "Remove from followers",
operationId: "AccountController.remove_from_followers",
security: [%{"oAuth" => ["follow", "write:follows"]}],
description: "Remove the given account from followers",
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
responses: %{
200 => Operation.response("Relationship", "application/json", AccountRelationship),
400 => Operation.response("Error", "application/json", ApiError),
404 => Operation.response("Error", "application/json", ApiError)
}
}
end
def note_operation do
%Operation{
tags: ["Account actions"],

View file

@ -53,12 +53,19 @@ defmodule Pleroma.Web.Federator do
@impl true
def publish(%{data: %{"object" => object}} = activity) when is_map(object) or is_list(object) do
PublisherWorker.enqueue("publish", %{
"activity_id" => activity.id,
"object_data" => Jason.encode!(object)
})
PublisherWorker.enqueue(
"publish",
%{
"activity_id" => activity.id,
"object_data" => Jason.encode!(object)
},
priority: publish_priority(activity)
)
end
defp publish_priority(%{type: "Delete"}), do: 3
defp publish_priority(_), do: 0
# Job Worker Callbacks
@spec perform(atom(), module(), any()) :: {:ok, any()} | {:error, any()}

View file

@ -76,15 +76,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
plug(
OAuthScopesPlug,
%{scopes: ["follow", "write:follows"]} when action in [:follow_by_uri, :follow, :unfollow]
%{scopes: ["follow", "write:follows"]}
when action in [:follow_by_uri, :follow, :unfollow, :remove_from_followers]
)
plug(OAuthScopesPlug, %{scopes: ["follow", "read:mutes"]} when action == :mutes)
plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
@relationship_actions [:follow, :unfollow]
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock note)a
@relationship_actions [:follow, :unfollow, :remove_from_followers]
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock note remove_from_followers)a
plug(
RateLimiter,
@ -447,6 +448,20 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
end
end
@doc "POST /api/v1/accounts/:id/remove_from_followers"
def remove_from_followers(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do
{:error, "Can not unfollow yourself"}
end
def remove_from_followers(%{assigns: %{user: followed, account: follower}} = conn, _params) do
with {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
render(conn, "relationship.json", user: followed, target: follower)
else
nil ->
render_error(conn, :not_found, "Record not found")
end
end
@doc "POST /api/v1/follows"
def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do
case User.get_cached_by_nickname(uri) do

View file

@ -19,6 +19,7 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do
def call(%{assigns: %{valid_signature: true}, params: %{"actor" => actor}} = conn, _opts) do
with actor_id <- Utils.get_ap_id(actor),
{:user, %User{} = user} <- {:user, user_from_key_id(conn)},
{:federate, true} <- {:federate, should_federate?(user)},
{:user_match, true} <- {:user_match, user.ap_id == actor_id} do
conn
|> assign(:user, user)
@ -27,33 +28,70 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do
{:user_match, false} ->
Logger.debug("Failed to map identity from signature (payload actor mismatch)")
Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{inspect(actor)}")
assign(conn, :valid_signature, false)
conn
|> assign(:valid_signature, false)
# remove me once testsuite uses mapped capabilities instead of what we do now
{:user, nil} ->
Logger.debug("Failed to map identity from signature (lookup failure)")
Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}")
conn
|> assign(:valid_signature, false)
{:federate, false} ->
Logger.debug("Identity from signature is instance blocked")
Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}")
conn
|> assign(:valid_signature, false)
end
end
# no payload, probably a signed fetch
def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
with %User{} = user <- user_from_key_id(conn) do
with %User{} = user <- user_from_key_id(conn),
{:federate, true} <- {:federate, should_federate?(user)} do
conn
|> assign(:user, user)
|> AuthHelper.skip_oauth()
else
{:federate, false} ->
Logger.debug("Identity from signature is instance blocked")
Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}")
conn
|> assign(:valid_signature, false)
nil ->
Logger.debug("Failed to map identity from signature (lookup failure)")
Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}")
only_permit_user_routes(conn)
_ ->
Logger.debug("Failed to map identity from signature (no payload actor mismatch)")
Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}")
assign(conn, :valid_signature, false)
conn
|> assign(:valid_signature, false)
end
end
# no signature at all
def call(conn, _opts), do: conn
defp only_permit_user_routes(%{path_info: ["users", _]} = conn) do
conn
|> assign(:limited_ap, true)
end
defp only_permit_user_routes(conn) do
conn
|> assign(:valid_signature, false)
end
defp key_id_from_conn(conn) do
with %{"keyId" => key_id} <- HTTPSignatures.signature_for_conn(conn),
{:ok, ap_id} <- Signature.key_id_to_actor_id(key_id) do
@ -73,4 +111,14 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do
nil
end
end
defp should_federate?(%User{ap_id: ap_id}), do: should_federate?(ap_id)
defp should_federate?(ap_id) do
if Pleroma.Config.get([:activitypub, :authorized_fetch_mode], false) do
Pleroma.Web.ActivityPub.Publisher.should_federate?(ap_id)
else
true
end
end
end

View file

@ -509,6 +509,7 @@ defmodule Pleroma.Web.Router do
post("/accounts/:id/mute", AccountController, :mute)
post("/accounts/:id/unmute", AccountController, :unmute)
post("/accounts/:id/note", AccountController, :note)
post("/accounts/:id/remove_from_followers", AccountController, :remove_from_followers)
get("/conversations", ConversationController, :index)
post("/conversations/:id/read", ConversationController, :mark_as_read)

View file

@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
def project do
[
app: :pleroma,
version: version("3.3.0"),
version: version("3.3.1"),
elixir: "~> 1.12",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
@ -120,7 +120,7 @@ defmodule Pleroma.Mixfile do
{:phoenix_pubsub, "~> 2.1"},
{:phoenix_ecto, "~> 4.4"},
{:ecto_enum, "~> 1.4"},
{:ecto_sql, "~> 3.8.3"},
{:ecto_sql, "~> 3.9.0"},
{:postgrex, ">= 0.16.3"},
{:oban, "~> 2.12.1"},
{:gettext,

View file

@ -26,10 +26,10 @@
"earmark": {:hex, :earmark, "1.4.26", "f0e3c3d5c278a6d448ad8c27ab0ecdec9c57a7710553138c56af220a6330a4fd", [:mix], [{:earmark_parser, "~> 1.4.26", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "e1231882b56bece0692af33f0959f06c9cd580c2dc2ecb1dc9f16f2750fa78c5"},
"earmark_parser": {:hex, :earmark_parser, "1.4.26", "f4291134583f373c7d8755566122908eb9662df4c4b63caa66a0eabe06569b0a", [:mix], [], "hexpm", "48d460899f8a0c52c5470676611c01f64f3337bad0b26ddab43648428d94aabc"},
"eblurhash": {:hex, :eblurhash, "1.2.2", "7da4255aaea984b31bb71155f673257353b0e0554d0d30dcf859547e74602582", [:rebar3], [], "hexpm", "8c20ca00904de023a835a9dcb7b7762fed32264c85a80c3cafa85288e405044c"},
"ecto": {:hex, :ecto, "3.8.4", "e06b8b87e62b27fea17fd2ff6041572ddd10339fd16cdf58446e402c6c90a74b", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f9244288b8d42db40515463a008cf3f4e0e564bb9c249fe87bf28a6d79fe82d4"},
"ecto": {:hex, :ecto, "3.9.1", "67173b1687afeb68ce805ee7420b4261649d5e2deed8fe5550df23bab0bc4396", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c80bb3d736648df790f7f92f81b36c922d9dd3203ca65be4ff01d067f54eb304"},
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
"ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.4", "5d43fd088d39a158c860b17e8d210669587f63ec89ea122a4654861c8c6e2db4", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.15.7", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "311db02f1b772e3d0dc7f56a05044b5e1499d78ed6abf38885e1ca70059449e5"},
"ecto_sql": {:hex, :ecto_sql, "3.8.3", "a7d22c624202546a39d615ed7a6b784580391e65723f2d24f65941b4dd73d471", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.8.4", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "348cb17fb9e6daf6f251a87049eafcb57805e2892e5e6a0f5dea0985d367329b"},
"ecto_sql": {:hex, :ecto_sql, "3.9.0", "2bb21210a2a13317e098a420a8c1cc58b0c3421ab8e3acfa96417dab7817918c", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a8f3f720073b8b1ac4c978be25fa7960ed7fd44997420c304a4a2e200b596453"},
"elasticsearch": {:git, "https://akkoma.dev/AkkomaGang/elasticsearch-elixir.git", "6cd946f75f6ab9042521a009d1d32d29a90113ca", [ref: "main"]},
"elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
"eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"},
@ -56,7 +56,7 @@
"httpoison": {:hex, :httpoison, "1.8.1", "df030d96de89dad2e9983f92b0c506a642d4b1f4a819c96ff77d12796189c63e", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "35156a6d678d6d516b9229e208942c405cf21232edd632327ecfaf4fd03e79e0"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"},
"jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"},
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"joken": {:hex, :joken, "2.5.0", "09be497d804b8115eb6f07615cef2e60c2a1008fb89dc0aef0d4c4b4609b99aa", [:mix], [{:jose, "~> 1.11.2", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "22b25c89617c5ed8ca7b31026340a25ea0f9ca7160f9706b79be9ed81fdf74e7"},
"jose": {:hex, :jose, "1.11.2", "f4c018ccf4fdce22c71e44d471f15f723cb3efab5d909ab2ba202b5bf35557b3", [:mix, :rebar3], [], "hexpm", "98143fbc48d55f3a18daba82d34fe48959d44538e9697c08f34200fa5f0947d2"},
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
@ -94,7 +94,7 @@
"plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "79fd4fcf34d110605c26560cbae8f23c603ec4158c08298bd4360fdea90bb5cf"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"},
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
"postgrex": {:hex, :postgrex, "0.16.3", "fac79a81a9a234b11c44235a4494d8565303fa4b9147acf57e48978a074971db", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "aeaae1d2d1322da4e5fe90d241b0a564ce03a3add09d7270fb85362166194590"},
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
"pot": {:hex, :pot, "1.0.2", "13abb849139fdc04ab8154986abbcb63bdee5de6ed2ba7e1713527e33df923dd", [:rebar3], [], "hexpm", "78fe127f5a4f5f919d6ea5a2a671827bd53eb9d37e5b4128c0ad3df99856c2e0"},
"quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "d736bfa7444112eb840027bb887832a0e403a4a3437f48028c3b29a2dbbd2543"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},

View file

@ -1149,18 +1149,6 @@ defmodule Pleroma.NotificationTest do
assert Notification.for_user(user) == []
end
test "it returns notifications for domain-blocked but followed user" do
user = insert(:user)
blocked = insert(:user, ap_id: "http://some-domain.com")
{:ok, user} = User.block_domain(user, "some-domain.com")
{:ok, _, _} = User.follow(user, blocked)
{:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
assert length(Notification.for_user(user)) == 1
end
test "it doesn't return notifications for muted thread", %{user: user} do
another_user = insert(:user)

View file

@ -311,7 +311,7 @@ defmodule Pleroma.UserTest do
describe "unfollow/2" do
setup do: clear_config([:instance, :external_user_synchronization])
test "unfollow with syncronizes external user" do
test "unfollow with synchronizes external user" do
clear_config([:instance, :external_user_synchronization], true)
followed =
@ -2260,7 +2260,7 @@ defmodule Pleroma.UserTest do
assert other_user.follower_count == 1
end
test "syncronizes the counters with the remote instance for the followed when enabled" do
test "synchronizes the counters with the remote instance for the followed when enabled" do
clear_config([:instance, :external_user_synchronization], false)
user = insert(:user)
@ -2282,7 +2282,7 @@ defmodule Pleroma.UserTest do
assert other_user.follower_count == 437
end
test "syncronizes the counters with the remote instance for the follower when enabled" do
test "synchronizes the counters with the remote instance for the follower when enabled" do
clear_config([:instance, :external_user_synchronization], false)
user = insert(:user)

View file

@ -559,6 +559,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
conn =
conn
|> assign(:valid_signature, true)
|> put_req_header(
"signature",
"keyId=\"http://mastodon.example.org/users/admin/main-key\""
)
|> put_req_header("content-type", "application/activity+json")
|> post("/inbox", data)
@ -589,6 +593,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
conn =
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{user.ap_id}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/inbox", data)
@ -602,12 +607,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
sender_url = data["actor"]
sender = insert(:user, ap_id: data["actor"])
Instances.set_consistently_unreachable(sender_url)
refute Instances.reachable?(sender_url)
conn =
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{sender.ap_id}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/inbox", data)
@ -632,6 +640,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
assert "ok" ==
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{followed_relay.ap_id}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/inbox", accept)
|> json_response(200)
@ -698,6 +707,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
actor = "https://example.com/users/lain"
insert(:user,
ap_id: actor,
featured_address: "https://example.com/users/lain/collections/featured"
)
Tesla.Mock.mock(fn
%{
method: :get,
@ -743,6 +757,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
assert "ok" ==
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{actor}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/inbox", data)
|> json_response(200)
@ -750,6 +765,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
assert Activity.get_by_ap_id(data["id"])
user = User.get_cached_by_ap_id(data["actor"])
assert user.pinned_objects[data["object"]]
data = %{
@ -764,6 +780,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
assert "ok" ==
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{actor}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/inbox", data)
|> json_response(200)
@ -790,6 +807,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
actor = "https://example.com/users/lain"
sender =
insert(:user,
ap_id: actor,
featured_address: "https://example.com/users/lain/collections/featured"
)
Tesla.Mock.mock(fn
%{
method: :get,
@ -844,6 +867,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
assert "ok" ==
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{sender.ap_id}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/inbox", data)
|> json_response(200)
@ -863,6 +887,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
assert "ok" ==
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{actor}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/inbox", data)
|> json_response(200)
@ -894,6 +919,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
conn =
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{data["actor"]}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/users/#{user.nickname}/inbox", data)
@ -915,6 +941,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
conn =
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{data["actor"]}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/users/#{user.nickname}/inbox", data)
@ -936,6 +963,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
conn =
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{data["actor"]}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/users/#{user.nickname}/inbox", data)
@ -960,6 +988,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
conn =
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{data["actor"]}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/users/#{user.nickname}/inbox", data)
@ -987,6 +1016,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
conn =
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{announcer.ap_id}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/users/#{user.nickname}/inbox", data)
@ -1017,6 +1047,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
conn =
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{actor.ap_id}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/users/#{recipient.nickname}/inbox", data)
@ -1063,6 +1094,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
conn =
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{data["actor"]}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/users/#{user.nickname}/inbox", data)
@ -1101,6 +1133,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{actor.ap_id}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/users/#{recipient.nickname}/inbox", data)
|> json_response(200)
@ -1193,6 +1226,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{actor.ap_id}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/users/#{reported_user.nickname}/inbox", data)
|> json_response(200)
@ -1248,6 +1282,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
conn
|> assign(:valid_signature, true)
|> put_req_header("signature", "keyId=\"#{remote_actor}/main-key\"")
|> put_req_header("content-type", "application/activity+json")
|> post("/users/#{reported_user.nickname}/inbox", data)
|> json_response(200)

View file

@ -1632,7 +1632,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
end
describe "fetch_follow_information_for_user" do
test "syncronizes following/followers counters" do
test "synchronizes following/followers counters" do
user =
insert(:user,
local: false,

View file

@ -1921,4 +1921,48 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|> get("/api/v1/accounts/relationships?id=#{other_user.id}")
|> json_response_and_validate_schema(200)
end
describe "remove from followers" do
setup do: oauth_access(["follow"])
test "removing user from followers", %{conn: conn, user: user} do
%{id: other_user_id} = other_user = insert(:user)
CommonAPI.follow(other_user, user)
assert %{"id" => ^other_user_id, "followed_by" => false} =
conn
|> post("/api/v1/accounts/#{other_user_id}/remove_from_followers")
|> json_response_and_validate_schema(200)
refute User.following?(other_user, user)
end
test "removing remote user from followers", %{conn: conn, user: user} do
%{id: other_user_id} = other_user = insert(:user, local: false)
CommonAPI.follow(other_user, user)
assert User.following?(other_user, user)
assert %{"id" => ^other_user_id, "followed_by" => false} =
conn
|> post("/api/v1/accounts/#{other_user_id}/remove_from_followers")
|> json_response_and_validate_schema(200)
refute User.following?(other_user, user)
end
test "removing user from followers errors", %{user: user, conn: conn} do
# self remove
conn_res = post(conn, "/api/v1/accounts/#{user.id}/remove_from_followers")
assert %{"error" => "Can not unfollow yourself"} =
json_response_and_validate_schema(conn_res, 400)
# remove non existing user
conn_res = post(conn, "/api/v1/accounts/doesntexist/remove_from_followers")
assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
end
end
end

View file

@ -9,6 +9,8 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlugTest do
import Tesla.Mock
import Plug.Conn
import Pleroma.Tests.Helpers, only: [clear_config: 2]
setup do
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
@ -47,6 +49,26 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlugTest do
assert %{valid_signature: false} == conn.assigns
end
test "it considers a mapped identity to be invalid when the associated instance is blocked" do
clear_config([:activitypub, :authorized_fetch_mode], true)
clear_config([:mrf_simple, :reject], [
{"mastodon.example.org", "anime is banned"}
])
on_exit(fn ->
Pleroma.Config.put([:activitypub, :authorized_fetch_mode], false)
Pleroma.Config.put([:mrf_simple, :reject], [])
end)
conn =
build_conn(:post, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"})
|> set_signature("http://mastodon.example.org/users/admin")
|> MappedSignatureToIdentityPlug.call(%{})
assert %{valid_signature: false} == conn.assigns
end
@tag skip: "known breakage; the testsuite presently depends on it"
test "it considers a mapped identity to be invalid when the identity cannot be found" do
conn =