Merge branch 'develop' into paradedb-search-backend
This commit is contained in:
commit
7e97216b52
111 changed files with 3801 additions and 350 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -56,6 +56,9 @@ pleroma.iml
|
|||
# asdf
|
||||
.tool-versions
|
||||
|
||||
# mise
|
||||
mise.toml
|
||||
|
||||
# Editor temp files
|
||||
*~
|
||||
*#
|
||||
|
|
|
|||
19
.woodpecker/changelog.yaml
Normal file
19
.woodpecker/changelog.yaml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
when:
|
||||
- event: pull_request
|
||||
|
||||
labels:
|
||||
platform: linux/amd64
|
||||
|
||||
variables:
|
||||
script_file_entrypoint: &script_file_entrypoint
|
||||
- /bin/sh
|
||||
- -c
|
||||
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
|
||||
|
||||
steps:
|
||||
check-changelog:
|
||||
image: docker.io/alpine:3.23
|
||||
entrypoint: *script_file_entrypoint
|
||||
commands:
|
||||
- apk add --no-cache git
|
||||
- sh ./tools/check-changelog
|
||||
60
.woodpecker/docker-combine.yaml
Normal file
60
.woodpecker/docker-combine.yaml
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
when:
|
||||
- event: push
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ]
|
||||
- event: tag
|
||||
- event: manual
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
- event: manual
|
||||
branch: stable
|
||||
|
||||
depends_on:
|
||||
- docker
|
||||
|
||||
skip_clone: true
|
||||
|
||||
labels:
|
||||
platform: linux/amd64
|
||||
|
||||
steps:
|
||||
docker-develop-combine:
|
||||
image: git.fluffytail.org/phnt/wpc-docker-tagger:latest
|
||||
when:
|
||||
- event: push
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
- event: manual
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
settings: &docker_settings
|
||||
registry: "git.pleroma.social"
|
||||
image: "pleroma/pleroma"
|
||||
architectures: [amd64, arm64]
|
||||
tags:
|
||||
- latest
|
||||
- develop
|
||||
- ${CI_COMMIT_SHA:0:8}
|
||||
username:
|
||||
from_secret: pleroma-ci-user
|
||||
password:
|
||||
from_secret: pleroma-ci-password
|
||||
|
||||
docker-stable-combine:
|
||||
image: git.fluffytail.org/phnt/wpc-docker-tagger:latest
|
||||
when:
|
||||
- evaluate: 'CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""'
|
||||
settings:
|
||||
<<: *docker_settings
|
||||
tags: &stable_docker_tags
|
||||
- latest
|
||||
- stable
|
||||
- ${CI_COMMIT_SHA:0:8}
|
||||
|
||||
docker-stable-tag-combine:
|
||||
image: git.fluffytail.org/phnt/wpc-docker-tagger:latest
|
||||
when:
|
||||
- event: tag
|
||||
- evaluate: 'CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""'
|
||||
settings:
|
||||
<<: *docker_settings
|
||||
tags:
|
||||
- <<: *stable_docker_tags
|
||||
- ${CI_COMMIT_TAG}
|
||||
96
.woodpecker/docker.yaml
Normal file
96
.woodpecker/docker.yaml
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
when:
|
||||
- event: push
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ]
|
||||
- event: tag
|
||||
- event: manual
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
- event: manual
|
||||
branch: stable
|
||||
|
||||
matrix:
|
||||
platform:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
|
||||
# This is needed for the when clauses below.
|
||||
labels:
|
||||
platform: ${platform}
|
||||
memory: 'high'
|
||||
|
||||
variables:
|
||||
docker_variables: &docker_variables
|
||||
repo: pleroma/pleroma
|
||||
registry: git.pleroma.social
|
||||
username:
|
||||
from_secret: pleroma-ci-user
|
||||
password:
|
||||
from_secret: pleroma-ci-password
|
||||
kaniko_image: &kaniko_image woodpeckerci/plugin-kaniko:2.3.1
|
||||
|
||||
steps:
|
||||
docker-develop-amd64:
|
||||
image: woodpeckerci/plugin-kaniko:2.3.1
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
settings:
|
||||
<<: *docker_variables
|
||||
tags:
|
||||
- latest-amd64
|
||||
- develop-amd64
|
||||
- ${CI_COMMIT_SHA:0:8}-amd64
|
||||
|
||||
docker-develop-arm64:
|
||||
image: woodpeckerci/plugin-kaniko:2.3.1
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
settings:
|
||||
<<: *docker_variables
|
||||
tags:
|
||||
- latest-arm64
|
||||
- develop-arm64
|
||||
- ${CI_COMMIT_SHA:0:8}-arm64
|
||||
|
||||
docker-stable-amd64:
|
||||
image: *kaniko_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""'
|
||||
settings:
|
||||
<<: *docker_variables
|
||||
tags: &amd64_tags
|
||||
- latest-amd64
|
||||
- stable-amd64
|
||||
- ${CI_COMMIT_SHA:0:8}-amd64
|
||||
|
||||
docker-stable-tag-amd64:
|
||||
image: *kaniko_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""'
|
||||
settings:
|
||||
<<: *docker_variables
|
||||
tags:
|
||||
- <<: *amd64_tags
|
||||
- ${CI_COMMIT_TAG}-amd64
|
||||
|
||||
docker-stable-arm64:
|
||||
image: *kaniko_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""'
|
||||
settings:
|
||||
<<: *docker_variables
|
||||
tags: &arm64_tags
|
||||
- latest-arm64
|
||||
- stable-arm64
|
||||
- ${CI_COMMIT_SHA:0:8}-arm64
|
||||
|
||||
docker-stable-tag-arm64:
|
||||
image: *kaniko_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""'
|
||||
settings:
|
||||
<<: *docker_variables
|
||||
tags:
|
||||
- <<: *arm64_tags
|
||||
- ${CI_COMMIT_TAG}-arm64
|
||||
77
.woodpecker/lint.yaml
Normal file
77
.woodpecker/lint.yaml
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
when:
|
||||
- event: pull_request
|
||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||
- event: push
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||
|
||||
labels:
|
||||
platform: linux/amd64
|
||||
|
||||
variables:
|
||||
script_file_entrypoint: &script_file_entrypoint
|
||||
- /bin/sh
|
||||
- -c
|
||||
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
|
||||
|
||||
steps:
|
||||
mix-format:
|
||||
image: &elixir-image
|
||||
docker.io/elixir:1.15-alpine
|
||||
entrypoint: *script_file_entrypoint
|
||||
failure: ignore
|
||||
commands:
|
||||
- |
|
||||
if ! mix format --check-formatted; then
|
||||
touch fail.stamp
|
||||
exit 1
|
||||
fi
|
||||
|
||||
credo:
|
||||
image: *elixir-image
|
||||
entrypoint: *script_file_entrypoint
|
||||
failure: ignore
|
||||
environment:
|
||||
MIX_ENV: test
|
||||
commands:
|
||||
- apk add --no-cache build-base cmake exiftool ffmpeg file-dev git openssl
|
||||
- adduser -D -h /home/testuser testuser
|
||||
- mkdir -p /home/testuser/.mix /home/testuser/.hex
|
||||
- chown -R testuser:testuser . /home/testuser
|
||||
- su testuser -c "HOME=/home/testuser mix local.hex --force"
|
||||
- su testuser -c "HOME=/home/testuser mix local.rebar --force"
|
||||
- su testuser -c "HOME=/home/testuser mix deps.get"
|
||||
- |
|
||||
if ! su testuser -c "HOME=/home/testuser mix analyze"; then
|
||||
touch fail.stamp
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# cycles:
|
||||
# image: *elixir-image
|
||||
# failure: ignore
|
||||
# commands:
|
||||
# - apk add --no-cache build-base cmake exiftool ffmpeg file-dev git openssl
|
||||
# - adduser -D -h /home/testuser testuser
|
||||
# - mkdir -p /home/testuser/.mix /home/testuser/.hex
|
||||
# - chown -R testuser:testuser . /home/testuser
|
||||
# - su testuser -c "HOME=/home/testuser mix local.hex --force"
|
||||
# - su testuser -c "HOME=/home/testuser mix local.rebar --force"
|
||||
# - su testuser -c "HOME=/home/testuser mix compile"
|
||||
# - |
|
||||
# if ! su testuser -c "HOME=/home/testuser mix xref graph --format cycles --label compile | awk '{print $0} END{exit ($0 != \"No cycles found\")}'"; then
|
||||
# touch fail.stamp
|
||||
# exit 1
|
||||
# fi
|
||||
|
||||
ensure-status:
|
||||
image: *elixir-image
|
||||
entrypoint: *script_file_entrypoint
|
||||
commands: |
|
||||
if test -f fail.stamp; then
|
||||
echo "One or more previous steps fails. Failing workflow..."
|
||||
exit 1
|
||||
else
|
||||
echo "All steps passed"
|
||||
exit 0
|
||||
fi
|
||||
292
.woodpecker/otp-musl.yaml
Normal file
292
.woodpecker/otp-musl.yaml
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
when:
|
||||
- event: push
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||
- event: tag
|
||||
- event: manual
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
- event: manual
|
||||
branch: stable
|
||||
|
||||
matrix:
|
||||
platform:
|
||||
- linux/amd64
|
||||
- linux/arm
|
||||
- linux/arm64
|
||||
|
||||
# This is needed for the when clauses below.
|
||||
# When the platform clause is fixed, this might not be needed anymore
|
||||
labels:
|
||||
platform: ${platform}
|
||||
|
||||
variables:
|
||||
script_file_entrypoint: &script_file_entrypoint
|
||||
- /bin/sh
|
||||
- -c
|
||||
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
|
||||
build_cmds: &build_cmds
|
||||
- apk add git build-base cmake file-dev openssl vips-dev zip
|
||||
- echo "import Config" > config/prod.secret.exs
|
||||
- mix local.hex --force
|
||||
- mix local.rebar --force
|
||||
- mix deps.get --only prod
|
||||
- mkdir release
|
||||
- export PLEROMA_BUILD_BRANCH=${CI_COMMIT_BRANCH}
|
||||
- mix release --path release
|
||||
build_image_amd64: &build_image_amd64 docker.io/hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-alpine-3.22.1
|
||||
build_image_arm: &build_image_arm docker.io/arm32v7/elixir:1.17.3-alpine
|
||||
build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-alpine-3.22.1
|
||||
artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0
|
||||
artifacts_uploader_settings: &artifacts_uploader_settings
|
||||
user:
|
||||
from_secret: pleroma-ci-user
|
||||
password:
|
||||
from_secret: pleroma-ci-password
|
||||
owner: 'pleroma'
|
||||
env: &env
|
||||
MIX_ENV: prod
|
||||
VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS
|
||||
|
||||
steps:
|
||||
otp-develop-amd64-musl:
|
||||
image: *build_image_amd64
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
environment: *env
|
||||
commands: &amd64_build
|
||||
- <<: *build_cmds
|
||||
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip release
|
||||
|
||||
otp-stable-amd64-musl:
|
||||
image: *build_image_amd64
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
|
||||
environment: *env
|
||||
commands: *amd64_build
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
otp-stable-tag-amd64-musl:
|
||||
image: *build_image_amd64
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||
environment: *env
|
||||
commands:
|
||||
- <<: *build_cmds
|
||||
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip release
|
||||
|
||||
otp-develop-arm-musl:
|
||||
image: *build_image_arm
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
environment: *env
|
||||
commands: &arm_build
|
||||
- <<: *build_cmds
|
||||
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip release
|
||||
|
||||
otp-stable-arm-musl:
|
||||
image: *build_image_arm
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
|
||||
environment: *env
|
||||
commands: *arm_build
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
otp-stable-tag-arm-musl:
|
||||
image: *build_image_arm
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
|
||||
environment: *env
|
||||
commands:
|
||||
- <<: *build_cmds
|
||||
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip release
|
||||
|
||||
otp-develop-arm64-musl:
|
||||
image: *build_image_arm64
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
environment: *env
|
||||
commands: &arm64_build
|
||||
- <<: *build_cmds
|
||||
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip release
|
||||
|
||||
otp-stable-arm64-musl:
|
||||
image: *build_image_arm64
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
|
||||
environment: *env
|
||||
commands: *arm64_build
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
otp-stable-tag-arm64-musl:
|
||||
image: *build_image_arm64
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
|
||||
environment: *env
|
||||
commands:
|
||||
- <<: *build_cmds
|
||||
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip release
|
||||
|
||||
upload-artifacts-amd64-musl:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64-musl
|
||||
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl
|
||||
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
|
||||
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
|
||||
update: 'true'
|
||||
|
||||
upload-latest-amd64-musl:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64-musl
|
||||
package_version: latest
|
||||
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
|
||||
file_name: pleroma.zip
|
||||
update: 'true'
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
upload-artifacts-tag-amd64-musl:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-stable-amd64-musl
|
||||
package_version: stable-${CI_COMMIT_SHA:0:8}-amd64-musl
|
||||
file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
|
||||
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
|
||||
update: 'true'
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
upload-latest-tag-amd64-musl:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-stable-amd64-musl
|
||||
package_version: latest
|
||||
file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
|
||||
file_name: pleroma.zip
|
||||
update: 'true'
|
||||
|
||||
upload-artifacts-arm-musl:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm-musl
|
||||
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl
|
||||
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip
|
||||
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip
|
||||
update: 'true'
|
||||
|
||||
upload-latest-arm-musl:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm-musl
|
||||
package_version: latest
|
||||
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip
|
||||
file_name: pleroma.zip
|
||||
update: 'true'
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
upload-artifacts-tag-arm-musl:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-stable-arm-musl
|
||||
package_version: stable-${CI_COMMIT_SHA:0:8}-arm-musl
|
||||
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip
|
||||
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip
|
||||
update: 'true'
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
upload-latest-tag-arm-musl:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-stable-arm-musl
|
||||
package_version: latest
|
||||
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip
|
||||
file_name: pleroma.zip
|
||||
update: 'true'
|
||||
|
||||
upload-artifacts-arm64-musl:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64-musl
|
||||
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl
|
||||
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
|
||||
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
|
||||
update: 'true'
|
||||
|
||||
upload-latest-arm64-musl:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64-musl
|
||||
package_version: latest
|
||||
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
|
||||
file_name: pleroma.zip
|
||||
update: 'true'
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
upload-artifacts-tag-arm64-musl:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-stable-arm64-musl
|
||||
package_version: stable-${CI_COMMIT_SHA:0:8}-arm64-musl
|
||||
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
|
||||
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
|
||||
update: 'true'
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
upload-latest-tag-arm64-musl:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-stable-arm64-musl
|
||||
package_version: latest
|
||||
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
|
||||
file_name: pleroma.zip
|
||||
update: 'true'
|
||||
293
.woodpecker/otp.yaml
Normal file
293
.woodpecker/otp.yaml
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
when:
|
||||
- event: push
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||
- event: tag
|
||||
- event: manual
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
- event: manual
|
||||
branch: stable
|
||||
|
||||
matrix:
|
||||
platform:
|
||||
- linux/amd64
|
||||
- linux/arm
|
||||
- linux/arm64
|
||||
|
||||
# This is needed for the when clauses below.
|
||||
# When the platform clause is fixed, this might not be needed anymore
|
||||
labels:
|
||||
platform: ${platform}
|
||||
|
||||
variables:
|
||||
script_file_entrypoint: &script_file_entrypoint
|
||||
- /bin/sh
|
||||
- -c
|
||||
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
|
||||
build_cmds: &build_cmds
|
||||
- apt-get update && apt-get install -y cmake libmagic-dev libvips-dev erlang-dev git build-essential zip
|
||||
- echo "import Config" > config/prod.secret.exs
|
||||
- mix local.hex --force
|
||||
- mix local.rebar --force
|
||||
- mix deps.get --only prod
|
||||
- mkdir release
|
||||
- export PLEROMA_BUILD_BRANCH=${CI_COMMIT_BRANCH}
|
||||
- mix release --path release
|
||||
build_image_amd64: &build_image_amd64 docker.io/hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716
|
||||
build_image_arm: &build_image_arm docker.io/arm32v7/elixir:1.17.3
|
||||
build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716
|
||||
artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0
|
||||
artifacts_uploader_settings: &artifacts_uploader_settings
|
||||
user:
|
||||
from_secret: pleroma-ci-user
|
||||
password:
|
||||
from_secret: pleroma-ci-password
|
||||
owner: 'pleroma'
|
||||
env: &env
|
||||
MIX_ENV: prod
|
||||
VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
|
||||
steps:
|
||||
otp-develop-amd64:
|
||||
image: *build_image_amd64
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
environment: *env
|
||||
commands: &amd64_build
|
||||
- <<: *build_cmds
|
||||
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip release
|
||||
|
||||
otp-stable-amd64:
|
||||
image: *build_image_amd64
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
|
||||
environment: *env
|
||||
commands: *amd64_build
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
otp-stable-tag-amd64:
|
||||
image: *build_image_amd64
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||
environment: *env
|
||||
commands:
|
||||
- <<: *build_cmds
|
||||
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-amd64.zip release
|
||||
|
||||
otp-develop-arm:
|
||||
image: *build_image_arm
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
environment: *env
|
||||
commands: &arm_build
|
||||
- <<: *build_cmds
|
||||
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip release
|
||||
|
||||
otp-stable-arm:
|
||||
image: *build_image_arm
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
|
||||
environment: *env
|
||||
commands: *arm_build
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
otp-stable-tag-arm:
|
||||
image: *build_image_arm
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
|
||||
environment: *env
|
||||
commands:
|
||||
- <<: *build_cmds
|
||||
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm.zip release
|
||||
|
||||
otp-develop-arm64:
|
||||
image: *build_image_arm64
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
environment: *env
|
||||
commands: &arm64_build
|
||||
- <<: *build_cmds
|
||||
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip release
|
||||
|
||||
otp-stable-arm64:
|
||||
image: *build_image_arm64
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
|
||||
environment: *env
|
||||
commands: *arm64_build
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
otp-stable-tag-arm64:
|
||||
image: *build_image_arm64
|
||||
entrypoint: *script_file_entrypoint
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
|
||||
environment: *env
|
||||
commands:
|
||||
- <<: *build_cmds
|
||||
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm64.zip release
|
||||
|
||||
upload-artifacts-amd64:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64
|
||||
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64
|
||||
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip
|
||||
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip
|
||||
update: 'true'
|
||||
|
||||
upload-latest-amd64:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64
|
||||
package_version: latest
|
||||
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip
|
||||
file_name: pleroma.zip
|
||||
update: 'true'
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
upload-artifacts-tag-amd64:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-stable-amd64
|
||||
package_version: stable-${CI_COMMIT_SHA:0:8}-amd64
|
||||
file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64.zip
|
||||
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-amd64.zip
|
||||
update: 'true'
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
upload-latest-tag-amd64:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-stable-amd64
|
||||
package_version: latest
|
||||
file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64.zip
|
||||
file_name: pleroma.zip
|
||||
update: 'true'
|
||||
|
||||
upload-artifacts-arm:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm
|
||||
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm
|
||||
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip
|
||||
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip
|
||||
update: 'true'
|
||||
|
||||
upload-latest-arm:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm
|
||||
package_version: latest
|
||||
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip
|
||||
file_name: pleroma.zip
|
||||
update: 'true'
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
upload-artifacts-tag-arm:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-stable-arm
|
||||
package_version: stable-${CI_COMMIT_SHA:0:8}-arm
|
||||
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm.zip
|
||||
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm.zip
|
||||
update: 'true'
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
upload-latest-tag-arm:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-stable-arm
|
||||
package_version: latest
|
||||
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm.zip
|
||||
file_name: pleroma.zip
|
||||
update: 'true'
|
||||
|
||||
upload-artifacts-arm64:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64
|
||||
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64
|
||||
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip
|
||||
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip
|
||||
update: 'true'
|
||||
|
||||
upload-latest-arm64:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64
|
||||
package_version: latest
|
||||
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip
|
||||
file_name: pleroma.zip
|
||||
update: 'true'
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
upload-artifacts-tag-arm64:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-stable-arm64
|
||||
package_version: stable-${CI_COMMIT_SHA:0:8}-arm64
|
||||
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64.zip
|
||||
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm64.zip
|
||||
update: 'true'
|
||||
|
||||
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||
upload-latest-tag-arm64:
|
||||
image: *artifacts_uploader_image
|
||||
when:
|
||||
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
|
||||
settings:
|
||||
<<: *artifacts_uploader_settings
|
||||
package_name: pleroma-otp-stable-arm64
|
||||
package_version: latest
|
||||
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64.zip
|
||||
file_name: pleroma.zip
|
||||
update: 'true'
|
||||
|
|
@ -1,10 +1,26 @@
|
|||
when:
|
||||
- event:
|
||||
- pull_request
|
||||
- event: pull_request
|
||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||
- event: push
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||
|
||||
depends_on:
|
||||
- lint
|
||||
|
||||
labels:
|
||||
platform: linux/amd64
|
||||
|
||||
variables:
|
||||
script_file_entrypoint: &script_file_entrypoint
|
||||
- /bin/sh
|
||||
- -c
|
||||
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
|
||||
|
||||
steps:
|
||||
test:
|
||||
unit-testing-elixir-1.15:
|
||||
image: elixir:1.15-alpine
|
||||
entrypoint: *script_file_entrypoint
|
||||
environment:
|
||||
MIX_ENV: test
|
||||
DB_HOST: postgres
|
||||
|
|
@ -17,7 +33,7 @@ steps:
|
|||
- su testuser -c "HOME=/home/testuser mix local.hex --force"
|
||||
- su testuser -c "HOME=/home/testuser mix local.rebar --force"
|
||||
- su testuser -c "HOME=/home/testuser mix deps.get"
|
||||
- su testuser -c "HOME=/home/testuser mix test"
|
||||
- su testuser -c "HOME=/home/testuser mix pleroma.test_runner --preload-modules"
|
||||
|
||||
services:
|
||||
postgres:
|
||||
44
.woodpecker/unit-testing-elixir-1.18.yaml
Normal file
44
.woodpecker/unit-testing-elixir-1.18.yaml
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
when:
|
||||
- event: pull_request
|
||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||
- event: push
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||
|
||||
depends_on:
|
||||
- lint
|
||||
|
||||
labels:
|
||||
platform: linux/amd64
|
||||
|
||||
variables:
|
||||
script_file_entrypoint: &script_file_entrypoint
|
||||
- /bin/sh
|
||||
- -c
|
||||
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
|
||||
|
||||
steps:
|
||||
unit-testing-elixir-1.18:
|
||||
image: elixir:1.18-otp-27-alpine
|
||||
entrypoint: *script_file_entrypoint
|
||||
environment:
|
||||
MIX_ENV: test
|
||||
DB_HOST: postgres
|
||||
DB_PORT: 5432
|
||||
commands:
|
||||
- apk add --no-cache build-base cmake exiftool ffmpeg file-dev git openssl
|
||||
- adduser -D -h /home/testuser testuser
|
||||
- mkdir -p /home/testuser/.mix /home/testuser/.hex
|
||||
- chown -R testuser:testuser . /home/testuser
|
||||
- su testuser -c "HOME=/home/testuser mix local.hex --force"
|
||||
- su testuser -c "HOME=/home/testuser mix local.rebar --force"
|
||||
- su testuser -c "HOME=/home/testuser mix deps.get"
|
||||
- su testuser -c "HOME=/home/testuser mix pleroma.test_runner --preload-modules"
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:13-alpine
|
||||
environment:
|
||||
POSTGRES_DB: pleroma_test
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
54
CHANGELOG.md
54
CHANGELOG.md
|
|
@ -4,6 +4,60 @@ 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/).
|
||||
|
||||
## 2.10.2
|
||||
|
||||
### Security
|
||||
|
||||
- ActivityPub: Fixed failed-signature inbox retry handling and signer identity checks to prevent spoofed remote activities from being processed
|
||||
|
||||
## 2.10.1
|
||||
|
||||
### Changed
|
||||
|
||||
- Move avatar_description and header_description fields to the account object
|
||||
- Update Bandit to 1.10.4
|
||||
- No-op code correctness improvements detected by Elixir 1.19 compiler
|
||||
- Downgrade Hackney to 1.20.1
|
||||
- Use a custom redirect handler to ensure MediaProxy redirects are followed with Hackney
|
||||
- Update Hackney, the default HTTP client, to the latest release which supports Happy Eyeballs for improved IPv6 federation
|
||||
- Paginate follow requests
|
||||
- Moved Phoenix LiveDashboard to /pleroma/live_dashboard
|
||||
- Add mute/block expiry to the relationship object
|
||||
- Filter indexable activities before inserting indexing jobs into the queue.
|
||||
|
||||
### Added
|
||||
|
||||
- Allow assigning users to reports
|
||||
- Allow fine-grained announce visibilities
|
||||
- Add immutable tag on cache-control header for several endpoints that's serving the same exact things.
|
||||
- Add reasonable defaults for :database_config_whitelist
|
||||
- Support lists `exclusive` param
|
||||
- Add v1/instance/domain_blocks endpoint
|
||||
- Add /api/v2/instance profile fields limits info used by Mastodon
|
||||
- Added Oban Web dashboard located at /pleroma/oban
|
||||
- Add instructions on how to run a release in docker, to make it easier to run on older distros.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix the daily email digest job which was not executing
|
||||
- Encode custom emoji URLs in EmojiReact activity tags.
|
||||
- Gopher: Fix Ranch listener not being stopped properly on Pleroma restart when database configuration is enabled
|
||||
- Fix fetching Hubzilla Actors with alsoKnownAs as string
|
||||
- Fix /phoenix/live_dashboard redirect not working when user added a path segment
|
||||
- Fix 404 error codes for missing static files
|
||||
- Fix OAuth app registration to accept `redirect_uris` as an array of strings (RFC 7591), while keeping backwards compatibility with string input.
|
||||
- Correct old migrations for expiring activities and user access tokens.
|
||||
- Federate `votersCount` correctly
|
||||
- DB prune: Check if user follows hashtag with no objects before deletion
|
||||
- Stop the rate limiter from crashing when run with wrong settings.
|
||||
- Restore embeds route
|
||||
- ReverseProxy: Recursively follow redirects until redirect_limit is reached
|
||||
- Fix compilation with vips-8.18.0 with bumping to vix 0.36.0
|
||||
|
||||
### Removed
|
||||
|
||||
- Docs: Removed outdated, incorrect, unmaintained and inappropriate installation documentation (Arch, NetBSD, NixOS)
|
||||
|
||||
## 2.10
|
||||
|
||||
### Security
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
Allow assigning users to reports
|
||||
|
|
@ -1 +0,0 @@
|
|||
Move avatar_description and header_description fields to the account object
|
||||
|
|
@ -1 +0,0 @@
|
|||
Various bookmark folders-related improvements
|
||||
|
|
@ -1 +0,0 @@
|
|||
Allow fine-grained announce visibilities
|
||||
|
|
@ -1 +0,0 @@
|
|||
Add immutable tag on cache-control header for several endpoints that's serving the same exact things.
|
||||
1
changelog.d/context-cleanup.skip
Normal file
1
changelog.d/context-cleanup.skip
Normal file
|
|
@ -0,0 +1 @@
|
|||
litepub-0.1.jsonld cleanup
|
||||
|
|
@ -1 +0,0 @@
|
|||
Add reasonable defaults for :database_config_whitelist
|
||||
|
|
@ -1 +0,0 @@
|
|||
No-op code correctness improvements detected by Elixir 1.19 compiler
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix the daily email digest job which was not executing
|
||||
|
|
@ -1 +0,0 @@
|
|||
Encode custom emoji URLs in EmojiReact activity tags.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Support lists `exclusive` param
|
||||
|
|
@ -1 +0,0 @@
|
|||
Gopher: Fix Ranch listener not being stopped properly on Pleroma restart when database configuration is enabled
|
||||
|
|
@ -1 +0,0 @@
|
|||
Use a custom redirect handler to ensure MediaProxy redirects are followed with Hackney
|
||||
|
|
@ -1 +0,0 @@
|
|||
Update Hackney, the default HTTP client, to the latest release which supports Happy Eyeballs for improved IPv6 federation
|
||||
1
changelog.d/http-signatures-0.1.3.fix
Normal file
1
changelog.d/http-signatures-0.1.3.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix compatibility with timestamped HTTP Signatures used by GoToSocial
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix fetching Hubzilla Actors with alsoKnownAs as string
|
||||
1
changelog.d/iceshrimpnet-reports-fix.fix
Normal file
1
changelog.d/iceshrimpnet-reports-fix.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Handle reports with just actor ap id as the object
|
||||
|
|
@ -1 +0,0 @@
|
|||
Docs: Removed outdated, incorrect, unmaintained and inappropriate installation documentation (Arch, NetBSD, NixOS)
|
||||
|
|
@ -1 +0,0 @@
|
|||
Add v1/instance/domain_blocks endpoint
|
||||
|
|
@ -1 +0,0 @@
|
|||
Add /api/v2/instance profile fields limits info used by Mastodon
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix /phoenix/live_dashboard redirect not working when user added a path segment
|
||||
1
changelog.d/mfm-backend.add
Normal file
1
changelog.d/mfm-backend.add
Normal file
|
|
@ -0,0 +1 @@
|
|||
Add backend support for Misskey Markdown (MFM) posts
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix 404 error codes for missing static files
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix OAuth app registration to accept `redirect_uris` as an array of strings (RFC 7591), while keeping backwards compatibility with string input.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Added Oban Web dashboard located at /pleroma/oban
|
||||
|
|
@ -1 +0,0 @@
|
|||
Correct old migrations for expiring activities and user access tokens.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Paginate follow requests
|
||||
|
|
@ -1 +0,0 @@
|
|||
Moved Phoenix LiveDashboard to /pleroma/live_dashboard
|
||||
1
changelog.d/pleroma-fe-link.fix
Normal file
1
changelog.d/pleroma-fe-link.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Updated Pleroma-FE build URL after Forgejo migration
|
||||
1
changelog.d/poll-voters-count-inflation.fix
Normal file
1
changelog.d/poll-voters-count-inflation.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix votersCount inflation when same voter picks multiple options
|
||||
|
|
@ -1 +0,0 @@
|
|||
Federate `votersCount` correctly
|
||||
|
|
@ -1 +0,0 @@
|
|||
DB prune: Check if user follows hashtag with no objects before deletion
|
||||
|
|
@ -1 +0,0 @@
|
|||
Stop the rate limiter from crashing when run with wrong settings.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Reduce the number of flaky tests by making them sync if they affect the global state, and silence noisy test output.
|
||||
1
changelog.d/reject-third-party-reports.fix
Normal file
1
changelog.d/reject-third-party-reports.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Reject incoming reports when both the reporter and reported account are remote
|
||||
|
|
@ -1 +0,0 @@
|
|||
Add mute/block expiry to the relationship object
|
||||
|
|
@ -1 +0,0 @@
|
|||
Add instructions on how to run a release in docker, to make it easier to run on older distros.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Restore embeds route
|
||||
|
|
@ -1 +0,0 @@
|
|||
ReverseProxy: Recursively follow redirects until redirect_limit is reached
|
||||
|
|
@ -1 +0,0 @@
|
|||
Filter indexable activities before inserting indexing jobs into the queue.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Update comment for prepare_object, rename prepare_outgoing
|
||||
1
changelog.d/user-search-sorting.change
Normal file
1
changelog.d/user-search-sorting.change
Normal file
|
|
@ -0,0 +1 @@
|
|||
Improve user search / autocompletion ordering.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Avoid code duplication in UserView
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix compilation with vips-8.18.0 with bumping to vix 0.36.0
|
||||
|
|
@ -203,7 +203,8 @@ config :pleroma, :instance,
|
|||
"text/plain",
|
||||
"text/html",
|
||||
"text/markdown",
|
||||
"text/bbcode"
|
||||
"text/bbcode",
|
||||
"text/x.misskeymarkdown"
|
||||
],
|
||||
autofollowed_nicknames: [],
|
||||
autofollowing_nicknames: [],
|
||||
|
|
@ -775,7 +776,7 @@ config :pleroma, :frontends,
|
|||
"name" => "pleroma-fe",
|
||||
"git" => "https://git.pleroma.social/pleroma/pleroma-fe",
|
||||
"build_url" =>
|
||||
"https://git.pleroma.social/pleroma/pleroma-fe/-/jobs/artifacts/${ref}/download?job=build",
|
||||
"https://git.pleroma.social/api/packages/pleroma/generic/pleroma-fe-builds/${ref}/latest.zip",
|
||||
"ref" => "develop"
|
||||
},
|
||||
"fedi-fe" => %{
|
||||
|
|
|
|||
|
|
@ -815,7 +815,8 @@ config :pleroma, :config_description, [
|
|||
"text/plain",
|
||||
"text/html",
|
||||
"text/markdown",
|
||||
"text/bbcode"
|
||||
"text/bbcode",
|
||||
"text/x.misskeymarkdown"
|
||||
]
|
||||
},
|
||||
%{
|
||||
|
|
@ -1394,7 +1395,13 @@ config :pleroma, :config_description, [
|
|||
label: "Post Content Type",
|
||||
type: {:dropdown, :atom},
|
||||
description: "Default post formatting option",
|
||||
suggestions: ["text/plain", "text/html", "text/markdown", "text/bbcode"]
|
||||
suggestions: [
|
||||
"text/plain",
|
||||
"text/html",
|
||||
"text/markdown",
|
||||
"text/bbcode",
|
||||
"text/x.misskeymarkdown"
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :redirectRootNoLogin,
|
||||
|
|
|
|||
|
|
@ -127,6 +127,13 @@ defmodule Pleroma.Formatter do
|
|||
Earmark.as_html!(text, %Earmark.Options{compact_output: true, smartypants: false})
|
||||
end
|
||||
|
||||
def markdown_to_html(text, opts) do
|
||||
Earmark.as_html!(
|
||||
text,
|
||||
%Earmark.Options{compact_output: true, smartypants: false} |> Map.merge(opts)
|
||||
)
|
||||
end
|
||||
|
||||
def html_escape({text, mentions, hashtags}, type) do
|
||||
{html_escape(text, type), mentions, hashtags}
|
||||
end
|
||||
|
|
@ -135,6 +142,10 @@ defmodule Pleroma.Formatter do
|
|||
HTML.filter_tags(text)
|
||||
end
|
||||
|
||||
def html_escape(text, "text/x.misskeymarkdown") do
|
||||
HTML.filter_tags(text)
|
||||
end
|
||||
|
||||
def html_escape(text, "text/plain") do
|
||||
Regex.split(@link_regex, text, include_captures: true)
|
||||
|> Enum.map_every(2, fn chunk ->
|
||||
|
|
|
|||
|
|
@ -75,8 +75,8 @@ defmodule Pleroma.Frontend do
|
|||
end
|
||||
|
||||
defp download_build(frontend_info, dest) do
|
||||
Logger.info("Downloading pre-built bundle for #{frontend_info["name"]}")
|
||||
url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"])
|
||||
Logger.info("Downloading pre-built bundle for #{frontend_info["name"]} from #{url}")
|
||||
|
||||
with {:ok, %{status: 200, body: zip_body}} <-
|
||||
Pleroma.HTTP.get(url, [], pool: :media, recv_timeout: 120_000) do
|
||||
|
|
|
|||
|
|
@ -372,13 +372,21 @@ defmodule Pleroma.Object do
|
|||
option
|
||||
end)
|
||||
|
||||
voters = [actor | object.data["voters"] || []] |> Enum.uniq()
|
||||
existing_voters = object.data["voters"] || []
|
||||
voters = [actor | existing_voters] |> Enum.uniq()
|
||||
new_voter? = actor not in existing_voters
|
||||
existing_voters_count = object.data["votersCount"]
|
||||
|
||||
voters_count =
|
||||
if Map.has_key?(object.data, "votersCount") do
|
||||
object.data["votersCount"] + 1
|
||||
else
|
||||
length(voters)
|
||||
cond do
|
||||
is_integer(existing_voters_count) and new_voter? ->
|
||||
existing_voters_count + 1
|
||||
|
||||
is_integer(existing_voters_count) ->
|
||||
existing_voters_count
|
||||
|
||||
true ->
|
||||
length(voters)
|
||||
end
|
||||
|
||||
data =
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@
|
|||
defmodule Pleroma.ReleaseTasks do
|
||||
@repo Pleroma.Repo
|
||||
|
||||
def run(args) do
|
||||
# TODO: Kept for some backwards compatibility with buggy pleroma_ctl,
|
||||
# if a mismatch between pleroma_ctl and Pleroma accidentaly happens.
|
||||
# Remove in the future.
|
||||
def run(args) when is_binary(args) do
|
||||
[task | args] = String.split(args)
|
||||
|
||||
case task do
|
||||
|
|
@ -16,6 +19,20 @@ defmodule Pleroma.ReleaseTasks do
|
|||
end
|
||||
end
|
||||
|
||||
# HACK: Script arguments need to be received as a list, otherwise (quoted) arguments with
|
||||
# whitespace will be broken. Previously the broken string form above was used,
|
||||
# escaping in the shell does not help.
|
||||
def run(args) when is_list(args) do
|
||||
[task | args] = args
|
||||
|
||||
case task do
|
||||
"migrate" -> migrate(args)
|
||||
"create" -> create()
|
||||
"rollback" -> rollback(args)
|
||||
task -> mix_task(task, args)
|
||||
end
|
||||
end
|
||||
|
||||
def find_module(task) do
|
||||
module_name =
|
||||
task
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ defmodule Pleroma.Signature do
|
|||
|> put_req_header("(request-target)", request_target)
|
||||
|> put_req_header("@request-target", request_target)
|
||||
|
||||
@http_signatures_impl.validate_conn(conn)
|
||||
@http_signatures_impl.validate_conn(conn) == true
|
||||
end
|
||||
|
||||
@spec validate_signature(Plug.Conn.t()) :: boolean()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.User.Search do
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators.Uri, as: UriType
|
||||
alias Pleroma.Instances.Instance
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.User
|
||||
|
||||
|
|
@ -88,12 +89,13 @@ defmodule Pleroma.User.Search do
|
|||
|> filter_invisible_users()
|
||||
|> filter_internal_users()
|
||||
|> filter_blocked_domains(for_user)
|
||||
|> filter_unreachable_users()
|
||||
|> fts_search(query_string)
|
||||
|> select_top_users(top_user_ids)
|
||||
|> trigram_rank(query_string)
|
||||
|> boost_search_rank(for_user, top_user_ids)
|
||||
|> subquery()
|
||||
|> order_by(desc: :search_rank)
|
||||
|> order_by_search_rank(for_user)
|
||||
|> maybe_restrict_local(for_user)
|
||||
|> maybe_restrict_accepting_chat_messages(capabilities)
|
||||
|> filter_deactivated_users()
|
||||
|
|
@ -196,6 +198,14 @@ defmodule Pleroma.User.Search do
|
|||
|
||||
defp filter_blocked_domains(query, _), do: query
|
||||
|
||||
defp filter_unreachable_users(query) do
|
||||
from(u in query,
|
||||
left_join: i in Instance,
|
||||
on: i.host == fragment("substring(? from '.*://([^/]*)')", u.ap_id),
|
||||
where: is_nil(i.unreachable_since)
|
||||
)
|
||||
end
|
||||
|
||||
defp maybe_resolve(true, user, query) do
|
||||
case {limit(), user} do
|
||||
{:all, _} -> :noop
|
||||
|
|
@ -236,6 +246,16 @@ defmodule Pleroma.User.Search do
|
|||
|
||||
from(u in subquery(query),
|
||||
select_merge: %{
|
||||
search_type:
|
||||
fragment(
|
||||
"""
|
||||
CASE WHEN (?) THEN 2
|
||||
WHEN (?) THEN 1
|
||||
ELSE 0 END
|
||||
""",
|
||||
u.id in ^top_user_ids,
|
||||
u.id in ^friends_ids or u.id in ^followers_ids
|
||||
),
|
||||
search_rank:
|
||||
fragment(
|
||||
"""
|
||||
|
|
@ -261,6 +281,14 @@ defmodule Pleroma.User.Search do
|
|||
defp boost_search_rank(query, _for_user, top_user_ids) do
|
||||
from(u in subquery(query),
|
||||
select_merge: %{
|
||||
search_type:
|
||||
fragment(
|
||||
"""
|
||||
CASE WHEN (?) THEN 2
|
||||
ELSE 0 END
|
||||
""",
|
||||
u.id in ^top_user_ids
|
||||
),
|
||||
search_rank:
|
||||
fragment(
|
||||
"""
|
||||
|
|
@ -273,4 +301,22 @@ defmodule Pleroma.User.Search do
|
|||
}
|
||||
)
|
||||
end
|
||||
|
||||
defp order_by_search_rank(query, %User{}) do
|
||||
order_by(
|
||||
query,
|
||||
[u],
|
||||
desc: u.search_type,
|
||||
desc_nulls_last:
|
||||
fragment(
|
||||
"CASE WHEN ? = 1 THEN COALESCE(?, ?) ELSE NULL END",
|
||||
u.search_type,
|
||||
u.last_status_at,
|
||||
u.last_active_at
|
||||
),
|
||||
desc: u.search_rank
|
||||
)
|
||||
end
|
||||
|
||||
defp order_by_search_rank(query, _), do: order_by(query, desc: :search_rank)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1677,44 +1677,80 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
show_birthday = !!birthday
|
||||
|
||||
# if WebFinger request was already done, we probably have acct, otherwise
|
||||
# we request WebFinger here
|
||||
nickname = additional[:nickname_from_acct] || generate_nickname(data)
|
||||
with {:ok, nickname} <- nickname_from_actor(data, additional) do
|
||||
{:ok,
|
||||
%{
|
||||
ap_id: data["id"],
|
||||
uri: get_actor_url(data["url"]),
|
||||
banner: normalize_image(data["image"]),
|
||||
fields: fields,
|
||||
emoji: emojis,
|
||||
is_locked: is_locked,
|
||||
is_discoverable: is_discoverable,
|
||||
invisible: invisible,
|
||||
avatar: normalize_image(data["icon"]),
|
||||
name: data["name"],
|
||||
follower_address: data["followers"],
|
||||
following_address: data["following"],
|
||||
featured_address: featured_address,
|
||||
bio: data["summary"] || "",
|
||||
actor_type: actor_type,
|
||||
also_known_as: normalize_also_known_as(data["alsoKnownAs"]),
|
||||
public_key: public_key,
|
||||
inbox: data["inbox"],
|
||||
shared_inbox: shared_inbox,
|
||||
accepts_chat_messages: accepts_chat_messages,
|
||||
birthday: birthday,
|
||||
show_birthday: show_birthday,
|
||||
pinned_objects: pinned_objects,
|
||||
nickname: nickname
|
||||
}}
|
||||
end
|
||||
end
|
||||
|
||||
%{
|
||||
ap_id: data["id"],
|
||||
uri: get_actor_url(data["url"]),
|
||||
banner: normalize_image(data["image"]),
|
||||
fields: fields,
|
||||
emoji: emojis,
|
||||
is_locked: is_locked,
|
||||
is_discoverable: is_discoverable,
|
||||
invisible: invisible,
|
||||
avatar: normalize_image(data["icon"]),
|
||||
name: data["name"],
|
||||
follower_address: data["followers"],
|
||||
following_address: data["following"],
|
||||
featured_address: featured_address,
|
||||
bio: data["summary"] || "",
|
||||
actor_type: actor_type,
|
||||
also_known_as: normalize_also_known_as(data["alsoKnownAs"]),
|
||||
public_key: public_key,
|
||||
inbox: data["inbox"],
|
||||
shared_inbox: shared_inbox,
|
||||
accepts_chat_messages: accepts_chat_messages,
|
||||
birthday: birthday,
|
||||
show_birthday: show_birthday,
|
||||
pinned_objects: pinned_objects,
|
||||
nickname: nickname
|
||||
}
|
||||
defp nickname_from_actor(data, additional) do
|
||||
generated = generated_nickname(data)
|
||||
|
||||
case additional[:nickname_from_acct] do
|
||||
^generated when is_binary(generated) ->
|
||||
{:ok, generated}
|
||||
|
||||
acct when is_binary(acct) ->
|
||||
with ^acct <- webfinger_nickname(data) do
|
||||
{:ok, acct}
|
||||
else
|
||||
_ -> {:error, {:webfinger_actor_mismatch, acct, data["id"]}}
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:ok, generate_nickname(data)}
|
||||
end
|
||||
end
|
||||
|
||||
defp generated_nickname(%{"preferredUsername" => username, "id" => ap_id})
|
||||
when is_binary(username) and is_binary(ap_id) do
|
||||
case URI.parse(ap_id) do
|
||||
%URI{host: host} when is_binary(host) -> "#{username}@#{host}"
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp generated_nickname(_), do: nil
|
||||
|
||||
defp webfinger_nickname(data) do
|
||||
with generated when is_binary(generated) <- generated_nickname(data),
|
||||
{:ok, %{"subject" => "acct:" <> acct, "ap_id" => ap_id}} <- WebFinger.finger(generated),
|
||||
true <- ap_id == data["id"] do
|
||||
acct
|
||||
end
|
||||
end
|
||||
|
||||
defp generate_nickname(%{"preferredUsername" => username} = data) when is_binary(username) do
|
||||
generated = "#{username}@#{URI.parse(data["id"]).host}"
|
||||
generated = generated_nickname(data)
|
||||
|
||||
if Config.get([WebFinger, :update_nickname_on_user_fetch]) do
|
||||
case WebFinger.finger(generated) do
|
||||
{:ok, %{"subject" => "acct:" <> acct}} -> acct
|
||||
case webfinger_nickname(data) do
|
||||
acct when is_binary(acct) -> acct
|
||||
_ -> generated
|
||||
end
|
||||
else
|
||||
|
|
@ -1794,9 +1830,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
defp collection_private(_data), do: {:ok, true}
|
||||
|
||||
def user_data_from_user_object(data, additional \\ []) do
|
||||
with {:ok, data} <- MRF.filter(data) do
|
||||
{:ok, object_to_user_data(data, additional)}
|
||||
with {:ok, data} <- MRF.filter(data),
|
||||
{:ok, data} <- object_to_user_data(data, additional) do
|
||||
{:ok, data}
|
||||
else
|
||||
{:error, _} = e -> e
|
||||
e -> {:error, e}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -348,7 +348,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
end
|
||||
|
||||
def inbox(%{assigns: %{valid_signature: false}} = conn, params) do
|
||||
Federator.incoming_ap_doc(%{
|
||||
Federator.incoming_failed_signature_ap_doc(%{
|
||||
method: conn.method,
|
||||
req_headers: conn.req_headers,
|
||||
request_path: conn.request_path,
|
||||
|
|
|
|||
|
|
@ -6,9 +6,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
|||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.CommonAPI.Utils
|
||||
|
||||
import Ecto.Changeset
|
||||
|
||||
|
|
@ -26,6 +29,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
|||
end
|
||||
|
||||
field(:replies, {:array, ObjectValidators.ObjectID}, default: [])
|
||||
field(:source, :map)
|
||||
end
|
||||
|
||||
def cast_and_apply(data) do
|
||||
|
|
@ -80,6 +84,113 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
|||
|
||||
def fix_attachments(data), do: data
|
||||
|
||||
defp remote_mention_resolver(
|
||||
%{"id" => ap_id, "tag" => tags},
|
||||
"@" <> nickname = mention,
|
||||
buffer,
|
||||
opts,
|
||||
acc
|
||||
)
|
||||
when is_binary(ap_id) and is_list(tags) do
|
||||
initial_host =
|
||||
ap_id
|
||||
|> URI.parse()
|
||||
|> Map.get(:host)
|
||||
|
||||
with mention_tag when not is_nil(mention_tag) <-
|
||||
Enum.find(tags, &mention_tag?(&1, mention, initial_host)),
|
||||
href when is_binary(href) <- mention_tag["href"],
|
||||
%User{} = user <- User.get_cached_by_ap_id(href) do
|
||||
link = Pleroma.Formatter.mention_from_user(user, opts)
|
||||
{link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}}
|
||||
else
|
||||
_ -> {buffer, acc}
|
||||
end
|
||||
end
|
||||
|
||||
defp remote_mention_resolver(_object, _mention, buffer, _opts, acc), do: {buffer, acc}
|
||||
|
||||
defp mention_tag?(%{"type" => "Mention", "name" => name}, mention, initial_host)
|
||||
when is_binary(name) do
|
||||
name == mention || mention == "#{name}@#{initial_host}"
|
||||
end
|
||||
|
||||
defp mention_tag?(_tag, _mention, _initial_host), do: false
|
||||
|
||||
defp scrub_content(%{"content" => content} = object) when is_binary(content) do
|
||||
Map.put(object, "content", HTML.filter_tags(content))
|
||||
end
|
||||
|
||||
defp scrub_content(object), do: object
|
||||
|
||||
defp mfm_parse_limit do
|
||||
min(Pleroma.Config.get([:instance, :limit]), Pleroma.Config.get([:instance, :remote_limit]))
|
||||
end
|
||||
|
||||
defp normalize_source(%{"source" => source} = object) when is_binary(source) do
|
||||
object
|
||||
|> Map.put("source", %{"content" => source})
|
||||
|> normalize_source()
|
||||
end
|
||||
|
||||
defp normalize_source(%{"source" => source} = object) when is_map(source) do
|
||||
source =
|
||||
case source["content"] do
|
||||
content when is_binary(content) ->
|
||||
if String.length(content) <= mfm_parse_limit() do
|
||||
source
|
||||
else
|
||||
Map.delete(source, "content")
|
||||
end
|
||||
|
||||
nil ->
|
||||
source
|
||||
|
||||
_ ->
|
||||
Map.delete(source, "content")
|
||||
end
|
||||
|
||||
Map.put(object, "source", source)
|
||||
end
|
||||
|
||||
defp normalize_source(object), do: object
|
||||
|
||||
defp fix_misskey_content(%{"htmlMfm" => true, "content" => content} = object)
|
||||
when is_binary(content) do
|
||||
Map.put(object, "content", HTML.filter_tags(content))
|
||||
end
|
||||
|
||||
defp fix_misskey_content(%{"htmlMfm" => true} = object), do: object
|
||||
|
||||
defp fix_misskey_content(
|
||||
%{"source" => %{"mediaType" => "text/x.misskeymarkdown", "content" => content}} = object
|
||||
)
|
||||
when is_binary(content) do
|
||||
mention_handler = fn nick, buffer, opts, acc ->
|
||||
remote_mention_resolver(object, nick, buffer, opts, acc)
|
||||
end
|
||||
|
||||
{linked, _mentions, _tags} =
|
||||
Utils.format_input(content, "text/x.misskeymarkdown", mention_handler: mention_handler)
|
||||
|
||||
Map.put(object, "content", linked)
|
||||
end
|
||||
|
||||
defp fix_misskey_content(%{"source" => %{"mediaType" => "text/x.misskeymarkdown"}} = object),
|
||||
do: scrub_content(object)
|
||||
|
||||
defp fix_misskey_content(%{"_misskey_content" => content} = object) when is_binary(content) do
|
||||
object
|
||||
|> Map.put("source", %{
|
||||
"content" => content,
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
})
|
||||
|> Map.delete("_misskey_content")
|
||||
|> fix_misskey_content()
|
||||
end
|
||||
|
||||
defp fix_misskey_content(object), do: object
|
||||
|
||||
defp fix(data) do
|
||||
data
|
||||
|> CommonFixes.fix_actor()
|
||||
|
|
@ -88,6 +199,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
|||
|> fix_tag()
|
||||
|> fix_replies()
|
||||
|> fix_attachments()
|
||||
|> normalize_source()
|
||||
|> fix_misskey_content()
|
||||
|> CommonFixes.fix_quote_url()
|
||||
|> CommonFixes.fix_likes()
|
||||
|> Transmogrifier.fix_emoji()
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do
|
|||
quote bind_quoted: binding() do
|
||||
field(:content, :string)
|
||||
field(:contentMap, ObjectValidators.ContentLanguageMap)
|
||||
field(:htmlMfm, :boolean)
|
||||
|
||||
field(:published, ObjectValidators.DateTime)
|
||||
field(:updated, ObjectValidators.DateTime)
|
||||
|
|
|
|||
|
|
@ -75,15 +75,40 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do
|
|||
end
|
||||
end
|
||||
|
||||
# For remote Updates, verify the host is the same.
|
||||
# For remote Updates, verify the Actor is the same
|
||||
def validate_updating_rights_remote(cng) do
|
||||
with actor = get_field(cng, :actor),
|
||||
object = get_field(cng, :object),
|
||||
{:ok, object_id} <- ObjectValidators.ObjectID.cast(object),
|
||||
actor_uri <- URI.parse(actor),
|
||||
object_uri <- URI.parse(object_id),
|
||||
true <- actor_uri.host == object_uri.host do
|
||||
cng
|
||||
entity <-
|
||||
Object.normalize(object_id, fetch: false) || User.get_cached_by_ap_id(object_id) do
|
||||
case entity do
|
||||
# Actor must own Object to update it
|
||||
%Object{} ->
|
||||
if actor == entity.data["actor"] do
|
||||
cng
|
||||
else
|
||||
cng
|
||||
|> add_error(:object, "Can't be updated by this actor")
|
||||
end
|
||||
|
||||
# Actor must only be allowed to update itself
|
||||
%User{} ->
|
||||
if actor == entity.ap_id do
|
||||
cng
|
||||
else
|
||||
cng
|
||||
|> add_error(:object, "Can't be updated by this actor")
|
||||
end
|
||||
|
||||
nil ->
|
||||
cng
|
||||
|> add_error(:object, "Can't be updated by this actor")
|
||||
|
||||
_ ->
|
||||
cng
|
||||
|> add_error(:object, "Update is neither for Object or Actor")
|
||||
end
|
||||
else
|
||||
_e ->
|
||||
cng
|
||||
|
|
|
|||
|
|
@ -430,6 +430,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
end)
|
||||
end
|
||||
|
||||
defp reject_third_party_report(%User{local: false}, %User{local: false} = account) do
|
||||
{:reject, "[Transmogrifier] third-party report: #{account.ap_id}"}
|
||||
end
|
||||
|
||||
defp reject_third_party_report(_, _), do: :ok
|
||||
|
||||
def handle_incoming(data, options \\ []) do
|
||||
data
|
||||
|> fix_recursive(&strip_internal_fields/1)
|
||||
|
|
@ -444,9 +450,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
) do
|
||||
with context <- data["context"] || Utils.generate_context_id(),
|
||||
content <- data["content"] || "",
|
||||
objects <- List.wrap(objects),
|
||||
%User{} = actor <- User.get_cached_by_ap_id(actor),
|
||||
# Reduce the object list to find the reported user.
|
||||
%User{} = account <- get_reported(objects),
|
||||
:ok <- reject_third_party_report(actor, account),
|
||||
# Remove the reported user from the object list.
|
||||
statuses <- Enum.filter(objects, fn ap_id -> ap_id != account.ap_id end) do
|
||||
%{
|
||||
|
|
|
|||
|
|
@ -120,7 +120,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
"https://www.w3.org/ns/activitystreams",
|
||||
"#{Endpoint.url()}/schemas/litepub-0.1.jsonld",
|
||||
%{
|
||||
"@language" => get_language(data)
|
||||
"@language" => get_language(data),
|
||||
"htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,7 +172,10 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
|
|||
type: :object,
|
||||
properties: %{
|
||||
title: %Schema{type: :string, description: "List title"},
|
||||
exclusive: %Schema{type: :boolean, description: "Whether members of the list should be removed from the “Home” feed"}
|
||||
exclusive: %Schema{
|
||||
type: :boolean,
|
||||
description: "Whether members of the list should be removed from the “Home” feed"
|
||||
}
|
||||
},
|
||||
required: [:title]
|
||||
},
|
||||
|
|
@ -188,7 +191,10 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
|
|||
type: :object,
|
||||
properties: %{
|
||||
title: %Schema{type: :string, description: "List title"},
|
||||
exclusive: %Schema{type: :boolean, description: "Whether members of the list should be removed from the “Home” feed"}
|
||||
exclusive: %Schema{
|
||||
type: :boolean,
|
||||
description: "Whether members of the list should be removed from the “Home” feed"
|
||||
}
|
||||
}
|
||||
},
|
||||
required: true
|
||||
|
|
|
|||
|
|
@ -317,6 +317,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
|
||||
emoji = Map.merge(emoji, summary_emoji)
|
||||
|
||||
media_type = Utils.get_content_type(draft.params[:content_type])
|
||||
{:ok, note_data, _meta} = Builder.note(draft)
|
||||
|
||||
object =
|
||||
|
|
@ -324,14 +325,18 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
|> Map.put("emoji", emoji)
|
||||
|> Map.put("source", %{
|
||||
"content" => draft.status,
|
||||
"mediaType" => Utils.get_content_type(draft.params[:content_type])
|
||||
"mediaType" => media_type
|
||||
})
|
||||
|> maybe_put("htmlMfm", true, media_type == "text/x.misskeymarkdown")
|
||||
|> Map.put("generator", draft.params[:generator])
|
||||
|> Map.put("language", draft.language)
|
||||
|
||||
%{draft | object: object}
|
||||
end
|
||||
|
||||
defp maybe_put(map, key, value, true), do: Map.put(map, key, value)
|
||||
defp maybe_put(map, _key, _value, _condition), do: map
|
||||
|
||||
defp preview?(%__MODULE__{} = draft) do
|
||||
preview? = Pleroma.Web.Utils.Params.truthy_param?(draft.params[:preview])
|
||||
%{draft | preview?: preview?}
|
||||
|
|
|
|||
|
|
@ -322,6 +322,14 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
|> Formatter.linkify(options)
|
||||
end
|
||||
|
||||
def format_input(text, "text/x.misskeymarkdown", options) do
|
||||
text
|
||||
|> Formatter.markdown_to_html(%{breaks: true})
|
||||
|> safe_mfm_to_html()
|
||||
|> Formatter.linkify(options)
|
||||
|> Formatter.html_escape("text/x.misskeymarkdown")
|
||||
end
|
||||
|
||||
def format_input(text, "text/markdown", options) do
|
||||
text
|
||||
|> Formatter.mentions_escape(options)
|
||||
|
|
@ -330,6 +338,16 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
|> Formatter.html_escape("text/html")
|
||||
end
|
||||
|
||||
defp safe_mfm_to_html(html) do
|
||||
html
|
||||
|> MfmParser.Parser.parse()
|
||||
|> MfmParser.Encoder.to_html()
|
||||
rescue
|
||||
_ -> html
|
||||
catch
|
||||
_, _ -> html
|
||||
end
|
||||
|
||||
def format_naive_asctime(date) do
|
||||
date |> DateTime.from_naive!("Etc/UTC") |> format_asctime
|
||||
end
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ defmodule Pleroma.Web.Endpoint do
|
|||
|
||||
@static_cache_control "public, max-age=1209600, immutable"
|
||||
@static_cache_disabled "public, no-cache"
|
||||
@favicon_cache_control "public, max=age=86400, immutable" # cache for a day
|
||||
# cache for a day
|
||||
@favicon_cache_control "public, max=age=86400, immutable"
|
||||
|
||||
# InstanceStatic needs to be before Plug.Static to be able to override shipped-static files
|
||||
# If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Web.Federator do
|
|||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Workers.PublisherWorker
|
||||
alias Pleroma.Workers.ReceiverWorker
|
||||
alias Pleroma.Workers.SignatureRetryWorker
|
||||
|
||||
require Logger
|
||||
|
||||
|
|
@ -35,12 +36,21 @@ defmodule Pleroma.Web.Federator do
|
|||
end
|
||||
|
||||
# Client API
|
||||
def incoming_ap_doc(%{params: params, req_headers: req_headers}) do
|
||||
ReceiverWorker.new(
|
||||
def incoming_failed_signature_ap_doc(%{
|
||||
method: method,
|
||||
params: params,
|
||||
req_headers: req_headers,
|
||||
request_path: request_path,
|
||||
query_string: query_string
|
||||
}) do
|
||||
SignatureRetryWorker.new(
|
||||
%{
|
||||
"op" => "incoming_ap_doc",
|
||||
"op" => "incoming_failed_signature_ap_doc",
|
||||
"method" => method,
|
||||
"req_headers" => req_headers,
|
||||
"params" => params,
|
||||
"request_path" => request_path,
|
||||
"query_string" => query_string,
|
||||
"timeout" => :timer.seconds(20)
|
||||
},
|
||||
priority: 2
|
||||
|
|
|
|||
|
|
@ -71,12 +71,12 @@ defmodule Pleroma.Web.MastodonAPI.PollView do
|
|||
end)
|
||||
end
|
||||
|
||||
defp voters_count(%{data: %{"votersCount" => voters}}) when is_integer(voters), do: voters
|
||||
|
||||
defp voters_count(%{data: %{"voters" => [_ | _] = voters}}) do
|
||||
length(voters)
|
||||
end
|
||||
|
||||
defp voters_count(%{data: %{"votersCount" => voters}}), do: voters
|
||||
|
||||
defp voters_count(_), do: 0
|
||||
|
||||
defp voted_and_own_votes(%{object: object} = params, options) do
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do
|
|||
# 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
|
||||
Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{inspect(actor)}")
|
||||
assign(conn, :valid_signature, false)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,40 +4,37 @@
|
|||
|
||||
defmodule Pleroma.Workers.ReceiverWorker do
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Signature
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Federator
|
||||
alias Pleroma.Workers.SignatureRetryWorker
|
||||
|
||||
use Oban.Worker, queue: :federator_incoming, max_attempts: 5, unique: [period: :infinity]
|
||||
|
||||
@impl true
|
||||
def perform(%Job{args: %{"op" => "incoming_ap_doc", "params" => params} = args} = job) do
|
||||
if signature_retry_job?(args) do
|
||||
perform_signature_retry(job)
|
||||
else
|
||||
perform_incoming(params)
|
||||
end
|
||||
end
|
||||
|
||||
def perform(%Job{
|
||||
args: %{
|
||||
"op" => "incoming_ap_doc",
|
||||
"method" => method,
|
||||
"params" => params,
|
||||
"req_headers" => req_headers,
|
||||
"request_path" => request_path,
|
||||
"query_string" => query_string
|
||||
}
|
||||
}) do
|
||||
# Oban's serialization converts our tuple headers to lists.
|
||||
# Revert it for the signature validation.
|
||||
req_headers = Enum.into(req_headers, [], &List.to_tuple(&1))
|
||||
def perform(%Job{args: %{"op" => "incoming_ap_doc"} = args} = job) do
|
||||
if signature_retry_job?(args) do
|
||||
perform_signature_retry(job)
|
||||
else
|
||||
process_errors(:missing_incoming_ap_doc_params)
|
||||
end
|
||||
end
|
||||
|
||||
conn_data = %Plug.Conn{
|
||||
method: method,
|
||||
params: params,
|
||||
req_headers: req_headers,
|
||||
request_path: request_path,
|
||||
query_string: query_string
|
||||
}
|
||||
defp perform_signature_retry(%Job{args: args} = job) do
|
||||
SignatureRetryWorker.perform(%Job{
|
||||
job
|
||||
| args: Map.put(args, "op", "incoming_failed_signature_ap_doc")
|
||||
})
|
||||
end
|
||||
|
||||
with {:ok, %User{}} <- User.get_or_fetch_by_ap_id(conn_data.params["actor"]),
|
||||
{:ok, _public_key} <- Signature.refetch_public_key(conn_data),
|
||||
{:signature, true} <- {:signature, Signature.validate_signature(conn_data)},
|
||||
{:ok, res} <- Federator.perform(:incoming_ap_doc, params) do
|
||||
defp perform_incoming(params) do
|
||||
with {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do
|
||||
unless Instances.reachable?(params["actor"]) do
|
||||
domain = URI.parse(params["actor"]).host
|
||||
Oban.insert(Pleroma.Workers.ReachabilityWorker.new(%{"domain" => domain}))
|
||||
|
|
@ -49,17 +46,8 @@ defmodule Pleroma.Workers.ReceiverWorker do
|
|||
end
|
||||
end
|
||||
|
||||
def perform(%Job{args: %{"op" => "incoming_ap_doc", "params" => params}}) do
|
||||
with {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do
|
||||
unless Instances.reachable?(params["actor"]) do
|
||||
domain = URI.parse(params["actor"]).host
|
||||
Oban.insert(Pleroma.Workers.ReachabilityWorker.new(%{"domain" => domain}))
|
||||
end
|
||||
|
||||
{:ok, res}
|
||||
else
|
||||
e -> process_errors(e)
|
||||
end
|
||||
defp signature_retry_job?(args) do
|
||||
Enum.any?(~w(method req_headers request_path query_string), &Map.has_key?(args, &1))
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
|
@ -85,10 +73,12 @@ defmodule Pleroma.Workers.ReceiverWorker do
|
|||
{:error, {:reject, _} = reason} -> {:cancel, reason}
|
||||
# HTTP Sigs
|
||||
{:signature, false} -> {:cancel, :invalid_signature}
|
||||
{:same_actor, false} -> {:cancel, :actor_signature_mismatch}
|
||||
# Origin / URL validation failed somewhere possibly due to spoofing
|
||||
{:error, :origin_containment_failed} -> {:cancel, :origin_containment_failed}
|
||||
# Unclear if this can be reached
|
||||
{:error, {:side_effects, {:error, :no_object_actor}} = reason} -> {:cancel, reason}
|
||||
:missing_incoming_ap_doc_params -> {:cancel, :missing_incoming_ap_doc_params}
|
||||
# Catchall
|
||||
{:error, _} = e -> e
|
||||
e -> {:error, e}
|
||||
|
|
|
|||
254
lib/pleroma/workers/signature_retry_worker.ex
Normal file
254
lib/pleroma/workers/signature_retry_worker.ex
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Workers.SignatureRetryWorker do
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Signature
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.Federator
|
||||
alias Pleroma.Web.Plugs.MappedSignatureToIdentityPlug
|
||||
|
||||
require Logger
|
||||
|
||||
use Oban.Worker, queue: :federator_incoming, max_attempts: 5, unique: [period: :infinity]
|
||||
|
||||
@impl true
|
||||
def perform(%Job{
|
||||
args: %{
|
||||
"op" => "incoming_failed_signature_ap_doc",
|
||||
"method" => method,
|
||||
"params" => params,
|
||||
"req_headers" => req_headers,
|
||||
"request_path" => request_path,
|
||||
"query_string" => query_string
|
||||
}
|
||||
})
|
||||
when is_binary(method) and is_map(params) and is_list(req_headers) and
|
||||
is_binary(request_path) and is_binary(query_string) do
|
||||
case normalize_req_headers(req_headers) do
|
||||
{:ok, req_headers} ->
|
||||
conn_data = %Plug.Conn{
|
||||
assigns: %{valid_signature: true},
|
||||
method: method,
|
||||
params: params,
|
||||
req_headers: req_headers,
|
||||
request_path: request_path,
|
||||
query_string: query_string
|
||||
}
|
||||
|
||||
signature_actor_result = signature_actor_id(conn_data)
|
||||
|
||||
with actor_id = Utils.get_ap_id(params["actor"]),
|
||||
{:signature_actor, {:ok, signature_actor_id}} <-
|
||||
{:signature_actor, signature_actor_result},
|
||||
{:same_actor, true} <- {:same_actor, signature_actor_id == actor_id},
|
||||
{:ok, %User{}} <- User.get_or_fetch_by_ap_id(actor_id),
|
||||
{:ok, _public_key} <- Signature.refetch_public_key(conn_data),
|
||||
{:signature, true} <- {:signature, validate_signature(conn_data)},
|
||||
{:same_actor, true} <- {:same_actor, validate_same_actor(conn_data)},
|
||||
{:ok, res} <- Federator.perform(:incoming_ap_doc, params) do
|
||||
unless Instances.reachable?(params["actor"]) do
|
||||
domain = URI.parse(params["actor"]).host
|
||||
Oban.insert(Pleroma.Workers.ReachabilityWorker.new(%{"domain" => domain}))
|
||||
end
|
||||
|
||||
{:ok, res}
|
||||
else
|
||||
e -> process_errors(e, retry_log_context(params, request_path, signature_actor_result))
|
||||
end
|
||||
|
||||
e ->
|
||||
process_errors(e, retry_log_context(params, request_path, nil))
|
||||
end
|
||||
end
|
||||
|
||||
def perform(%Job{args: %{"op" => "incoming_failed_signature_ap_doc"} = args}) do
|
||||
process_errors(
|
||||
:missing_signature_retry_metadata,
|
||||
retry_log_context(Map.get(args, "params"), Map.get(args, "request_path"), nil)
|
||||
)
|
||||
end
|
||||
|
||||
def perform(%Job{args: args}) when is_map(args) do
|
||||
process_errors(
|
||||
:missing_signature_retry_metadata,
|
||||
retry_log_context(Map.get(args, "params"), Map.get(args, "request_path"), nil)
|
||||
)
|
||||
end
|
||||
|
||||
def perform(%Job{}), do: process_errors(:missing_signature_retry_metadata)
|
||||
|
||||
@impl true
|
||||
def timeout(%_{args: %{"timeout" => timeout}}), do: timeout
|
||||
|
||||
def timeout(_job), do: :timer.seconds(5)
|
||||
|
||||
defp normalize_req_headers(req_headers) do
|
||||
req_headers
|
||||
|> Enum.reduce_while({:ok, []}, fn
|
||||
{key, value}, {:ok, acc} when is_binary(key) and is_binary(value) ->
|
||||
{:cont, {:ok, [{key, value} | acc]}}
|
||||
|
||||
[key, value], {:ok, acc} when is_binary(key) and is_binary(value) ->
|
||||
{:cont, {:ok, [{key, value} | acc]}}
|
||||
|
||||
_, _ ->
|
||||
{:halt, {:error, :invalid_signature_retry_metadata}}
|
||||
end)
|
||||
|> case do
|
||||
{:ok, headers} -> {:ok, Enum.reverse(headers)}
|
||||
error -> error
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_same_actor(conn_data) do
|
||||
case MappedSignatureToIdentityPlug.call(conn_data, []) do
|
||||
%Plug.Conn{assigns: %{valid_signature: true}} ->
|
||||
true
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_signature(conn_data) do
|
||||
Signature.validate_signature(conn_data)
|
||||
rescue
|
||||
_ -> false
|
||||
catch
|
||||
_, _ -> false
|
||||
end
|
||||
|
||||
defp signature_actor_id(conn_data) do
|
||||
Signature.get_actor_id(conn_data)
|
||||
rescue
|
||||
_ -> {:error, :invalid_signature}
|
||||
catch
|
||||
_, _ -> {:error, :invalid_signature}
|
||||
end
|
||||
|
||||
defp process_errors(errors, context \\ %{})
|
||||
|
||||
defp process_errors({:error, {:error, _} = error}, context), do: process_errors(error, context)
|
||||
|
||||
defp process_errors(errors, context) do
|
||||
result =
|
||||
case errors do
|
||||
# User fetch failures
|
||||
{:error, :not_found} = reason ->
|
||||
{:cancel, reason}
|
||||
|
||||
{:error, :forbidden} = reason ->
|
||||
{:cancel, reason}
|
||||
|
||||
# Inactive user
|
||||
{:error, {:user_active, false} = reason} ->
|
||||
{:cancel, reason}
|
||||
|
||||
# Validator will error and return a changeset error
|
||||
# e.g., duplicate activities or if the object was deleted
|
||||
{:error, {:validate, {:error, _changeset} = reason}} ->
|
||||
{:cancel, reason}
|
||||
|
||||
# Duplicate detection during Normalization
|
||||
{:error, :already_present} ->
|
||||
{:cancel, :already_present}
|
||||
|
||||
# MRFs will return a reject
|
||||
{:error, {:reject, _} = reason} ->
|
||||
{:cancel, reason}
|
||||
|
||||
# HTTP Sigs
|
||||
{:signature_actor, {:error, _}} ->
|
||||
{:cancel, :invalid_signature}
|
||||
|
||||
{:signature, false} ->
|
||||
{:cancel, :invalid_signature}
|
||||
|
||||
{:same_actor, false} ->
|
||||
{:cancel, :actor_signature_mismatch}
|
||||
|
||||
# Origin / URL validation failed somewhere possibly due to spoofing
|
||||
{:error, :origin_containment_failed} ->
|
||||
{:cancel, :origin_containment_failed}
|
||||
|
||||
# Unclear if this can be reached
|
||||
{:error, {:side_effects, {:error, :no_object_actor}} = reason} ->
|
||||
{:cancel, reason}
|
||||
|
||||
# Fail closed if the retry cannot reconstruct the original request.
|
||||
:missing_signature_retry_metadata ->
|
||||
{:cancel, :missing_signature_retry_metadata}
|
||||
|
||||
{:error, :invalid_signature_retry_metadata} ->
|
||||
{:cancel, :invalid_signature_retry_metadata}
|
||||
|
||||
# Catchall
|
||||
{:error, _} = e ->
|
||||
e
|
||||
|
||||
e ->
|
||||
{:error, e}
|
||||
end
|
||||
|
||||
log_signature_retry_rejection(result, context)
|
||||
result
|
||||
end
|
||||
|
||||
defp retry_log_context(params, request_path, signature_actor_result) when is_map(params) do
|
||||
signature_actor =
|
||||
case signature_actor_result do
|
||||
{:ok, actor} when is_binary(actor) -> actor
|
||||
actor when is_binary(actor) -> actor
|
||||
_ -> nil
|
||||
end
|
||||
|
||||
%{
|
||||
activity_id: params["id"],
|
||||
payload_actor: Utils.get_ap_id(params["actor"]),
|
||||
request_path: request_path,
|
||||
signature_actor: signature_actor,
|
||||
type: params["type"]
|
||||
}
|
||||
end
|
||||
|
||||
defp retry_log_context(_params, request_path, signature_actor_result) do
|
||||
signature_actor =
|
||||
case signature_actor_result do
|
||||
{:ok, actor} when is_binary(actor) -> actor
|
||||
actor when is_binary(actor) -> actor
|
||||
_ -> nil
|
||||
end
|
||||
|
||||
%{
|
||||
activity_id: nil,
|
||||
payload_actor: nil,
|
||||
request_path: request_path,
|
||||
signature_actor: signature_actor,
|
||||
type: nil
|
||||
}
|
||||
end
|
||||
|
||||
defp log_signature_retry_rejection({:cancel, reason}, context)
|
||||
when reason in [
|
||||
:actor_signature_mismatch,
|
||||
:invalid_signature,
|
||||
:invalid_signature_retry_metadata,
|
||||
:missing_signature_retry_metadata,
|
||||
:origin_containment_failed
|
||||
] do
|
||||
Logger.warning(
|
||||
"Failed-signature inbox retry rejected " <>
|
||||
"reason=#{inspect(reason)} " <>
|
||||
"payload_actor=#{inspect(context[:payload_actor])} " <>
|
||||
"signature_actor=#{inspect(context[:signature_actor])} " <>
|
||||
"activity_id=#{inspect(context[:activity_id])} " <>
|
||||
"type=#{inspect(context[:type])} " <>
|
||||
"request_path=#{inspect(context[:request_path])}"
|
||||
)
|
||||
end
|
||||
|
||||
defp log_signature_retry_rejection(_result, _context), do: :ok
|
||||
end
|
||||
9
mix.exs
9
mix.exs
|
|
@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
|
|||
def project do
|
||||
[
|
||||
app: :pleroma,
|
||||
version: version("2.10.0"),
|
||||
version: version("2.10.2"),
|
||||
elixir: "~> 1.15",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
compilers: Mix.compilers(),
|
||||
|
|
@ -160,6 +160,7 @@ defmodule Pleroma.Mixfile do
|
|||
{:sweet_xml, "~> 0.7.5"},
|
||||
{:earmark, "1.4.46"},
|
||||
{:bbcode_pleroma, "~> 0.2.0"},
|
||||
{:pleroma_mfm_parser, "~> 0.2.1"},
|
||||
{:cors_plug, "~> 2.0"},
|
||||
{:web_push_encryption, "~> 0.3.1"},
|
||||
{:swoosh, "~> 1.16.12"},
|
||||
|
|
@ -172,7 +173,7 @@ defmodule Pleroma.Mixfile do
|
|||
{:timex, "~> 3.7"},
|
||||
{:ueberauth, "~> 0.10"},
|
||||
{:linkify, "~> 0.5.3"},
|
||||
{:http_signatures, "~> 0.1.2"},
|
||||
{:http_signatures, "~> 0.1.3"},
|
||||
{:telemetry, "~> 1.0.0", override: true},
|
||||
{:poolboy, "~> 1.5"},
|
||||
{:prom_ex, "~> 1.9"},
|
||||
|
|
@ -197,7 +198,7 @@ defmodule Pleroma.Mixfile do
|
|||
{:elixir_make, "~> 0.7.8", override: true},
|
||||
{:blurhash, "~> 0.1.0", hex: :rinpatch_blurhash},
|
||||
{:exile, "~> 0.10.0"},
|
||||
{:bandit, "~> 1.5.7"},
|
||||
{:bandit, "~> 1.10"},
|
||||
{:websock_adapter, "~> 0.5.8"},
|
||||
{:oban_live_dashboard, "~> 0.1.1"},
|
||||
{:multipart, "~> 0.4.0", optional: true},
|
||||
|
|
@ -211,7 +212,7 @@ defmodule Pleroma.Mixfile do
|
|||
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
|
||||
{:mock, "~> 0.3.9", only: :test},
|
||||
{:covertool, "~> 2.0", only: :test},
|
||||
{:hackney, "~> 1.25.0", override: true},
|
||||
{:hackney, "~> 1.20.1", override: true},
|
||||
{:mox, "~> 1.2", only: :test},
|
||||
{:websockex, "~> 0.4.3", only: :test},
|
||||
{:benchee, "~> 1.4", only: :benchmark},
|
||||
|
|
|
|||
14
mix.lock
14
mix.lock
|
|
@ -1,7 +1,7 @@
|
|||
%{
|
||||
"accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm", "11b18c220bcc2eab63b5470c038ef10eb6783bcb1fcdb11aa4137defa5ac1bb8"},
|
||||
"argon2_elixir": {:hex, :argon2_elixir, "4.1.3", "4f28318286f89453364d7fbb53e03d4563fd7ed2438a60237eba5e426e97785f", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "7c295b8d8e0eaf6f43641698f962526cdf87c6feb7d14bd21e599271b510608c"},
|
||||
"bandit": {:hex, :bandit, "1.5.7", "6856b1e1df4f2b0cb3df1377eab7891bec2da6a7fd69dc78594ad3e152363a50", [:mix], [{:hpax, "~> 1.0.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "f2dd92ae87d2cbea2fa9aa1652db157b6cba6c405cb44d4f6dd87abba41371cd"},
|
||||
"bandit": {:hex, :bandit, "1.10.4", "02b9734c67c5916a008e7eb7e2ba68aaea6f8177094a5f8d95f1fb99069aac17", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "a5faf501042ac1f31d736d9d4a813b3db4ef812e634583b6a457b0928798a51d"},
|
||||
"base62": {:hex, :base62, "1.2.2", "85c6627eb609317b70f555294045895ffaaeb1758666ab9ef9ca38865b11e629", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "d41336bda8eaa5be197f1e4592400513ee60518e5b9f4dcf38f4b4dae6f377bb"},
|
||||
"bbcode_pleroma": {:hex, :bbcode_pleroma, "0.2.0", "d36f5bca6e2f62261c45be30fa9b92725c0655ad45c99025cb1c3e28e25803ef", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "19851074419a5fedb4ef49e1f01b30df504bb5dbb6d6adfc135238063bebd1c3"},
|
||||
"bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.1", "5114d780459a04f2b4aeef52307de23de961b69e13a5cd98a911e39fda13f420", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "42182d5f46764def15bf9af83739e3bf4ad22661b1c34fc3e88558efced07279"},
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e7b7cc34cc16b383461b966484c297e4ec9aeef6", [ref: "e7b7cc34cc16b383461b966484c297e4ec9aeef6"]},
|
||||
"castore": {:hex, :castore, "1.0.15", "8aa930c890fe18b6fe0a0cff27b27d0d4d231867897bd23ea772dee561f032a3", [:mix], [], "hexpm", "96ce4c69d7d5d7a0761420ef743e2f4096253931a3ba69e5ff8ef1844fe446d3"},
|
||||
"cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"},
|
||||
"certifi": {:hex, :certifi, "2.15.0", "0e6e882fcdaaa0a5a9f2b3db55b1394dba07e8d6d9bcad08318fb604c6839712", [:rebar3], [], "hexpm", "b147ed22ce71d72eafdad94f055165c1c182f61a2ff49df28bcc71d1d5b94a60"},
|
||||
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
|
||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||
"comeonin": {:hex, :comeonin, "5.5.1", "5113e5f3800799787de08a6e0db307133850e635d34e9fab23c70b6501669510", [:mix], [], "hexpm", "65aac8f19938145377cee73973f192c5645873dcf550a8a6b18187d17c13ccdb"},
|
||||
"concurrent_limiter": {:hex, :concurrent_limiter, "0.1.1", "43ae1dc23edda1ab03dd66febc739c4ff710d047bb4d735754909f9a474ae01c", [:mix], [{:telemetry, "~> 0.3", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "53968ff238c0fbb4d7ed76ddb1af0be6f3b2f77909f6796e249e737c505a16eb"},
|
||||
|
|
@ -59,10 +59,10 @@
|
|||
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},
|
||||
"gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"},
|
||||
"gun": {:hex, :gun, "2.2.0", "b8f6b7d417e277d4c2b0dc3c07dfdf892447b087f1cc1caff9c0f556b884e33d", [:make, :rebar3], [{:cowlib, ">= 2.15.0 and < 3.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "76022700c64287feb4df93a1795cff6741b83fb37415c40c34c38d2a4645261a"},
|
||||
"hackney": {:hex, :hackney, "1.25.0", "390e9b83f31e5b325b9f43b76e1a785cbdb69b5b6cd4e079aa67835ded046867", [:rebar3], [{:certifi, "~> 2.15.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "7209bfd75fd1f42467211ff8f59ea74d6f2a9e81cbcee95a56711ee79fd6b1d4"},
|
||||
"hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"},
|
||||
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
|
||||
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
|
||||
"http_signatures": {:hex, :http_signatures, "0.1.2", "ed1cc7043abcf5bb4f30d68fb7bad9d618ec1a45c4ff6c023664e78b67d9c406", [:mix], [], "hexpm", "f08aa9ac121829dae109d608d83c84b940ef2f183ae50f2dd1e9a8bc619d8be7"},
|
||||
"http_signatures": {:hex, :http_signatures, "0.1.3", "19f26aee35b4684e37efdce3ac4638605e6e41a04368186bd39d2b6138a60ea9", [:mix], [{:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "20313a65516db88006f85b090f6f76cc5b04e9609b45943657e6781eb91174f4"},
|
||||
"httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"},
|
||||
"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.8", "d26bb7bdbdf21ae401ead2092bf2bb4bf57fe44a62f5eaa5025280720ace8a40", [:mix], [], "hexpm", "d5b26da66603bb56c933c65214c72152f0de9a6ea53618b56d63302a68f6a90e"},
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
"meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||
"mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
|
||||
"mimerl": {:hex, :mimerl, "1.4.0", "3882a5ca67fbbe7117ba8947f27643557adec38fa2307490c4c4207624cb213b", [:rebar3], [], "hexpm", "13af15f9f68c65884ecca3a3891d50a7b57d82152792f3e19d88650aa126b144"},
|
||||
"mimerl": {:hex, :mimerl, "1.5.0", "f35aca6f23242339b3666e0ac0702379e362b469d0aea167f6cc713547e777ed", [:rebar3], [], "hexpm", "db648ce065bae14ea84ca8b5dd123f42f49417cef693541110bf6f9e9be9ecc4"},
|
||||
"mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"},
|
||||
"mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
|
||||
"mock": {:hex, :mock, "0.3.9", "10e44ad1f5962480c5c9b9fa779c6c63de9bd31997c8e04a853ec990a9d841af", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "9e1b244c4ca2551bb17bb8415eed89e40ee1308e0fbaed0a4fdfe3ec8a4adbd3"},
|
||||
|
|
@ -112,6 +112,7 @@
|
|||
"phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.1", "b74ccaa8046fbc388a62134360ee7d9742d5a8ae74063f34eb050279de7a99e1", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "4000eeba3f9d7d1a6bf56d2bd56733d5cadf41a7f0d8ffe5bb67e7d667e204a2"},
|
||||
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
|
||||
"phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"},
|
||||
"pleroma_mfm_parser": {:hex, :pleroma_mfm_parser, "0.2.1", "87a40fec4e2e035447df34e1c98e1b816b6a448d49baea73eb2273531ac5ce66", [:mix], [], "hexpm", "ec4845461da95d63ab3d592086d65114c45057a1f2d48d768f3def45b0d7696f"},
|
||||
"plug": {:hex, :plug, "1.19.1", "09bac17ae7a001a68ae393658aa23c7e38782be5c5c00c80be82901262c394c0", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "560a0017a8f6d5d30146916862aaf9300b7280063651dd7e532b8be168511e62"},
|
||||
"plug_cowboy": {:hex, :plug_cowboy, "2.7.4", "729c752d17cf364e2b8da5bdb34fb5804f56251e88bb602aff48ae0bd8673d11", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9b85632bd7012615bae0a5d70084deb1b25d2bcbb32cab82d1e9a1e023168aa3"},
|
||||
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
|
||||
|
|
@ -128,6 +129,7 @@
|
|||
"prometheus_phx": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/prometheus-phx.git", "9cd8f248c9381ffedc799905050abce194a97514", [branch: "no-logging"]},
|
||||
"prometheus_plugs": {:hex, :prometheus_plugs, "1.1.5", "25933d48f8af3a5941dd7b621c889749894d8a1082a6ff7c67cc99dec26377c5", [:mix], [{:accept, "~> 0.1", [hex: :accept, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}, {:prometheus_process_collector, "~> 1.1", [hex: :prometheus_process_collector, repo: "hexpm", optional: true]}], "hexpm", "0273a6483ccb936d79ca19b0ab629aef0dba958697c94782bb728b920dfc6a79"},
|
||||
"quantile_estimator": {:hex, :quantile_estimator, "0.2.1", "ef50a361f11b5f26b5f16d0696e46a9e4661756492c981f7b2229ef42ff1cd15", [:rebar3], [], "hexpm", "282a8a323ca2a845c9e6f787d166348f776c1d4a41ede63046d72d422e3da946"},
|
||||
"quic": {:hex, :quic, "0.10.2", "4b390507a85f65ce47808f3df1a864e0baf9adb7a1b4ea9f4dcd66fe9d0cb166", [:rebar3], [], "hexpm", "7c196a66973c877a59768a5687f0a0610ff11817254d0a4e45cc4e3a16b1d00b"},
|
||||
"ranch": {:hex, :ranch, "2.2.0", "25528f82bc8d7c6152c57666ca99ec716510fe0925cb188172f41ce93117b1b0", [:make, :rebar3], [], "hexpm", "fa0b99a1780c80218a4197a59ea8d3bdae32fbff7e88527d7d8a4787eff4f8e7"},
|
||||
"recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"},
|
||||
"remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8", [ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"]},
|
||||
|
|
@ -144,7 +146,7 @@
|
|||
"telemetry_metrics_prometheus_core": {:hex, :telemetry_metrics_prometheus_core, "1.2.1", "c9755987d7b959b557084e6990990cb96a50d6482c683fb9622a63837f3cd3d8", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5e2c599da4983c4f88a33e9571f1458bf98b0cf6ba930f1dc3a6e8cf45d5afb6"},
|
||||
"telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"},
|
||||
"tesla": {:hex, :tesla, "1.15.3", "3a2b5c37f09629b8dcf5d028fbafc9143c0099753559d7fe567eaabfbd9b8663", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.21", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:mox, "~> 1.0", [hex: :mox, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "98bb3d4558abc67b92fb7be4cd31bb57ca8d80792de26870d362974b58caeda7"},
|
||||
"thousand_island": {:hex, :thousand_island, "1.3.14", "ad45ebed2577b5437582bcc79c5eccd1e2a8c326abf6a3464ab6c06e2055a34a", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d0d24a929d31cdd1d7903a4fe7f2409afeedff092d277be604966cd6aa4307ef"},
|
||||
"thousand_island": {:hex, :thousand_island, "1.4.3", "2158209580f633be38d43ec4e3ce0a01079592b9657afff9080d5d8ca149a3af", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6e4ce09b0fd761a58594d02814d40f77daff460c48a7354a15ab353bb998ea0b"},
|
||||
"timex": {:hex, :timex, "3.7.7", "3ed093cae596a410759104d878ad7b38e78b7c2151c6190340835515d4a46b8a", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "0ec4b09f25fe311321f9fc04144a7e3affe48eb29481d7a5583849b6c4dfa0a7"},
|
||||
"toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"},
|
||||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
||||
|
|
|
|||
|
|
@ -82,12 +82,50 @@ defmodule Pleroma.HTML.Scrubber.Default do
|
|||
"recipients-inline",
|
||||
"quote-inline",
|
||||
"invisible",
|
||||
"ellipsis"
|
||||
"ellipsis",
|
||||
"mfm-center",
|
||||
"mfm-flip",
|
||||
"mfm-font",
|
||||
"mfm-blur",
|
||||
"mfm-rotate",
|
||||
"mfm-x2",
|
||||
"mfm-x3",
|
||||
"mfm-x4",
|
||||
"mfm-position",
|
||||
"mfm-scale",
|
||||
"mfm-fg",
|
||||
"mfm-bg",
|
||||
"mfm-jelly",
|
||||
"mfm-twitch",
|
||||
"mfm-shake",
|
||||
"mfm-spin",
|
||||
"mfm-jump",
|
||||
"mfm-bounce",
|
||||
"mfm-rainbow",
|
||||
"mfm-tada",
|
||||
"mfm-sparkle"
|
||||
])
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values(:p, "class", ["quote-inline"])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:span, ["lang"])
|
||||
Meta.allow_tag_with_these_attributes(:span, [
|
||||
"lang",
|
||||
"data-mfm-h",
|
||||
"data-mfm-v",
|
||||
"data-mfm-x",
|
||||
"data-mfm-y",
|
||||
"data-mfm-alternate",
|
||||
"data-mfm-speed",
|
||||
"data-mfm-deg",
|
||||
"data-mfm-left",
|
||||
"data-mfm-serif",
|
||||
"data-mfm-monospace",
|
||||
"data-mfm-cursive",
|
||||
"data-mfm-fantasy",
|
||||
"data-mfm-emoji",
|
||||
"data-mfm-math",
|
||||
"data-mfm-color"
|
||||
])
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values(:code, "class", ["inline"])
|
||||
|
||||
|
|
|
|||
|
|
@ -4,46 +4,55 @@
|
|||
"https://w3id.org/security/v1",
|
||||
"https://purl.archive.org/socialweb/webfinger",
|
||||
{
|
||||
"Emoji": "toot:Emoji",
|
||||
"as": "https://www.w3.org/ns/activitystreams#",
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"schema": "http://schema.org#",
|
||||
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
||||
|
||||
"fedibird": "http://fedibird.com/ns#",
|
||||
"litepub": "http://litepub.social/ns#",
|
||||
"sm": "http://smithereen.software/ns#",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
|
||||
"alsoKnownAs": {
|
||||
"@id": "as:alsoKnownAs",
|
||||
"@type": "@id"
|
||||
},
|
||||
"Hashtag": "as:Hashtag",
|
||||
"PropertyValue": "schema:PropertyValue",
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"quoteUrl": "as:quoteUrl",
|
||||
"sensitive": "as:sensitive",
|
||||
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"conversation": {
|
||||
"@id": "ostatus:conversation",
|
||||
"@type": "@id"
|
||||
},
|
||||
"discoverable": "toot:discoverable",
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"capabilities": "litepub:capabilities",
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"schema": "http://schema.org#",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"fedibird": "http://fedibird.com/ns#",
|
||||
|
||||
"PropertyValue": "schema:PropertyValue",
|
||||
"value": "schema:value",
|
||||
"sensitive": "as:sensitive",
|
||||
"litepub": "http://litepub.social/ns#",
|
||||
"invisible": "litepub:invisible",
|
||||
|
||||
"quoteUri": "fedibird:quoteUri",
|
||||
|
||||
"capabilities": "litepub:capabilities",
|
||||
"ChatMessage": "litepub:ChatMessage",
|
||||
"directMessage": "litepub:directMessage",
|
||||
"EmojiReact": "litepub:EmojiReact",
|
||||
"formerRepresentations": "litepub:formerRepresentations",
|
||||
"invisible": "litepub:invisible",
|
||||
"listMessage": {
|
||||
"@id": "litepub:listMessage",
|
||||
"@type": "@id"
|
||||
},
|
||||
"quoteUrl": "as:quoteUrl",
|
||||
"quoteUri": "fedibird:quoteUri",
|
||||
"oauthRegistrationEndpoint": {
|
||||
"@id": "litepub:oauthRegistrationEndpoint",
|
||||
"@type": "@id"
|
||||
},
|
||||
"EmojiReact": "litepub:EmojiReact",
|
||||
"ChatMessage": "litepub:ChatMessage",
|
||||
"alsoKnownAs": {
|
||||
"@id": "as:alsoKnownAs",
|
||||
"@type": "@id"
|
||||
},
|
||||
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
||||
"formerRepresentations": "litepub:formerRepresentations",
|
||||
"sm": "http://smithereen.software/ns#",
|
||||
"nonAnonymous": "sm:nonAnonymous"
|
||||
|
||||
"nonAnonymous": "sm:nonAnonymous",
|
||||
|
||||
"discoverable": "toot:discoverable",
|
||||
"Emoji": "toot:Emoji"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -78,14 +78,19 @@ update() {
|
|||
|
||||
RELEASE_ROOT=$(dirname "$SCRIPTPATH")
|
||||
uri="https://git.pleroma.social"
|
||||
project_id="2"
|
||||
project_branch="${BRANCH:-$(detect_branch)}"
|
||||
flavour="${FLAVOUR:-$(detect_flavour)}"
|
||||
project_name="pleroma"
|
||||
package_base="${uri}/api/packages/${project_name}/generic"
|
||||
if [ -n "$FULL_URI" ]; then
|
||||
full_uri="$FULL_URI"
|
||||
else
|
||||
project_branch="${BRANCH:-$(detect_branch)}"
|
||||
flavour="${FLAVOUR:-$(detect_flavour)}"
|
||||
full_uri="${package_base}"/pleroma-otp-"${project_branch}"-"${flavour}"/latest/pleroma.zip
|
||||
fi
|
||||
tmp="${TMP_DIR:-/tmp}"
|
||||
artifact="$tmp/pleroma.zip"
|
||||
full_uri="${FULL_URI:-${uri}/api/v4/projects/${project_id}/jobs/artifacts/${project_branch}/download?job=${flavour}}"
|
||||
echo "Downloading the artifact from ${full_uri} to ${artifact}"
|
||||
curl "$full_uri" -o "${artifact}"
|
||||
curl -fL "$full_uri" -o "${artifact}"
|
||||
echo "Unpacking ${artifact} to ${tmp}"
|
||||
unzip -q "$artifact" -d "$tmp"
|
||||
echo "Copying files over to $RELEASE_ROOT"
|
||||
|
|
@ -137,7 +142,14 @@ else
|
|||
SCRIPT=$(realpath "$0")
|
||||
SCRIPTPATH=$(dirname "$SCRIPT")
|
||||
|
||||
FULL_ARGS="$*"
|
||||
# HACK: Script arguments need to be sent as an array to Mix tasks, otherwise they will break (quoted) arguments with whitespace.
|
||||
# Previously it was sent as string, which would get split on whitespace on the task side.
|
||||
# Encode as Elixir binary literals to avoid string escaping and interpolation issues.
|
||||
PREPARED_ARGS=""
|
||||
for arg in "$@"; do
|
||||
bytes=$(printf '%s' "$arg" | od -An -v -tu1 | tr -s '[:space:]' ',' | sed 's/^,//; s/,$//')
|
||||
PREPARED_ARGS="$PREPARED_ARGS <<$bytes>>,"
|
||||
done
|
||||
|
||||
ACTION="$1"
|
||||
if [ $# -gt 0 ]; then
|
||||
|
|
@ -154,8 +166,8 @@ else
|
|||
if [ "$ACTION" = "update" ]; then
|
||||
update "$@"
|
||||
elif [ "$ACTION" = "migrate" ] || [ "$ACTION" = "rollback" ] || [ "$ACTION" = "create" ] || [ "$ACTION $SUBACTION" = "instance gen" ] || [ "$PLEROMA_CTL_RPC_DISABLED" = true ]; then
|
||||
"$SCRIPTPATH"/pleroma eval 'Pleroma.ReleaseTasks.run("'"$FULL_ARGS"'")'
|
||||
"$SCRIPTPATH"/pleroma eval 'Pleroma.ReleaseTasks.run(['"${PREPARED_ARGS%%,}"'])'
|
||||
else
|
||||
"$SCRIPTPATH"/pleroma rpc 'Pleroma.ReleaseTasks.run("'"$FULL_ARGS"'")'
|
||||
"$SCRIPTPATH"/pleroma rpc 'Pleroma.ReleaseTasks.run(['"${PREPARED_ARGS%%,}"'])'
|
||||
fi
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -144,7 +144,13 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
|
|||
quarantined_instances: [],
|
||||
managed_config: true,
|
||||
static_dir: "instance/static/",
|
||||
allowed_post_formats: ["text/plain", "text/html", "text/markdown", "text/bbcode"],
|
||||
allowed_post_formats: [
|
||||
"text/plain",
|
||||
"text/html",
|
||||
"text/markdown",
|
||||
"text/bbcode",
|
||||
"text/x.misskeymarkdown"
|
||||
],
|
||||
autofollowed_nicknames: [],
|
||||
max_pinned_statuses: 1,
|
||||
attachment_links: false,
|
||||
|
|
|
|||
293
test/pleroma/pleroma_ctl_test.exs
Normal file
293
test/pleroma/pleroma_ctl_test.exs
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.PleromaCtlTest do
|
||||
use ExUnit.Case, async: false
|
||||
|
||||
@pleroma_ctl Path.expand("../../rel/files/bin/pleroma_ctl", __DIR__)
|
||||
|
||||
setup do
|
||||
tmp_dir =
|
||||
Path.join(System.tmp_dir!(), "pleroma_ctl_test_#{System.unique_integer([:positive])}")
|
||||
|
||||
release_root = Path.join(tmp_dir, "release")
|
||||
bin_dir = Path.join(release_root, "bin")
|
||||
|
||||
File.mkdir_p!(bin_dir)
|
||||
File.cp!(@pleroma_ctl, Path.join(bin_dir, "pleroma_ctl"))
|
||||
File.chmod!(Path.join(bin_dir, "pleroma_ctl"), 0o755)
|
||||
|
||||
on_exit(fn -> File.rm_rf!(tmp_dir) end)
|
||||
|
||||
{:ok, tmp_dir: tmp_dir, release_root: release_root, bin_dir: bin_dir}
|
||||
end
|
||||
|
||||
test "update downloads branch-scoped latest OTP package", %{
|
||||
tmp_dir: tmp_dir,
|
||||
bin_dir: bin_dir,
|
||||
release_root: release_root
|
||||
} do
|
||||
stubs_dir = Path.join(tmp_dir, "stubs")
|
||||
curl_args_path = Path.join(tmp_dir, "curl.args")
|
||||
File.mkdir_p!(stubs_dir)
|
||||
write_update_stubs(stubs_dir, curl_args_path, "unused", "glibc")
|
||||
|
||||
update_tmp_dir = Path.join(tmp_dir, "update_tmp")
|
||||
File.mkdir_p!(update_tmp_dir)
|
||||
|
||||
{output, status} =
|
||||
System.cmd(
|
||||
Path.join(bin_dir, "pleroma_ctl"),
|
||||
[
|
||||
"update",
|
||||
"--branch",
|
||||
"develop",
|
||||
"--flavour",
|
||||
"amd64",
|
||||
"--tmp-dir",
|
||||
update_tmp_dir,
|
||||
"--no-rm"
|
||||
],
|
||||
env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}],
|
||||
stderr_to_stdout: true
|
||||
)
|
||||
|
||||
assert status == 0, output
|
||||
assert File.exists?(Path.join(release_root, "bin/marker"))
|
||||
|
||||
assert ["-fL", url, "-o", artifact_path] =
|
||||
curl_args_path
|
||||
|> File.read!()
|
||||
|> String.split("\n", trim: true)
|
||||
|
||||
assert url ==
|
||||
"https://git.pleroma.social/api/packages/pleroma/generic/pleroma-otp-develop-amd64/latest/pleroma.zip"
|
||||
|
||||
assert artifact_path == Path.join(update_tmp_dir, "pleroma.zip")
|
||||
end
|
||||
|
||||
test "update detects stable branch and local flavour", %{
|
||||
tmp_dir: tmp_dir,
|
||||
bin_dir: bin_dir,
|
||||
release_root: release_root
|
||||
} do
|
||||
stubs_dir = Path.join(tmp_dir, "stubs")
|
||||
curl_args_path = Path.join(tmp_dir, "curl.args")
|
||||
File.mkdir_p!(stubs_dir)
|
||||
write_update_stubs(stubs_dir, curl_args_path, "x86_64", "glibc")
|
||||
write_start_erl_data(release_root, "2.10.0")
|
||||
|
||||
{output, status} =
|
||||
System.cmd(
|
||||
Path.join(bin_dir, "pleroma_ctl"),
|
||||
["update", "--tmp-dir", create_update_tmp_dir(tmp_dir), "--no-rm"],
|
||||
env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}],
|
||||
stderr_to_stdout: true
|
||||
)
|
||||
|
||||
assert status == 0, output
|
||||
|
||||
assert curl_url(curl_args_path) ==
|
||||
"https://git.pleroma.social/api/packages/pleroma/generic/pleroma-otp-stable-amd64/latest/pleroma.zip"
|
||||
end
|
||||
|
||||
test "update detects develop branch and musl arm flavour", %{
|
||||
tmp_dir: tmp_dir,
|
||||
bin_dir: bin_dir,
|
||||
release_root: release_root
|
||||
} do
|
||||
stubs_dir = Path.join(tmp_dir, "stubs")
|
||||
curl_args_path = Path.join(tmp_dir, "curl.args")
|
||||
File.mkdir_p!(stubs_dir)
|
||||
write_update_stubs(stubs_dir, curl_args_path, "armv7l", "musl")
|
||||
write_start_erl_data(release_root, "2.10.0.develop")
|
||||
|
||||
{output, status} =
|
||||
System.cmd(
|
||||
Path.join(bin_dir, "pleroma_ctl"),
|
||||
["update", "--tmp-dir", create_update_tmp_dir(tmp_dir), "--no-rm"],
|
||||
env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}],
|
||||
stderr_to_stdout: true
|
||||
)
|
||||
|
||||
assert status == 0, output
|
||||
|
||||
assert curl_url(curl_args_path) ==
|
||||
"https://git.pleroma.social/api/packages/pleroma/generic/pleroma-otp-develop-arm-musl/latest/pleroma.zip"
|
||||
end
|
||||
|
||||
test "update with zip URL bypasses branch and flavour detection", %{
|
||||
tmp_dir: tmp_dir,
|
||||
bin_dir: bin_dir,
|
||||
release_root: release_root
|
||||
} do
|
||||
stubs_dir = Path.join(tmp_dir, "stubs")
|
||||
curl_args_path = Path.join(tmp_dir, "curl.args")
|
||||
File.mkdir_p!(stubs_dir)
|
||||
write_update_stubs(stubs_dir, curl_args_path, "unsupported-arch", "unsupported-libc")
|
||||
write_start_erl_data(release_root, "2.10.0.custombranch")
|
||||
|
||||
custom_url = "https://example.test/custom.zip"
|
||||
|
||||
{output, status} =
|
||||
System.cmd(
|
||||
Path.join(bin_dir, "pleroma_ctl"),
|
||||
[
|
||||
"update",
|
||||
"--zip-url",
|
||||
custom_url,
|
||||
"--tmp-dir",
|
||||
create_update_tmp_dir(tmp_dir),
|
||||
"--no-rm"
|
||||
],
|
||||
env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}],
|
||||
stderr_to_stdout: true
|
||||
)
|
||||
|
||||
assert status == 0, output
|
||||
assert curl_url(curl_args_path) == custom_url
|
||||
end
|
||||
|
||||
test "passes arguments with spaces and Elixir string metacharacters", %{
|
||||
tmp_dir: tmp_dir,
|
||||
bin_dir: bin_dir
|
||||
} do
|
||||
capture_path = Path.join(tmp_dir, "captured_args")
|
||||
eval_path = Path.join(tmp_dir, "pleroma_ctl_eval.exs")
|
||||
|
||||
write_executable(Path.join(bin_dir, "pleroma"), """
|
||||
#!/bin/sh
|
||||
{
|
||||
printf '%s\n' 'defmodule Pleroma.ReleaseTasks do'
|
||||
printf '%s\n' ' def run(args), do: File.write!(System.fetch_env!("PLEROMA_CTL_CAPTURE"), :erlang.term_to_binary(args))'
|
||||
printf '%s\n' 'end'
|
||||
printf '%s\n' "$2"
|
||||
} > "$PLEROMA_CTL_EVAL_FILE"
|
||||
|
||||
exec elixir "$PLEROMA_CTL_EVAL_FILE"
|
||||
""")
|
||||
|
||||
{output, status} =
|
||||
System.cmd(
|
||||
Path.join(bin_dir, "pleroma_ctl"),
|
||||
[
|
||||
"user",
|
||||
"",
|
||||
"has space",
|
||||
~s(has "quote"),
|
||||
~s(has \\ backslash),
|
||||
~S(#{:not_interpolated})
|
||||
],
|
||||
env: [
|
||||
{"PLEROMA_CTL_CAPTURE", capture_path},
|
||||
{"PLEROMA_CTL_EVAL_FILE", eval_path}
|
||||
],
|
||||
stderr_to_stdout: true
|
||||
)
|
||||
|
||||
assert status == 0, output
|
||||
|
||||
assert capture_path
|
||||
|> File.read!()
|
||||
|> :erlang.binary_to_term() == [
|
||||
"user",
|
||||
"",
|
||||
"has space",
|
||||
~s(has "quote"),
|
||||
~s(has \\ backslash),
|
||||
~S(#{:not_interpolated})
|
||||
]
|
||||
end
|
||||
|
||||
defp write_executable(path, contents) do
|
||||
File.write!(path, contents)
|
||||
File.chmod!(path, 0o755)
|
||||
end
|
||||
|
||||
defp write_start_erl_data(release_root, version) do
|
||||
releases_dir = Path.join(release_root, "releases")
|
||||
File.mkdir_p!(releases_dir)
|
||||
File.write!(Path.join(releases_dir, "start_erl.data"), "erts-15.0 #{version}\n")
|
||||
end
|
||||
|
||||
defp create_update_tmp_dir(tmp_dir) do
|
||||
update_tmp_dir = Path.join(tmp_dir, "update_tmp")
|
||||
File.mkdir_p!(update_tmp_dir)
|
||||
update_tmp_dir
|
||||
end
|
||||
|
||||
defp write_update_stubs(stubs_dir, curl_args_path, arch, libc) do
|
||||
write_executable(Path.join(stubs_dir, "curl"), """
|
||||
#!/bin/sh
|
||||
printf '%s\n' "$@" > "#{curl_args_path}"
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-o)
|
||||
artifact="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
: > "$artifact"
|
||||
""")
|
||||
|
||||
write_executable(Path.join(stubs_dir, "unzip"), """
|
||||
#!/bin/sh
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-d)
|
||||
dest="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
mkdir -p "$dest/release/bin"
|
||||
printf 'marker' > "$dest/release/bin/marker"
|
||||
""")
|
||||
|
||||
write_executable(Path.join(stubs_dir, "uname"), """
|
||||
#!/bin/sh
|
||||
printf '%s\n' '#{arch}'
|
||||
""")
|
||||
|
||||
write_executable(Path.join(stubs_dir, "getconf"), getconf_stub(libc))
|
||||
|
||||
write_executable(Path.join(stubs_dir, "ldd"), """
|
||||
#!/bin/sh
|
||||
printf '%s\n' 'musl libc (mock)'
|
||||
""")
|
||||
end
|
||||
|
||||
defp getconf_stub("glibc") do
|
||||
"""
|
||||
#!/bin/sh
|
||||
printf '%s\n' 'glibc 2.40'
|
||||
"""
|
||||
end
|
||||
|
||||
defp getconf_stub(_libc) do
|
||||
"""
|
||||
#!/bin/sh
|
||||
exit 1
|
||||
"""
|
||||
end
|
||||
|
||||
defp curl_url(curl_args_path) do
|
||||
["-fL", url, "-o", _artifact_path] =
|
||||
curl_args_path
|
||||
|> File.read!()
|
||||
|> String.split("\n", trim: true)
|
||||
|
||||
url
|
||||
end
|
||||
end
|
||||
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.SignatureTest do
|
|||
import Mock
|
||||
|
||||
alias Pleroma.Signature
|
||||
alias Pleroma.StubbedHTTPSignaturesMock, as: HTTPSignaturesMock
|
||||
|
||||
setup do
|
||||
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
|
|
@ -103,6 +104,18 @@ defmodule Pleroma.SignatureTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "validate_signature/1" do
|
||||
test "treats HTTP signature errors as failed validation" do
|
||||
conn = %Plug.Conn{method: "GET", request_path: "/inbox", req_headers: []}
|
||||
|
||||
Mox.expect(HTTPSignaturesMock, :validate_conn, fn _conn ->
|
||||
{:error, :request_target_header}
|
||||
end)
|
||||
|
||||
assert Signature.validate_signature(conn) == false
|
||||
end
|
||||
end
|
||||
|
||||
describe "key_id_to_actor_id/1" do
|
||||
test "it properly deduces the actor id for misskey" do
|
||||
assert Signature.key_id_to_actor_id("https://example.com/users/1234/publickey") ==
|
||||
|
|
|
|||
195
test/pleroma/user/search_test.exs
Normal file
195
test/pleroma/user/search_test.exs
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.User.SearchTest do
|
||||
use Pleroma.DataCase, async: false
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
||||
describe "search/2 mention suggestions" do
|
||||
test "prioritizes followed/follower users before others" do
|
||||
user = insert(:user)
|
||||
|
||||
related =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "hj@real.example",
|
||||
ap_id: "https://real.example/users/hj",
|
||||
last_status_at: ~N[2020-01-01 00:00:00]
|
||||
)
|
||||
|
||||
other = insert(:user, nickname: "hj", last_status_at: ~N[2020-01-02 00:00:00])
|
||||
|
||||
{:ok, _related, _user} = User.follow(related, user)
|
||||
|
||||
results = User.search("hj", for_user: user) |> Enum.map(& &1.id)
|
||||
|
||||
assert results == [related.id, other.id]
|
||||
end
|
||||
|
||||
test "orders followed/follower users by most recent activity" do
|
||||
user = insert(:user)
|
||||
|
||||
older =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "ali@remote.example",
|
||||
ap_id: "https://remote.example/users/ali",
|
||||
last_status_at: ~N[2020-01-01 00:00:00]
|
||||
)
|
||||
|
||||
newer =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "alia@remote.example",
|
||||
ap_id: "https://remote.example/users/alia",
|
||||
last_status_at: ~N[2020-01-02 00:00:00]
|
||||
)
|
||||
|
||||
{:ok, _user, _older} = User.follow(user, older)
|
||||
{:ok, _user, _newer} = User.follow(user, newer)
|
||||
|
||||
assert [newer.id, older.id] ==
|
||||
User.search("ali", for_user: user)
|
||||
|> Enum.map(& &1.id)
|
||||
end
|
||||
|
||||
test "groups followed/follower users first and sorts them by recency" do
|
||||
user = insert(:user)
|
||||
|
||||
following_newest =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "mentiontesta@related.example",
|
||||
ap_id: "https://related.example/users/mentiontesta",
|
||||
last_status_at: ~N[2020-01-03 00:00:00]
|
||||
)
|
||||
|
||||
follower_middle =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "mentiontestb@related.example",
|
||||
ap_id: "https://related.example/users/mentiontestb",
|
||||
last_status_at: ~N[2020-01-02 00:00:00]
|
||||
)
|
||||
|
||||
mutual_oldest =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "mentiontestc@related.example",
|
||||
ap_id: "https://related.example/users/mentiontestc",
|
||||
last_status_at: ~N[2020-01-01 00:00:00]
|
||||
)
|
||||
|
||||
unrelated_newer =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "mentiontestd@unrelated.example",
|
||||
ap_id: "https://unrelated.example/users/mentiontestd",
|
||||
last_status_at: ~N[2020-01-04 00:00:00]
|
||||
)
|
||||
|
||||
{:ok, _user, _following_newest} = User.follow(user, following_newest)
|
||||
{:ok, _follower_middle, _user} = User.follow(follower_middle, user)
|
||||
|
||||
{:ok, _user, _mutual_oldest} = User.follow(user, mutual_oldest)
|
||||
{:ok, _mutual_oldest, _user} = User.follow(mutual_oldest, user)
|
||||
|
||||
results = User.search("mentiontest", for_user: user)
|
||||
|
||||
assert Enum.map(results, & &1.id) ==
|
||||
[following_newest.id, follower_middle.id, mutual_oldest.id, unrelated_newer.id]
|
||||
end
|
||||
|
||||
test "uses last_active_at when last_status_at is missing" do
|
||||
user = insert(:user)
|
||||
|
||||
older =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "activefallbacka@remote.example",
|
||||
ap_id: "https://remote.example/users/activefallbacka",
|
||||
last_status_at: nil,
|
||||
last_active_at: ~N[2020-01-01 00:00:00]
|
||||
)
|
||||
|
||||
newer =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "activefallbackb@remote.example",
|
||||
ap_id: "https://remote.example/users/activefallbackb",
|
||||
last_status_at: nil,
|
||||
last_active_at: ~N[2020-01-02 00:00:00]
|
||||
)
|
||||
|
||||
{:ok, _user, _older} = User.follow(user, older)
|
||||
{:ok, _user, _newer} = User.follow(user, newer)
|
||||
|
||||
assert [newer.id, older.id] ==
|
||||
User.search("activefallback", for_user: user)
|
||||
|> Enum.map(& &1.id)
|
||||
end
|
||||
|
||||
test "does not return deactivated users even if related" do
|
||||
user = insert(:user)
|
||||
|
||||
active =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "deactivatedtesta@remote.example",
|
||||
ap_id: "https://remote.example/users/deactivatedtesta",
|
||||
last_status_at: ~N[2020-01-02 00:00:00]
|
||||
)
|
||||
|
||||
deactivated =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "deactivatedtestb@remote.example",
|
||||
ap_id: "https://remote.example/users/deactivatedtestb",
|
||||
last_status_at: ~N[2020-01-03 00:00:00]
|
||||
)
|
||||
|
||||
{:ok, _user, _active} = User.follow(user, active)
|
||||
{:ok, _user, _deactivated} = User.follow(user, deactivated)
|
||||
Repo.update!(Ecto.Changeset.change(deactivated, is_active: false))
|
||||
|
||||
results = User.search("deactivatedtest", for_user: user) |> Enum.map(& &1.id)
|
||||
|
||||
assert results == [active.id]
|
||||
end
|
||||
|
||||
test "does not return users from unreachable instances" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, _instance} = Instances.set_unreachable("dead.example")
|
||||
|
||||
dead =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "ali@dead.example",
|
||||
ap_id: "https://dead.example/users/ali",
|
||||
last_status_at: ~N[2020-01-02 00:00:00]
|
||||
)
|
||||
|
||||
alive =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "ali@alive.example",
|
||||
ap_id: "https://alive.example/users/ali",
|
||||
last_status_at: ~N[2020-01-02 00:00:00]
|
||||
)
|
||||
|
||||
{:ok, _user, _alive} = User.follow(user, alive)
|
||||
{:ok, _user, _dead} = User.follow(user, dead)
|
||||
|
||||
results = User.search("ali", for_user: user) |> Enum.map(& &1.id)
|
||||
|
||||
assert results == [alive.id]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -876,17 +876,17 @@ defmodule Pleroma.UserTest do
|
|||
describe "get_or_fetch/1 remote users with tld, while BE is running on a subdomain" do
|
||||
setup do: clear_config([Pleroma.Web.WebFinger, :update_nickname_on_user_fetch], true)
|
||||
|
||||
test "for mastodon" do
|
||||
ap_id = "a@mastodon.example"
|
||||
{:ok, fetched_user} = User.get_or_fetch(ap_id)
|
||||
test "fetches a mastodon split-domain nickname" do
|
||||
nickname = "a@mastodon.example"
|
||||
{:ok, fetched_user} = User.get_or_fetch(nickname)
|
||||
|
||||
assert fetched_user.ap_id == "https://sub.mastodon.example/users/a"
|
||||
assert fetched_user.nickname == "a@mastodon.example"
|
||||
end
|
||||
|
||||
test "for pleroma" do
|
||||
ap_id = "a@pleroma.example"
|
||||
{:ok, fetched_user} = User.get_or_fetch(ap_id)
|
||||
test "fetches a pleroma split-domain nickname" do
|
||||
nickname = "a@pleroma.example"
|
||||
{:ok, fetched_user} = User.get_or_fetch(nickname)
|
||||
|
||||
assert fetched_user.ap_id == "https://sub.pleroma.example/users/a"
|
||||
assert fetched_user.nickname == "a@pleroma.example"
|
||||
|
|
@ -936,6 +936,89 @@ defmodule Pleroma.UserTest do
|
|||
assert fetched_user == "not found nonexistent"
|
||||
end
|
||||
|
||||
test "does not rename an existing remote actor from rogue WebFinger data" do
|
||||
clear_config([Pleroma.Web.WebFinger, :update_nickname_on_user_fetch], true)
|
||||
|
||||
actor_id = "https://legit-actor.example/users/alice"
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{url: "https://evil-webfinger.example/.well-known/host-meta"} ->
|
||||
{:ok, %Tesla.Env{status: 404}}
|
||||
|
||||
%{
|
||||
url:
|
||||
"https://evil-webfinger.example/.well-known/webfinger?resource=acct:claimed@evil-webfinger.example"
|
||||
} ->
|
||||
Tesla.Mock.json(%{
|
||||
"subject" => "acct:claimed@evil-webfinger.example",
|
||||
"links" => [
|
||||
%{
|
||||
"rel" => "self",
|
||||
"type" => "application/activity+json",
|
||||
"href" => actor_id
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
%{url: ^actor_id} ->
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [{"content-type", "application/activity+json"}],
|
||||
body:
|
||||
Jason.encode!(%{
|
||||
"id" => actor_id,
|
||||
"type" => "Person",
|
||||
"preferredUsername" => "alice",
|
||||
"name" => "Alice",
|
||||
"summary" => "",
|
||||
"inbox" => "https://legit-actor.example/users/alice/inbox",
|
||||
"outbox" => "https://legit-actor.example/users/alice/outbox",
|
||||
"followers" => "https://legit-actor.example/users/alice/followers",
|
||||
"following" => "https://legit-actor.example/users/alice/following"
|
||||
})
|
||||
}}
|
||||
|
||||
%{url: "https://legit-actor.example/.well-known/host-meta"} ->
|
||||
{:ok, %Tesla.Env{status: 404}}
|
||||
|
||||
%{
|
||||
url:
|
||||
"https://legit-actor.example/.well-known/webfinger?resource=acct:alice@legit-actor.example"
|
||||
} ->
|
||||
Tesla.Mock.json(%{
|
||||
"subject" => "acct:alice@legit-actor.example",
|
||||
"links" => [
|
||||
%{
|
||||
"rel" => "self",
|
||||
"type" => "application/activity+json",
|
||||
"href" => actor_id
|
||||
}
|
||||
]
|
||||
})
|
||||
end)
|
||||
|
||||
assert {:error, {:webfinger_actor_mismatch, "claimed@evil-webfinger.example", ^actor_id}} =
|
||||
ActivityPub.make_user_from_nickname("claimed@evil-webfinger.example")
|
||||
|
||||
refute User.get_by_ap_id(actor_id)
|
||||
refute User.get_by_nickname("claimed@evil-webfinger.example")
|
||||
|
||||
orig_user =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "alice@legit-actor.example",
|
||||
ap_id: actor_id
|
||||
)
|
||||
|
||||
assert {:error, {:webfinger_actor_mismatch, "claimed@evil-webfinger.example", ^actor_id}} =
|
||||
ActivityPub.make_user_from_nickname("claimed@evil-webfinger.example")
|
||||
|
||||
assert {:error, _} = User.get_or_fetch_by_nickname("claimed@evil-webfinger.example")
|
||||
assert User.get_by_id(orig_user.id).nickname == "alice@legit-actor.example"
|
||||
refute User.get_by_nickname("claimed@evil-webfinger.example")
|
||||
end
|
||||
|
||||
test "updates an existing user, if stale" do
|
||||
a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Workers.ReceiverWorker
|
||||
alias Pleroma.Workers.SignatureRetryWorker
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
|
|
@ -36,6 +37,36 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
setup do: clear_config([:instance, :federating], true)
|
||||
|
||||
defp assign_valid_signature_for_actor(conn, %User{ap_id: actor_id}) do
|
||||
assign_valid_signature_for_actor(conn, actor_id)
|
||||
end
|
||||
|
||||
defp assign_valid_signature_for_actor(conn, actor) do
|
||||
actor_id = Utils.get_ap_id(actor)
|
||||
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> put_req_header("signature", "keyId=\"#{actor_id}#main-key\"")
|
||||
end
|
||||
|
||||
defp expect_signature_retry_from(%User{} = signer) do
|
||||
signer_json = UserView.render("user.json", %{user: signer}) |> Map.delete("featured")
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{url: url} when url == signer.ap_id ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: Jason.encode!(signer_json),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
|
||||
env ->
|
||||
apply(HttpRequestMock, :request, [env])
|
||||
end)
|
||||
|
||||
Mox.expect(Pleroma.StubbedHTTPSignaturesMock, :validate_conn, fn _conn -> true end)
|
||||
end
|
||||
|
||||
describe "/relay" do
|
||||
setup do: clear_config([:instance, :allow_relay])
|
||||
|
||||
|
|
@ -688,7 +719,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", data)
|
||||
|
||||
|
|
@ -716,7 +747,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", data)
|
||||
|
||||
|
|
@ -726,6 +757,199 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
assert Activity.get_by_ap_id(data["id"])
|
||||
end
|
||||
|
||||
test "does not create a forged post after failed signature retry", %{conn: conn} do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
object_id = "https://two.com/objects/inbox-forged-note"
|
||||
|
||||
data = %{
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/inbox-forged-create",
|
||||
"context" => "https://two.com/contexts/inbox-forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => object_id,
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"context" => "https://two.com/contexts/inbox-forged-create",
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
expect_signature_retry_from(alice)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, false)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> put_req_header("signature", "keyId=\"https://one.com/users/alice#main-key\"")
|
||||
|> post("/inbox", data)
|
||||
|
||||
assert "ok" == json_response(conn, 200)
|
||||
|
||||
assert [{:cancel, :actor_signature_mismatch}] =
|
||||
ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker))
|
||||
|
||||
refute Activity.get_by_ap_id(data["id"])
|
||||
refute Object.get_by_ap_id(object_id)
|
||||
end
|
||||
|
||||
test "does not create a forged like after failed signature retry", %{conn: conn} do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
|
||||
data = %{
|
||||
"type" => "Like",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/inbox-forged-like",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
expect_signature_retry_from(alice)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, false)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> put_req_header("signature", "keyId=\"https://one.com/users/alice#main-key\"")
|
||||
|> post("/inbox", data)
|
||||
|
||||
assert "ok" == json_response(conn, 200)
|
||||
|
||||
assert [{:cancel, :actor_signature_mismatch}] =
|
||||
ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker))
|
||||
|
||||
refute Activity.get_by_ap_id(data["id"])
|
||||
end
|
||||
|
||||
test "does not delete an object after failed signature retry", %{conn: conn} do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
object_id = note.data["id"]
|
||||
|
||||
data = %{
|
||||
"type" => "Delete",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/inbox-forged-delete",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => object_id
|
||||
}
|
||||
|
||||
expect_signature_retry_from(alice)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, false)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> put_req_header("signature", "keyId=\"https://one.com/users/alice#main-key\"")
|
||||
|> post("/inbox", data)
|
||||
|
||||
assert "ok" == json_response(conn, 200)
|
||||
|
||||
assert [{:cancel, :actor_signature_mismatch}] =
|
||||
ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker))
|
||||
|
||||
refute Activity.get_by_ap_id(data["id"])
|
||||
assert %Object{data: %{"type" => "Note"}} = Object.get_by_ap_id(object_id)
|
||||
end
|
||||
|
||||
test "does not create a forged post signed by a different actor", %{conn: conn} do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
object_id = "https://two.com/objects/inbox-signed-forged-note"
|
||||
|
||||
data = %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/inbox-signed-forged-create",
|
||||
"context" => "https://two.com/contexts/inbox-signed-forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => object_id,
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"context" => "https://two.com/contexts/inbox-signed-forged-create",
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
expect_signature_retry_from(alice)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> put_req_header("date", "Thu, 25 Jul 2024 13:33:31 GMT")
|
||||
|> put_req_header("digest", "SHA-256=fake-digest")
|
||||
|> put_req_header(
|
||||
"signature",
|
||||
"keyId=\"#{alice.ap_id}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\""
|
||||
)
|
||||
|> post("/inbox", data)
|
||||
|
||||
assert conn.assigns.valid_signature == false
|
||||
assert "ok" == json_response(conn, 200)
|
||||
|
||||
assert [{:cancel, :actor_signature_mismatch}] =
|
||||
ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker))
|
||||
|
||||
refute Activity.get_by_ap_id(data["id"])
|
||||
refute Object.get_by_ap_id(object_id)
|
||||
end
|
||||
|
||||
test "does not create a forged like signed by a different actor", %{conn: conn} do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
|
||||
data = %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Like",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/inbox-signed-forged-like",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
expect_signature_retry_from(alice)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> put_req_header("date", "Thu, 25 Jul 2024 13:33:31 GMT")
|
||||
|> put_req_header("digest", "SHA-256=fake-digest")
|
||||
|> put_req_header(
|
||||
"signature",
|
||||
"keyId=\"#{alice.ap_id}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\""
|
||||
)
|
||||
|> post("/inbox", data)
|
||||
|
||||
assert conn.assigns.valid_signature == false
|
||||
assert "ok" == json_response(conn, 200)
|
||||
|
||||
assert [{:cancel, :actor_signature_mismatch}] =
|
||||
ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker))
|
||||
|
||||
refute Activity.get_by_ap_id(data["id"])
|
||||
end
|
||||
|
||||
test "accept follow activity", %{conn: conn} do
|
||||
clear_config([:instance, :federating], true)
|
||||
relay = Relay.get_actor()
|
||||
|
|
@ -742,7 +966,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert "ok" ==
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(followed_relay)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", accept)
|
||||
|> json_response(200)
|
||||
|
|
@ -822,16 +1046,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
test "Unknown activity types are discarded", %{conn: conn} do
|
||||
unknown_types = ["Poke", "Read", "Dazzle"]
|
||||
|
||||
actor =
|
||||
insert(:user, local: false, ap_id: "https://unknown.mastodon.instance/users/somebody")
|
||||
|
||||
Enum.each(unknown_types, fn bad_type ->
|
||||
params =
|
||||
%{
|
||||
"type" => bad_type,
|
||||
"actor" => "https://unknown.mastodon.instance/users/somebody"
|
||||
"actor" => actor.ap_id
|
||||
}
|
||||
|> Jason.encode!()
|
||||
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(actor)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", params)
|
||||
|> json_response(400)
|
||||
|
|
@ -900,7 +1127,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert "ok" ==
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", data)
|
||||
|> json_response(200)
|
||||
|
|
@ -921,7 +1148,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert "ok" ==
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", data)
|
||||
|> json_response(200)
|
||||
|
|
@ -990,7 +1217,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert "ok" ==
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", data)
|
||||
|> json_response(200)
|
||||
|
|
@ -1009,7 +1236,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert "ok" ==
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", data)
|
||||
|> json_response(200)
|
||||
|
|
@ -1040,7 +1267,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1061,7 +1288,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1082,7 +1309,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1106,7 +1333,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1133,7 +1360,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1163,7 +1390,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{recipient.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1228,7 +1455,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
}
|
||||
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{recipient.nickname}/inbox", data)
|
||||
|> json_response(200)
|
||||
|
|
@ -1318,7 +1545,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
}
|
||||
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{reported_user.nickname}/inbox", data)
|
||||
|> json_response(200)
|
||||
|
|
@ -1372,7 +1599,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
}
|
||||
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{reported_user.nickname}/inbox", data)
|
||||
|> json_response(200)
|
||||
|
|
@ -1405,7 +1632,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1428,7 +1655,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1451,7 +1678,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -2571,6 +2798,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
setup do: clear_config([:media_proxy])
|
||||
setup do: clear_config([Pleroma.Upload])
|
||||
|
||||
# majic's libmagic port is unavailable on local Darwin runs; Linux CI still runs this test.
|
||||
@tag :skip_darwin
|
||||
test "POST /api/ap/upload_media", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
defp regexes_match!([],[]), do: true
|
||||
defp regexes_match!([], []), do: true
|
||||
|
||||
defp regexes_match!([authority | authority_rest], [checked | checked_rest]) do
|
||||
authority.source == checked.source and regexes_match!(authority_rest, checked_rest)
|
||||
|
|
|
|||
|
|
@ -149,6 +149,171 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
|
|||
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
|
||||
end
|
||||
|
||||
test "a Misskey MFM note is rendered from source content" do
|
||||
user = insert(:user, ap_id: "https://misskey.example/users/alice")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/1",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "original content",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"source" => %{
|
||||
"content" => "$[spin.speed=1s mfm goes here] <script>alert('xss')</script>",
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content, source: source}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert source["mediaType"] == "text/x.misskeymarkdown"
|
||||
assert content =~ ~s(class="mfm-spin")
|
||||
assert content =~ ~s(data-mfm-speed="1s")
|
||||
assert content =~ "mfm goes here"
|
||||
refute content =~ "original content"
|
||||
refute content =~ "<script"
|
||||
end
|
||||
|
||||
test "a Misskey MFM note resolves only cached AP mention tags" do
|
||||
remote_user = insert(:user, ap_id: "https://misskey.example/users/carol")
|
||||
local_user = insert(:user, nickname: "local_user")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/3",
|
||||
"type" => "Note",
|
||||
"actor" => remote_user.ap_id,
|
||||
"attributedTo" => remote_user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "original content",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"tag" => [
|
||||
%{
|
||||
"type" => "Mention",
|
||||
"name" => "@local_user",
|
||||
"href" => local_user.ap_id
|
||||
},
|
||||
%{
|
||||
"type" => "Mention",
|
||||
"name" => "@uncached",
|
||||
"href" => "https://misskey.example/users/uncached"
|
||||
}
|
||||
],
|
||||
"source" => %{
|
||||
"content" => "@local_user @uncached $[spin hello]",
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert content =~ local_user.ap_id
|
||||
assert content =~ "@uncached"
|
||||
end
|
||||
|
||||
test "a Misskey MFM note drops oversized source content instead of parsing it" do
|
||||
user = insert(:user, ap_id: "https://misskey.example/users/oversized")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/4",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "<span class=\"mfm-spin\">safe fallback</span>",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"source" => %{
|
||||
"content" => String.duplicate("x", 5_001),
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content, source: source}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert content == "<span class=\"mfm-spin\">safe fallback</span>"
|
||||
refute Map.has_key?(source, "content")
|
||||
end
|
||||
|
||||
test "a note drops oversized non-MFM source content" do
|
||||
user = insert(:user, ap_id: "https://example.com/users/source")
|
||||
|
||||
note = %{
|
||||
"id" => "https://example.com/notes/1",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "regular content",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"source" => %{
|
||||
"content" => String.duplicate("x", 5_001),
|
||||
"mediaType" => "text/markdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{source: source}} = ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert source == %{"mediaType" => "text/markdown"}
|
||||
end
|
||||
|
||||
test "a Misskey MFM note with legacy _misskey_content is rendered" do
|
||||
user = insert(:user, ap_id: "https://misskey.example/users/legacy")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/5",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "original content",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"_misskey_content" => "$[spin legacy]"
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content, source: source}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert source == %{"content" => "$[spin legacy]", "mediaType" => "text/x.misskeymarkdown"}
|
||||
assert content =~ ~s(class="mfm-spin")
|
||||
assert content =~ "legacy"
|
||||
end
|
||||
|
||||
test "a Misskey MFM note with htmlMfm is scrubbed but not rendered from source content" do
|
||||
user = insert(:user, ap_id: "https://misskey.example/users/bob")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/2",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" =>
|
||||
"<span class=\"mfm-spin\">already rendered</span><script>alert('xss')</script>",
|
||||
"htmlMfm" => true,
|
||||
"context" => Utils.generate_context_id(),
|
||||
"source" => %{
|
||||
"content" => String.duplicate("x", 5_001),
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content, htmlMfm: true, source: source}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert content == "<span class=\"mfm-spin\">already rendered</span>alert('xss')"
|
||||
refute Map.has_key?(source, "content")
|
||||
end
|
||||
|
||||
test "a Note with validated likes collection validates" do
|
||||
insert(:user, ap_id: "https://pol.social/users/mkljczk")
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do
|
|||
assert {:ok, _update, []} = ObjectValidator.validate(valid_update, [])
|
||||
end
|
||||
|
||||
test "returns an error if the object can't be updated by the actor", %{
|
||||
test "returns an error if the object can't be updated by the actor (different domain)", %{
|
||||
valid_update: valid_update
|
||||
} do
|
||||
other_user = insert(:user, local: false)
|
||||
|
|
@ -41,27 +41,72 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do
|
|||
assert {:error, _cng} = ObjectValidator.validate(update, [])
|
||||
end
|
||||
|
||||
test "validates as long as the object is same-origin with the actor", %{
|
||||
test "returns an error if the object can't be updated by the actor (same domain)", %{
|
||||
user: user,
|
||||
valid_update: valid_update
|
||||
} do
|
||||
other_user = insert(:user)
|
||||
user_ap_id = user.ap_id
|
||||
user_domain = URI.parse(user_ap_id).host
|
||||
other_user = insert(:user, local: false, domain: user_domain)
|
||||
|
||||
update =
|
||||
valid_update
|
||||
|> Map.put("actor", other_user.ap_id)
|
||||
|
||||
assert {:ok, _update, []} = ObjectValidator.validate(update, [])
|
||||
assert {:error, _cng} = ObjectValidator.validate(update, [])
|
||||
end
|
||||
|
||||
test "validates if the object is not of an Actor type" do
|
||||
note = insert(:note)
|
||||
test "validates if the object is not of an Actor type", %{user: user} do
|
||||
note = insert(:note, user: user)
|
||||
updated_note = note.data |> Map.put("content", "edited content")
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, update, _} = Builder.update(other_user, updated_note)
|
||||
{:ok, update, _} = Builder.update(user, updated_note)
|
||||
|
||||
assert {:ok, _update, _} = ObjectValidator.validate(update, [])
|
||||
end
|
||||
|
||||
test "returns an error if the remote update target is unknown" do
|
||||
remote_user = insert(:user, local: false, ap_id: "https://example.com/users/alice")
|
||||
|
||||
update = %{
|
||||
"type" => "Update",
|
||||
"actor" => remote_user.ap_id,
|
||||
"id" => "https://example.com/activities/update-unknown-object",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "https://example.com/objects/unknown",
|
||||
"actor" => remote_user.ap_id,
|
||||
"content" => "edited content",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"updated" => "2024-07-25T13:34:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
assert {:error, %Ecto.Changeset{} = cng} = ObjectValidator.validate(update, local: false)
|
||||
refute cng.valid?
|
||||
assert Keyword.has_key?(cng.errors, :object)
|
||||
end
|
||||
|
||||
test "returns an error if the remote update target IRI is unknown" do
|
||||
remote_user = insert(:user, local: false, ap_id: "https://example.com/users/alice")
|
||||
|
||||
update = %{
|
||||
"type" => "Update",
|
||||
"actor" => remote_user.ap_id,
|
||||
"id" => "https://example.com/activities/update-unknown-object-iri",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => "https://example.com/objects/unknown-iri"
|
||||
}
|
||||
|
||||
assert {:error, %Ecto.Changeset{} = cng} = ObjectValidator.validate(update, local: false)
|
||||
refute cng.valid?
|
||||
assert Keyword.has_key?(cng.errors, :object)
|
||||
end
|
||||
end
|
||||
|
||||
describe "update note" do
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue