diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 88789035d..b34c7e98d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,23 +1,29 @@ image: elixir:1.8.1 -variables: +variables: &global_variables POSTGRES_DB: pleroma_test POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres DB_HOST: postgres MIX_ENV: test + DOCKER_DRIVER: overlay2 + DOCKER_HOST: unix:///var/run/docker.sock + DOCKER_IMAGE: $CI_REGISTRY_IMAGE:latest + DOCKER_IMAGE_SHA: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA -cache: +cache: &global_cache_policy key: ${CI_COMMIT_REF_SLUG} paths: - - deps - - _build + - deps + - _build + stages: - build - test - benchmark - deploy - release + - docker before_script: - mix local.hex --force @@ -46,6 +52,10 @@ benchmark: unit-testing: stage: test + cache: &testing_cache_policy + <<: *global_cache_policy + policy: pull + services: - name: postgres:9.6 alias: postgres @@ -58,6 +68,7 @@ unit-testing: federated-testing: stage: test + cache: *testing_cache_policy services: - name: minibikini/postgres-with-rum:12 alias: postgres @@ -71,11 +82,13 @@ federated-testing: unit-testing-rum: stage: test + cache: *testing_cache_policy services: - name: minibikini/postgres-with-rum:12 alias: postgres command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] variables: + <<: *global_variables RUM_ENABLED: "true" script: - mix deps.get @@ -86,17 +99,20 @@ unit-testing-rum: lint: stage: test + cache: *testing_cache_policy script: - mix format --check-formatted analysis: stage: test + cache: *testing_cache_policy script: - mix deps.get - mix credo --strict --only=warnings,todo,fixme,consistency,readability docs-deploy: stage: deploy + cache: *testing_cache_policy image: alpine:latest only: - stable@pleroma/pleroma @@ -254,3 +270,20 @@ arm64-musl: variables: *release-variables before_script: *before-release-musl script: *release + +docker: + stage: docker + image: docker:latest + tags: + - dind + before_script: &before-docker + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - export CI_JOB_TIMESTAMP=$(date --utc -Iseconds) + - export CI_VCS_REF=$CI_COMMIT_SHORT_SHA + script: + - docker pull $DOCKER_IMAGE || true + - docker build --cache-from $DOCKER_IMAGE --build-arg VCS_REF=$CI_VCS_REF --build-arg BUILD_DATE=$CI_JOB_TIMESTAMP -t $DOCKER_IMAGE_SHA -t $DOCKER_IMAGE . + - docker push $DOCKER_IMAGE_SHA + - docker push $DOCKER_IMAGE + only: + - develop diff --git a/CHANGELOG.md b/CHANGELOG.md index feb3f9b3c..e864cd88d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Removed - **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media` - **Breaking**: OStatus protocol support +- **Breaking**: MDII uploader ### Changed +- **Breaking:** attachments are removed along with statuses when there are no other references to it - **Breaking:** Elixir >=1.8 is now required (was >= 1.7) - **Breaking:** attachment links (`config :pleroma, :instance, no_attachment_links` and `config :pleroma, Pleroma.Upload, link_name`) disabled by default - Replaced [pleroma_job_queue](https://git.pleroma.social/pleroma/pleroma_job_queue) and `Pleroma.Web.Federator.RetryQueue` with [Oban](https://github.com/sorentwo/oban) (see [`docs/config.md`](docs/config.md) on migrating customized worker / retry settings) @@ -21,6 +23,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Deprecated `User.Info` embedded schema (fields moved to `User`) - Store status data inside Flag activity - Deprecated (reorganized as `UserRelationship` entity) User fields with user AP IDs (`blocks`, `mutes`, `muted_reblogs`, `muted_notifications`, `subscribers`). +- Logger: default log level changed from `warn` to `info`.
API Changes @@ -51,6 +54,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Support for `X-Forwarded-For` and similar HTTP headers which used by reverse proxies to pass a real user IP address to the backend. Must not be enabled unless your instance is behind at least one reverse proxy (such as Nginx, Apache HTTPD or Varnish Cache). - MRF: New module which handles incoming posts based on their age. By default, all incoming posts that are older than 2 days will be unlisted and not shown to their followers. - User notification settings: Add `privacy_option` option. +- Support for custom Elixir modules (such as MRF policies) - User settings: Add _This account is a_ option. - OAuth: admin scopes support (relevant setting: `[:auth, :enforce_oauth_admin_scope_usage]`).
@@ -104,6 +108,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Mastodon API: Inability to get some local users by nickname in `/api/v1/accounts/:id_or_nickname` - AdminAPI: If some status received reports both in the "new" format and "old" format it was considered reports on two different statuses (in the context of grouped reports) - Admin API: Error when trying to update reports in the "old" format +- Mastodon API: Marking a conversation as read (`POST /api/v1/conversations/:id/read`) now no longer brings it to the top in the user's direct conversation list
## [1.1.6] - 2019-11-19 diff --git a/Dockerfile b/Dockerfile index c61dcfde9..4f7f12716 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,20 @@ RUN apk add git gcc g++ musl-dev make &&\ FROM alpine:3.9 +ARG BUILD_DATE +ARG VCS_REF + +LABEL maintainer="ops@pleroma.social" \ + org.opencontainers.image.title="pleroma" \ + org.opencontainers.image.description="Pleroma for Docker" \ + org.opencontainers.image.authors="ops@pleroma.social" \ + org.opencontainers.image.vendor="pleroma.social" \ + org.opencontainers.image.documentation="https://git.pleroma.social/pleroma/pleroma" \ + org.opencontainers.image.licenses="AGPL-3.0" \ + org.opencontainers.image.url="https://pleroma.social" \ + org.opencontainers.image.revision=$VCS_REF \ + org.opencontainers.image.created=$BUILD_DATE + ARG HOME=/opt/pleroma ARG DATA=/var/lib/pleroma diff --git a/config/benchmark.exs b/config/benchmark.exs index dd99cf5fd..84c6782a2 100644 --- a/config/benchmark.exs +++ b/config/benchmark.exs @@ -82,3 +82,11 @@ IO.puts("RUM enabled: #{rum_enabled}") config :pleroma, Pleroma.ReverseProxy.Client, Pleroma.ReverseProxy.ClientMock + +if File.exists?("./config/benchmark.secret.exs") do + import_config "benchmark.secret.exs" +else + IO.puts( + "You may want to create benchmark.secret.exs to declare custom database connection parameters." + ) +end diff --git a/config/config.exs b/config/config.exs index 47098858b..d41abf090 100644 --- a/config/config.exs +++ b/config/config.exs @@ -67,7 +67,7 @@ config :pleroma, Pleroma.Captcha, enabled: true, - seconds_valid: 60, + seconds_valid: 300, method: Pleroma.Captcha.Native config :pleroma, Pleroma.Captcha.Kocaptcha, endpoint: "https://captcha.kotobank.ch" @@ -108,10 +108,6 @@ streaming_enabled: true, public_endpoint: "https://s3.amazonaws.com" -config :pleroma, Pleroma.Uploaders.MDII, - cgi: "https://mdii.sakura.ne.jp/mdii-post.cgi", - files: "https://mdii.sakura.ne.jp" - config :pleroma, :emoji, shortcode_globs: ["/emoji/custom/**/*.png"], pack_extensions: [".png", ".gif"], @@ -621,6 +617,8 @@ activity_pub: nil, activity_pub_question: 30_000 +config :pleroma, :modules, runtime_dir: "instance/modules" + config :swarm, node_blacklist: [~r/myhtml_.*$/] # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. diff --git a/config/description.exs b/config/description.exs index 45e4b43f1..1089fd86c 100644 --- a/config/description.exs +++ b/config/description.exs @@ -2557,23 +2557,6 @@ } ] }, - %{ - group: :pleroma, - key: Pleroma.Uploaders.MDII, - type: :group, - children: [ - %{ - key: :cgi, - type: :string, - suggestions: ["https://mdii.sakura.ne.jp/mdii-post.cgi"] - }, - %{ - key: :files, - type: :string, - suggestions: ["https://mdii.sakura.ne.jp"] - } - ] - }, %{ group: :pleroma, key: :http, diff --git a/config/prod.exs b/config/prod.exs index 25873f360..adbce5606 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -20,8 +20,8 @@ config :phoenix, serve_endpoints: true # Do not print debug messages in production -config :logger, :console, level: :warn -config :logger, :ex_syslogger, level: :warn +config :logger, :console, level: :info +config :logger, :ex_syslogger, level: :info # ## SSL Support # diff --git a/config/releases.exs b/config/releases.exs index 98c5ceccd..b224960db 100644 --- a/config/releases.exs +++ b/config/releases.exs @@ -2,6 +2,7 @@ config :pleroma, :instance, static_dir: "/var/lib/pleroma/static" config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads" +config :pleroma, :modules, runtime_dir: "/var/lib/pleroma/modules" config_path = System.get_env("PLEROMA_CONFIG_PATH") || "/etc/pleroma/config.exs" diff --git a/config/test.exs b/config/test.exs index d52ced612..5c66a36f1 100644 --- a/config/test.exs +++ b/config/test.exs @@ -95,6 +95,8 @@ config :pleroma, Pleroma.ReverseProxy.Client, Pleroma.ReverseProxy.ClientMock +config :pleroma, :modules, runtime_dir: "test/fixtures/modules" + if File.exists?("./config/test.secret.exs") do import_config "test.secret.exs" else diff --git a/docs/API/differences_in_mastoapi_responses.md b/docs/API/differences_in_mastoapi_responses.md index 7f5d7681d..bb62ed5f2 100644 --- a/docs/API/differences_in_mastoapi_responses.md +++ b/docs/API/differences_in_mastoapi_responses.md @@ -46,7 +46,7 @@ The `id` parameter can also be the `nickname` of the user. This only works in th Has these additional fields under the `pleroma` object: - `tags`: Lists an array of tags for the user -- `relationship{}`: Includes fields as documented for Mastodon API https://docs.joinmastodon.org/api/entities/#relationship +- `relationship{}`: Includes fields as documented for Mastodon API https://docs.joinmastodon.org/entities/relationship/ - `is_moderator`: boolean, nullable, true if user is a moderator - `is_admin`: boolean, nullable, true if user is an admin - `confirmation_pending`: boolean, true if a new user account is waiting on email confirmation to be activated diff --git a/docs/API/pleroma_api.md b/docs/API/pleroma_api.md index 7228d805b..689edbcc2 100644 --- a/docs/API/pleroma_api.md +++ b/docs/API/pleroma_api.md @@ -70,59 +70,6 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi * Response: JSON. Returns `{"status": "success"}` if the account was successfully disabled, `{"error": "[error message]"}` otherwise * Example response: `{"error": "Invalid password."}` -## `/api/account/register` -### Register a new user -* Method `POST` -* Authentication: not required -* Params: - * `nickname` - * `fullname` - * `bio` - * `email` - * `password` - * `confirm` - * `captcha_solution`: optional, contains provider-specific captcha solution, - * `captcha_token`: optional, contains provider-specific captcha token - * `token`: invite token required when the registrations aren't public. -* Response: JSON. Returns a user object on success, otherwise returns `{"error": "error_msg"}` -* Example response: -```json -{ - "background_image": null, - "cover_photo": "https://pleroma.soykaf.com/images/banner.png", - "created_at": "Tue Dec 18 16:55:56 +0000 2018", - "default_scope": "public", - "description": "blushy-crushy fediverse idol + pleroma dev\nlet's be friends \nぷれろまの生徒会長。謎の外人。日本語OK. \n公主病.", - "description_html": "blushy-crushy fediverse idol + pleroma dev.
let's be friends
ぷれろまの生徒会長。謎の外人。日本語OK.
公主病.", - "favourites_count": 0, - "fields": [], - "followers_count": 0, - "following": false, - "follows_you": false, - "friends_count": 0, - "id": 6, - "is_local": true, - "locked": false, - "name": "lain", - "name_html": "lain", - "no_rich_text": false, - "pleroma": { - "tags": [] - }, - "profile_image_url": "https://pleroma.soykaf.com/images/avi.png", - "profile_image_url_https": "https://pleroma.soykaf.com/images/avi.png", - "profile_image_url_original": "https://pleroma.soykaf.com/images/avi.png", - "profile_image_url_profile_size": "https://pleroma.soykaf.com/images/avi.png", - "rights": { - "delete_others_notice": false - }, - "screen_name": "lain", - "statuses_count": 0, - "statusnet_blocking": false, - "statusnet_profile_url": "https://pleroma.soykaf.com/users/lain" -} -``` - ## `/api/pleroma/admin/`… See [Admin-API](admin_api.md) diff --git a/docs/administration/CLI_tasks/digest.md b/docs/administration/CLI_tasks/digest.md index a70f24c06..1badda8c3 100644 --- a/docs/administration/CLI_tasks/digest.md +++ b/docs/administration/CLI_tasks/digest.md @@ -14,8 +14,9 @@ mix pleroma.digest test [] Example: + ```sh tab="OTP" - ./bin/pleroma_ctl digest test donaldtheduck 2019-05-20 +./bin/pleroma_ctl digest test donaldtheduck 2019-05-20 ``` ```sh tab="From Source" diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index ce2a14210..cad3af68d 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -453,6 +453,7 @@ An example for Sendgrid adapter: ```elixir config :pleroma, Pleroma.Emails.Mailer, + enabled: true, adapter: Swoosh.Adapters.Sendgrid, api_key: "YOUR_API_KEY" ``` @@ -461,13 +462,13 @@ An example for SMTP adapter: ```elixir config :pleroma, Pleroma.Emails.Mailer, + enabled: true, adapter: Swoosh.Adapters.SMTP, relay: "smtp.gmail.com", username: "YOUR_USERNAME@gmail.com", password: "YOUR_SMTP_PASSWORD", port: 465, ssl: true, - tls: :always, auth: :always ``` @@ -836,3 +837,7 @@ config :auto_linker, rel: "ugc" ] ``` + +## Custom Runtime Modules (`:modules`) + +* `runtime_dir`: A path to custom Elixir modules (such as MRF policies). diff --git a/docs/installation/centos7_en.md b/docs/installation/centos7_en.md deleted file mode 100644 index ad4f58dc1..000000000 --- a/docs/installation/centos7_en.md +++ /dev/null @@ -1,274 +0,0 @@ -# Installing on CentOS 7 -## Installation - -This guide is a step-by-step installation guide for CentOS 7. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.digitalocean.com/community/tutorials/how-to-create-a-sudo-user-on-centos-quickstart). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su -s $SHELL -c 'command'` instead. - -### Required packages - -* `postgresql` (9,6+, CentOS 7 comes with 9.2, we will install version 11 in this guide) -* `elixir` (1.5+) -* `erlang` -* `erlang-parsetools` -* `erlang-xmerl` -* `git` -* Development Tools - -#### Optional packages used in this guide - -* `nginx` (preferred, example configs for other reverse proxies can be found in the repo) -* `certbot` (or any other ACME client for Let’s Encrypt certificates) - -### Prepare the system - -* First update the system, if not already done: - -```shell -sudo yum update -``` - -* Install some of the above mentioned programs: - -```shell -sudo yum install wget git unzip -``` - -* Install development tools: - -```shell -sudo yum group install "Development Tools" -``` - -### Install Elixir and Erlang - -* Add the EPEL repo: - -```shell -sudo yum install epel-release -sudo yum -y update -``` - -* Install Erlang repository: - -```shell -wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm -sudo rpm -Uvh erlang-solutions-1.0-1.noarch.rpm -``` - -* Install Erlang: - -```shell -sudo yum install erlang erlang-parsetools erlang-xmerl -``` - -* Download [latest Elixir release from Github](https://github.com/elixir-lang/elixir/releases/tag/v1.8.1) (Example for the newest version at the time when this manual was written) - -```shell -wget -P /tmp/ https://github.com/elixir-lang/elixir/releases/download/v1.8.1/Precompiled.zip -``` - -* Create folder where you want to install Elixir, we’ll use: - -```shell -sudo mkdir -p /opt/elixir -``` - -* Unzip downloaded file there: - -```shell -sudo unzip /tmp/Precompiled.zip -d /opt/elixir -``` - -* Create symlinks for the pre-compiled binaries: - -```shell -for e in elixir elixirc iex mix; do sudo ln -s /opt/elixir/bin/${e} /usr/local/bin/${e}; done -``` - -### Install PostgreSQL - -* Add the Postgresql repository: - -```shell -sudo yum install https://download.postgresql.org/pub/repos/yum/11/redhat/rhel-7-x86_64/pgdg-centos11-11-2.noarch.rpm -``` - -* Install the Postgresql server: - -```shell -sudo yum install postgresql11-server postgresql11-contrib -``` - -* Initialize database: - -```shell -sudo /usr/pgsql-11/bin/postgresql-11-setup initdb -``` - -* Open configuration file `/var/lib/pgsql/11/data/pg_hba.conf` and change the following lines from: - -```plain -# IPv4 local connections: -host all all 127.0.0.1/32 ident -# IPv6 local connections: -host all all ::1/128 ident -``` - -to - -```plain -# IPv4 local connections: -host all all 127.0.0.1/32 md5 -# IPv6 local connections: -host all all ::1/128 md5 -``` - -* Enable and start postgresql server: - -```shell -sudo systemctl enable --now postgresql-11.service -``` - -### Install PleromaBE - -* Add a new system user for the Pleroma service: - -```shell -sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma -``` - -**Note**: To execute a single command as the Pleroma system user, use `sudo -Hu pleroma command`. You can also switch to a shell by using `sudo -Hu pleroma $SHELL`. If you don’t have and want `sudo` on your system, you can use `su` as root user (UID 0) for a single command by using `su -l pleroma -s $SHELL -c 'command'` and `su -l pleroma -s $SHELL` for starting a shell. - -* Git clone the PleromaBE repository and make the Pleroma user the owner of the directory: - -```shell -sudo mkdir -p /opt/pleroma -sudo chown -R pleroma:pleroma /opt/pleroma -sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma -``` - -* Change to the new directory: - -```shell -cd /opt/pleroma -``` - -* Install the dependencies for Pleroma and answer with `yes` if it asks you to install `Hex`: - -```shell -sudo -Hu pleroma mix deps.get -``` - -* Generate the configuration: `sudo -Hu pleroma mix pleroma.instance gen` - * Answer with `yes` if it asks you to install `rebar3`. - * This may take some time, because parts of pleroma get compiled first. - * After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`. - -* Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances): - -```shell -mv config/{generated_config.exs,prod.secret.exs} -``` - -* The previous command creates also the file `config/setup_db.psql`, with which you can create the database: - -```shell -sudo -Hu postgres psql -f config/setup_db.psql -``` - -* Now run the database migration: - -```shell -sudo -Hu pleroma MIX_ENV=prod mix ecto.migrate -``` - -* Now you can start Pleroma already - -```shell -sudo -Hu pleroma MIX_ENV=prod mix phx.server -``` - -### Finalize installation - -If you want to open your newly installed instance to the world, you should run nginx or some other webserver/proxy in front of Pleroma and you should consider to create a systemd service file for Pleroma. - -#### Nginx - -* Install nginx, if not already done: - -```shell -sudo yum install nginx -``` - -* Setup your SSL cert, using your method of choice or certbot. If using certbot, first install it: - -```shell -sudo yum install certbot-nginx -``` - -and then set it up: - -```shell -sudo mkdir -p /var/lib/letsencrypt/ -sudo certbot certonly --email -d --standalone -``` - -If that doesn’t work, make sure, that nginx is not already running. If it still doesn’t work, try setting up nginx first (change ssl “on” to “off” and try again). - ---- - -* Copy the example nginx configuration to the nginx folder - -```shell -sudo cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/conf.d/pleroma.conf -``` - -* Before starting nginx edit the configuration and change it to your needs (e.g. change servername, change cert paths) -* Enable and start nginx: - -```shell -sudo systemctl enable --now nginx -``` - -If you need to renew the certificate in the future, uncomment the relevant location block in the nginx config and run: - -```shell -sudo certbot certonly --email -d --webroot -w /var/lib/letsencrypt/ -``` - -#### Other webserver/proxies - -You can find example configurations for them in `/opt/pleroma/installation/`. - -#### Systemd service - -* Copy example service file - -```shell -sudo cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service -``` - -* Edit the service file and make sure that all paths fit your installation -* Enable and start `pleroma.service`: - -```shell -sudo systemctl enable --now pleroma.service -``` - -#### Create your first user - -If your instance is up and running, you can create your first user with administrative rights with the following task: - -```shell -sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new --admin -``` - -#### Further reading - -* [Backup your instance](../administration/backup.md) -* [Hardening your instance](../configuration/hardening.md) -* [How to activate mediaproxy](../configuration/howto_mediaproxy.md) -* [Updating your instance](../administration/updating.md) - -## Questions - -Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**. diff --git a/docs/installation/migrating_from_source_otp_en.md b/docs/installation/migrating_from_source_otp_en.md index 87568faad..31c2f1294 100644 --- a/docs/installation/migrating_from_source_otp_en.md +++ b/docs/installation/migrating_from_source_otp_en.md @@ -1,42 +1,28 @@ # Switching a from-source install to OTP releases + ## What are OTP releases? OTP releases are as close as you can get to binary releases with Erlang/Elixir. The release is self-contained, and provides everything needed to boot it, it is easily administered via the provided shell script to open up a remote console, start/stop/restart the release, start in the background, send remote commands, and more. -### Can I still run the develop branch if I decide to use them? -Yes, we produce builds for every commit in `develop`. However `develop` is considered unstable, please don't use it in production because of faster access to new features, unless you need them as an app developer. -## Why would one want to switch? -Benefits of OTP releases over from-source installs include: -* **Less space used.** OTP releases come without source code, build tools, have docs and debug symbols stripped from the compiled bytecode and do not cointain tests, docs, revision history. -* **Minimal system dependencies.** Excluding the database and reverse proxy, only `curl`, `unzip` and `ncurses` are needed to download and run the release. Because Erlang runtime and Elixir are shipped with Pleroma, one can use the latest BEAM optimizations and Pleroma features, without having to worry about outdated system repos or a missing `erlang-*` package. -* **Potentially less bugs and better performance.** This extends on the previous point, because we have control over exactly what gets shipped, we can tweak the VM arguments and forget about weird bugs due to Erlang/Elixir version mismatches. -* **Faster and less bug-prone mix tasks.** On a from-source install one has to wait untill a new Pleroma node is started for each mix task and they execute outside of the instance context (for example if a user was deleted via a mix task, the instance will have no knowledge of that and continue to display status count and follows before the cache expires). Mix tasks in OTP releases are executed by calling into a running instance via RPC, which solves both of these problems. -### Sounds great, how do I switch? -Currently we support Linux machines with GNU (e.g. Debian, Ubuntu) or musl (e.g. Alpine) libc and `x86_64`, `aarch64` or `armv7l` CPUs. If you are unsure, check the [Detecting flavour](otp_en.md#detecting-flavour) section in OTP install guide. If your platform is supported, proceed with the guide, if not check the [My platform is not supported](#my-platform-is-not-supported) section. -### I don't think it is worth the effort, can I stay on a from-source install? -Yes, currently there are no plans to deprecate them. - -### My platform is not supported -If you think your platform is a popular choice for running Pleroma instances, or has the potential to become one, you can [file an issue on our Gitlab](https://git.pleroma.social/pleroma/pleroma/issues/new). If not, guides on how to build and update releases by yourself will be available soon. ## Pre-requisites You will be running commands as root. If you aren't root already, please elevate your priviledges by executing `sudo su`/`su`. The system needs to have `curl` and `unzip` installed for downloading and unpacking release builds. -Debian/Ubuntu: -```sh +```sh tab="Alpine" +apk add curl unzip +``` + +```sh tab="Debian/Ubuntu" apt install curl unzip ``` -Alpine: -``` -apk add curl unzip -``` ## Moving content out of the application directory When using OTP releases the application directory changes with every version so it would be a bother to keep content there (and also dangerous unless `--no-rm` option is used when updating). Fortunately almost all paths in Pleroma are configurable, so it is possible to move them out of there. Pleroma should be stopped before proceeding. ### Moving uploads/custom public files directory + ```sh # Create uploads directory and set proper permissions (skip if using a remote uploader) # Note: It does not have to be `/var/lib/pleroma/uploads`, you can configure it to be something else later @@ -92,8 +78,8 @@ Before proceeding, get the flavour from [Detecting flavour](otp_en.md#detecting- rm -r ~pleroma/* # Set the flavour environment variable to the string you got in Detecting flavour section. -# For example if the flavour is `arm64-musl` the command will be -export FLAVOUR="arm64-musl" +# For example if the flavour is `amd64-musl` the command will be +export FLAVOUR="amd64-musl" # Clone the release build into a temporary directory and unpack it # Replace `stable` with `unstable` if you want to run the unstable branch @@ -124,8 +110,15 @@ OTP releases have different service files than from-source installs so they need **Warning:** The service files assume pleroma user's home directory is `/opt/pleroma`, please make sure all paths fit your installation. -Debian/Ubuntu: -```sh +```sh tab="Alpine" +# Copy the service into a proper directory +cp -f ~pleroma/installation/init.d/pleroma /etc/init.d/pleroma + +# Start pleroma +rc-service pleroma start +``` + +```sh tab="Debian/Ubuntu" # Copy the service into a proper directory cp ~pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service @@ -139,14 +132,6 @@ systemctl reenable pleroma systemctl start pleroma ``` -Alpine: -```sh -# Copy the service into a proper directory -cp -f ~pleroma/installation/init.d/pleroma /etc/init.d/pleroma - -# Start pleroma -rc-service pleroma start -``` ## Running mix tasks Refer to [Running mix tasks](otp_en.md#running-mix-tasks) section from OTP release installation guide. ## Updating diff --git a/docs/installation/otp_en.md b/docs/installation/otp_en.md index 965e30e2a..93230806c 100644 --- a/docs/installation/otp_en.md +++ b/docs/installation/otp_en.md @@ -6,7 +6,7 @@ You will be running commands as root. If you aren't root already, please elevate your priviledges by executing `sudo su`/`su`. -While in theory OTP releases are possbile to install on any compatible machine, for the sake of simplicity this guide focuses only on Debian/Ubuntu/Alpine. +While in theory OTP releases are possbile to install on any compatible machine, for the sake of simplicity this guide focuses only on Debian/Ubuntu and Alpine. ### Detecting flavour @@ -20,6 +20,7 @@ If your platform is supported the output will contain the flavour string, you wi ### Installing the required packages Other than things bundled in the OTP release Pleroma depends on: + * curl (to download the release build) * unzip (needed to unpack release builds) * ncurses (ERTS won't run without it) @@ -27,18 +28,16 @@ Other than things bundled in the OTP release Pleroma depends on: * nginx (could be swapped with another reverse proxy but this guide covers only it) * certbot (for Let's Encrypt certificates, could be swapped with another ACME client, but this guide covers only it) -Debian/Ubuntu: -```sh -apt install curl unzip libncurses5 postgresql postgresql-contrib nginx certbot -``` -Alpine: - -```sh +```sh tab="Alpine" echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories apk update apk add curl unzip ncurses postgresql postgresql-contrib nginx certbot ``` +```sh tab="Debian/Ubuntu" +apt install curl unzip libncurses5 postgresql postgresql-contrib nginx certbot +``` + ## Setup ### Configuring PostgreSQL #### (Optional) Installing RUM indexes @@ -48,12 +47,7 @@ apk add curl unzip ncurses postgresql postgresql-contrib nginx certbot RUM indexes are an alternative indexing scheme that is not included in PostgreSQL by default. You can read more about them on the [Configuration page](../configuration/cheatsheet.md#rum-indexing-for-full-text-search). They are completely optional and most of the time are not worth it, especially if you are running a single user instance (unless you absolutely need ordered search results). -Debian/Ubuntu (available only on Buster/19.04): -```sh -apt install postgresql-11-rum -``` -Alpine: -```sh +```sh tab="Alpine" apk add git build-base postgresql-dev git clone https://github.com/postgrespro/rum /tmp/rum cd /tmp/rum @@ -62,25 +56,31 @@ make USE_PGXS=1 install cd rm -r /tmp/rum ``` + +```sh tab="Debian/Ubuntu" +# Available only on Buster/19.04 +apt install postgresql-11-rum +``` + #### (Optional) Performance configuration For optimal performance, you may use [PGTune](https://pgtune.leopard.in.ua), don't forget to restart postgresql after editing the configuration -Debian/Ubuntu: -```sh -systemctl restart postgresql -``` -Alpine: -```sh +```sh tab="Alpine" rc-service postgresql restart ``` + +```sh tab="Debian/Ubuntu" +systemctl restart postgresql +``` + ### Installing Pleroma ```sh -# Create the Pleroma user +# Create a Pleroma user adduser --system --shell /bin/false --home /opt/pleroma pleroma # Set the flavour environment variable to the string you got in Detecting flavour section. -# For example if the flavour is `arm64-musl` the command will be -export FLAVOUR="arm64-musl" +# For example if the flavour is `amd64-musl` the command will be +export FLAVOUR="amd64-musl" # Clone the release build into a temporary directory and unpack it su pleroma -s $SHELL -lc " @@ -133,49 +133,52 @@ su pleroma -s $SHELL -lc "./bin/pleroma stop" ### Setting up nginx and getting Let's Encrypt SSL certificaties +#### Get a Let's Encrypt certificate ```sh -# Get a Let's Encrypt certificate certbot certonly --standalone --preferred-challenges http -d yourinstance.tld +``` -# Copy the Pleroma nginx configuration to the nginx folder -# The location of nginx configs is dependent on the distro +#### Copy Pleroma nginx configuration to the nginx folder -# For Debian/Ubuntu: +The location of nginx configs is dependent on the distro + +```sh tab="Alpine" +cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/conf.d/pleroma.conf +``` + +```sh tab="Debian/Ubuntu" cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/sites-available/pleroma.nginx ln -s /etc/nginx/sites-available/pleroma.nginx /etc/nginx/sites-enabled/pleroma.nginx -# For Alpine: -cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/conf.d/pleroma.conf -# If your distro does not have either of those you can append -# `include /etc/nginx/pleroma.conf` to the end of the http section in /etc/nginx/nginx.conf and -cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/pleroma.conf +``` -# Edit the nginx config replacing example.tld with your (sub)domain +If your distro does not have either of those you can append `include /etc/nginx/pleroma.conf` to the end of the http section in /etc/nginx/nginx.conf and +```sh +cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/pleroma.conf +``` + +#### Edit the nginx config +```sh +# Replace example.tld with your (sub)domain $EDITOR path-to-nginx-config # Verify that the config is valid nginx -t +``` +#### Start nginx -# Start nginx -# For Debian/Ubuntu: -systemctl start nginx -# For Alpine: +```sh tab="Alpine" rc-service nginx start ``` -At this point if you open your (sub)domain in a browser you should see a 502 error, that's because pleroma is not started yet. +```sh tab="Debian/Ubuntu" +systemctl start nginx +``` + +At this point if you open your (sub)domain in a browser you should see a 502 error, that's because Pleroma is not started yet. ### Setting up a system service -Debian/Ubuntu: -```sh -# Copy the service into a proper directory -cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service -# Start pleroma and enable it on boot -systemctl start pleroma -systemctl enable pleroma -``` -Alpine: -```sh +```sh tab="Alpine" # Copy the service into a proper directory cp /opt/pleroma/installation/init.d/pleroma /etc/init.d/pleroma @@ -184,13 +187,22 @@ rc-service pleroma start rc-update add pleroma ``` +```sh tab="Debian/Ubuntu" +# Copy the service into a proper directory +cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service + +# Start pleroma and enable it on boot +systemctl start pleroma +systemctl enable pleroma +``` + If everything worked, you should see Pleroma-FE when visiting your domain. If that didn't happen, try reviewing the installation steps, starting Pleroma in the foreground and seeing if there are any errrors. -Still doesn't work? Feel free to contact us on [#pleroma on freenode](https://webchat.freenode.net/?channels=%23pleroma) or via matrix at , you can also [file an issue on our Gitlab](https://git.pleroma.social/pleroma/pleroma/issues/new) +Still doesn't work? Feel free to contact us on [#pleroma on freenode](https://irc.pleroma.social) or via matrix at , you can also [file an issue on our Gitlab](https://git.pleroma.social/pleroma/pleroma-support/issues/new) ## Post installation -### Setting up auto-renew Let's Encrypt certificate +### Setting up auto-renew of the Let's Encrypt certificate ```sh # Create the directory for webroot challenges mkdir -p /var/lib/letsencrypt @@ -201,25 +213,8 @@ $EDITOR path-to-nginx-config # Verify that the config is valid nginx -t ``` -Debian/Ubuntu: -```sh -# Restart nginx -systemctl restart nginx -# Ensure the webroot menthod and post hook is working -certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --dry-run --post-hook 'systemctl nginx reload' - -# Add it to the daily cron -echo '#!/bin/sh -certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --post-hook "systemctl reload nginx" -' > /etc/cron.daily/renew-pleroma-cert -chmod +x /etc/cron.daily/renew-pleroma-cert - -# If everything worked the output should contain /etc/cron.daily/renew-pleroma-cert -run-parts --test /etc/cron.daily -``` -Alpine: -```sh +```sh tab="Alpine" # Restart nginx rc-service nginx restart @@ -236,15 +231,25 @@ certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ -- ' > /etc/periodic/daily/renew-pleroma-cert chmod +x /etc/periodic/daily/renew-pleroma-cert -# If everything worked this should output /etc/periodic/daily/renew-pleroma-cert +# If everything worked the output should contain /etc/cron.daily/renew-pleroma-cert run-parts --test /etc/periodic/daily ``` -### Running mix tasks -Throughout the wiki and guides there is a lot of references to mix tasks. Since `mix` is a build tool, you can't just call `mix pleroma.task`, instead you should call `pleroma_ctl` stripping pleroma/ecto namespace. -So for example, if the task is `mix pleroma.user set admin --admin`, you should run it like this: -```sh -su pleroma -s $SHELL -lc "./bin/pleroma_ctl user set admin --admin" +```sh tab="Debian/Ubuntu" +# Restart nginx +systemctl restart nginx + +# Ensure the webroot menthod and post hook is working +certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --dry-run --post-hook 'systemctl reload nginx' + +# Add it to the daily cron +echo '#!/bin/sh +certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --post-hook "systemctl reload nginx" +' > /etc/cron.daily/renew-pleroma-cert +chmod +x /etc/cron.daily/renew-pleroma-cert + +# If everything worked the output should contain /etc/cron.daily/renew-pleroma-cert +run-parts --test /etc/cron.daily ``` ## Create your first user and set as admin @@ -270,4 +275,3 @@ But you should **always check the release notes/changelog** in case there are co * [Backup your instance](../administration/backup.md) * [Hardening your instance](../configuration/hardening.md) * [How to activate mediaproxy](../configuration/howto_mediaproxy.md) -* [Updating your instance](../administration/updating.md) diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 5b844aa41..2ae052069 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Application do import Cachex.Spec use Application + require Logger @name Mix.Project.config()[:name] @version Mix.Project.config()[:version] @@ -33,6 +34,7 @@ def start(_type, _args) do Pleroma.HTML.compile_scrubbers() Pleroma.Config.DeprecationWarnings.warn() setup_instrumenters() + load_custom_modules() # Define workers and child supervisors to be supervised children = @@ -68,6 +70,28 @@ def start(_type, _args) do Supervisor.start_link(children, opts) end + def load_custom_modules do + dir = Pleroma.Config.get([:modules, :runtime_dir]) + + if dir && File.exists?(dir) do + dir + |> Pleroma.Utils.compile_dir() + |> case do + {:error, _errors, _warnings} -> + raise "Invalid custom modules" + + {:ok, modules, _warnings} -> + if @env != :test do + Enum.each(modules, fn mod -> + Logger.info("Custom module loaded: #{inspect(mod)}") + end) + end + + :ok + end + end + end + defp setup_instrumenters do require Prometheus.Registry diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index aafe57280..e5d28ebff 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -64,11 +64,13 @@ def mark_as_read(%User{} = user, %Conversation{} = conversation) do end def mark_as_read(participation) do - participation - |> read_cng(%{read: true}) - |> Repo.update() + __MODULE__ + |> where(id: ^participation.id) + |> update(set: [read: true]) + |> select([p], p) + |> Repo.update_all([]) |> case do - {:ok, participation} -> + {1, [participation]} -> participation = Repo.preload(participation, :user) User.set_unread_conversation_count(participation.user) {:ok, participation} diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index 2cae29f35..11513106e 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -10,9 +10,7 @@ def compile_scrubbers do dir = Path.join(:code.priv_dir(:pleroma), "scrubbers") dir - |> File.ls!() - |> Enum.map(&Path.join(dir, &1)) - |> Kernel.ParallelCompiler.compile() + |> Pleroma.Utils.compile_dir() |> case do {:error, _errors, _warnings} -> raise "Compiling scrubbers failed" diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index eb37b95a6..2452a7389 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -17,6 +17,8 @@ defmodule Pleroma.Object do require Logger + @type t() :: %__MODULE__{} + schema "objects" do field(:data, :map) @@ -79,6 +81,20 @@ def get_by_ap_id(ap_id) do Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id))) end + @doc """ + Get a single attachment by it's name and href + """ + @spec get_attachment_by_name_and_href(String.t(), String.t()) :: Object.t() | nil + def get_attachment_by_name_and_href(name, href) do + query = + from(o in Object, + where: fragment("(?)->>'name' = ?", o.data, ^name), + where: fragment("(?)->>'href' = ?", o.data, ^href) + ) + + Repo.one(query) + end + defp warn_on_no_object_preloaded(ap_id) do "Object.normalize() called without preloaded object (#{inspect(ap_id)}). Consider preloading the object" |> Logger.debug() @@ -164,6 +180,7 @@ def swap_object_with_tombstone(object) do def delete(%Object{data: %{"id" => id}} = object) do with {:ok, _obj} = swap_object_with_tombstone(object), + :ok <- delete_attachments(object), deleted_activity = Activity.delete_all_by_object_ap_id(id), {:ok, true} <- Cachex.del(:object_cache, "object:#{id}"), {:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path) do @@ -171,6 +188,77 @@ def delete(%Object{data: %{"id" => id}} = object) do end end + defp delete_attachments(%{data: %{"attachment" => [_ | _] = attachments, "actor" => actor}}) do + hrefs = + Enum.flat_map(attachments, fn attachment -> + Enum.map(attachment["url"], & &1["href"]) + end) + + names = Enum.map(attachments, & &1["name"]) + + uploader = Pleroma.Config.get([Pleroma.Upload, :uploader]) + + # find all objects for copies of the attachments, name and actor doesn't matter here + delete_ids = + from(o in Object, + where: + fragment( + "to_jsonb(array(select jsonb_array_elements((?)#>'{url}') ->> 'href'))::jsonb \\?| (?)", + o.data, + ^hrefs + ) + ) + |> Repo.all() + # we should delete 1 object for any given attachment, but don't delete files if + # there are more than 1 object for it + |> Enum.reduce(%{}, fn %{ + id: id, + data: %{ + "url" => [%{"href" => href}], + "actor" => obj_actor, + "name" => name + } + }, + acc -> + Map.update(acc, href, %{id: id, count: 1}, fn val -> + case obj_actor == actor and name in names do + true -> + # set id of the actor's object that will be deleted + %{val | id: id, count: val.count + 1} + + false -> + # another actor's object, just increase count to not delete file + %{val | count: val.count + 1} + end + end) + end) + |> Enum.map(fn {href, %{id: id, count: count}} -> + # only delete files that have single instance + with 1 <- count do + prefix = + case Pleroma.Config.get([Pleroma.Upload, :base_url]) do + nil -> "media" + _ -> "" + end + + base_url = Pleroma.Config.get([__MODULE__, :base_url], Pleroma.Web.base_url()) + + file_path = String.trim_leading(href, "#{base_url}/#{prefix}") + + uploader.delete_file(file_path) + end + + id + end) + + from(o in Object, where: o.id in ^delete_ids) + |> Repo.delete_all() + + :ok + end + + defp delete_attachments(%{data: _data}), do: :ok + def prune(%Object{data: %{"id" => id}} = object) do with {:ok, object} <- Repo.delete(object), {:ok, true} <- Cachex.del(:object_cache, "object:#{id}"), diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 4d71c91a8..a1bde90f1 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -154,7 +154,7 @@ defp maybe_date_fetch(headers, date) do end def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do - Logger.info("Fetching object #{id} via AP") + Logger.debug("Fetching object #{id} via AP") date = Pleroma.Signature.signed_date() diff --git a/lib/pleroma/plugs/oauth_scopes_plug.ex b/lib/pleroma/plugs/oauth_scopes_plug.ex index 174a8389c..07c0f7fdb 100644 --- a/lib/pleroma/plugs/oauth_scopes_plug.ex +++ b/lib/pleroma/plugs/oauth_scopes_plug.ex @@ -18,16 +18,13 @@ def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do token = assigns[:token] scopes = transform_scopes(scopes, options) - matched_scopes = token && filter_descendants(scopes, token.scopes) + matched_scopes = (token && filter_descendants(scopes, token.scopes)) || [] cond do - is_nil(token) -> - maybe_perform_instance_privacy_check(conn, options) - - op == :| && Enum.any?(matched_scopes) -> + token && op == :| && Enum.any?(matched_scopes) -> conn - op == :& && matched_scopes == scopes -> + token && op == :& && matched_scopes == scopes -> conn options[:fallback] == :proceed_unauthenticated -> diff --git a/lib/pleroma/uploaders/local.ex b/lib/pleroma/uploaders/local.ex index 36b3c35ec..2e6fe3292 100644 --- a/lib/pleroma/uploaders/local.ex +++ b/lib/pleroma/uploaders/local.ex @@ -5,10 +5,12 @@ defmodule Pleroma.Uploaders.Local do @behaviour Pleroma.Uploaders.Uploader + @impl true def get_file(_) do {:ok, {:static_dir, upload_path()}} end + @impl true def put_file(upload) do {local_path, file} = case Enum.reverse(Path.split(upload.path)) do @@ -33,4 +35,15 @@ def put_file(upload) do def upload_path do Pleroma.Config.get!([__MODULE__, :uploads]) end + + @impl true + def delete_file(path) do + upload_path() + |> Path.join(path) + |> File.rm() + |> case do + :ok -> :ok + {:error, posix_error} -> {:error, to_string(posix_error)} + end + end end diff --git a/lib/pleroma/uploaders/mdii.ex b/lib/pleroma/uploaders/mdii.ex deleted file mode 100644 index c36f3d61d..000000000 --- a/lib/pleroma/uploaders/mdii.ex +++ /dev/null @@ -1,37 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Uploaders.MDII do - @moduledoc "Represents uploader for https://github.com/hakaba-hitoyo/minimal-digital-image-infrastructure" - - alias Pleroma.Config - alias Pleroma.HTTP - - @behaviour Pleroma.Uploaders.Uploader - - # MDII-hosted images are never passed through the MediaPlug; only local media. - # Delegate to Pleroma.Uploaders.Local - def get_file(file) do - Pleroma.Uploaders.Local.get_file(file) - end - - def put_file(upload) do - cgi = Config.get([Pleroma.Uploaders.MDII, :cgi]) - files = Config.get([Pleroma.Uploaders.MDII, :files]) - - {:ok, file_data} = File.read(upload.tempfile) - - extension = String.split(upload.name, ".") |> List.last() - query = "#{cgi}?#{extension}" - - with {:ok, %{status: 200, body: body}} <- - HTTP.post(query, file_data, [], adapter: [pool: :default]) do - remote_file_name = String.split(body) |> List.first() - public_url = "#{files}/#{remote_file_name}.#{extension}" - {:ok, {:url, public_url}} - else - _ -> Pleroma.Uploaders.Local.put_file(upload) - end - end -end diff --git a/lib/pleroma/uploaders/s3.ex b/lib/pleroma/uploaders/s3.ex index 9876b6398..feb89cea6 100644 --- a/lib/pleroma/uploaders/s3.ex +++ b/lib/pleroma/uploaders/s3.ex @@ -10,6 +10,7 @@ defmodule Pleroma.Uploaders.S3 do # The file name is re-encoded with S3's constraints here to comply with previous # links with less strict filenames + @impl true def get_file(file) do config = Config.get([__MODULE__]) bucket = Keyword.fetch!(config, :bucket) @@ -35,6 +36,7 @@ def get_file(file) do ])}} end + @impl true def put_file(%Pleroma.Upload{} = upload) do config = Config.get([__MODULE__]) bucket = Keyword.get(config, :bucket) @@ -69,6 +71,18 @@ def put_file(%Pleroma.Upload{} = upload) do end end + @impl true + def delete_file(file) do + [__MODULE__, :bucket] + |> Config.get() + |> ExAws.S3.delete_object(file) + |> ExAws.request() + |> case do + {:ok, %{status_code: 204}} -> :ok + error -> {:error, inspect(error)} + end + end + @regex Regex.compile!("[^0-9a-zA-Z!.*/'()_-]") def strict_encode(name) do String.replace(name, @regex, "-") diff --git a/lib/pleroma/uploaders/uploader.ex b/lib/pleroma/uploaders/uploader.ex index c0b22c28a..d71e213d2 100644 --- a/lib/pleroma/uploaders/uploader.ex +++ b/lib/pleroma/uploaders/uploader.ex @@ -36,6 +36,8 @@ defmodule Pleroma.Uploaders.Uploader do @callback put_file(Pleroma.Upload.t()) :: :ok | {:ok, file_spec()} | {:error, String.t()} | :wait_callback + @callback delete_file(file :: String.t()) :: :ok | {:error, String.t()} + @callback http_callback(Plug.Conn.t(), Map.t()) :: {:ok, Plug.Conn.t()} | {:ok, Plug.Conn.t(), file_spec()} @@ -43,7 +45,6 @@ defmodule Pleroma.Uploaders.Uploader do @optional_callbacks http_callback: 2 @spec put_file(module(), Pleroma.Upload.t()) :: {:ok, file_spec()} | {:error, String.t()} - def put_file(uploader, upload) do case uploader.put_file(upload) do :ok -> {:ok, {:file, upload.path}} diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 706aee2ff..2e225415c 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1430,20 +1430,47 @@ def get_or_fetch_by_ap_id(ap_id) do Creates an internal service actor by URI if missing. Optionally takes nickname for addressing. """ - def get_or_create_service_actor_by_ap_id(uri, nickname \\ nil) do - with user when is_nil(user) <- get_cached_by_ap_id(uri) do - {:ok, user} = - %User{ - invisible: true, - local: true, - ap_id: uri, - nickname: nickname, - follower_address: uri <> "/followers" - } - |> Repo.insert() + @spec get_or_create_service_actor_by_ap_id(String.t(), String.t()) :: User.t() | nil + def get_or_create_service_actor_by_ap_id(uri, nickname) do + {_, user} = + case get_cached_by_ap_id(uri) do + nil -> + with {:error, %{errors: errors}} <- create_service_actor(uri, nickname) do + Logger.error("Cannot create service actor: #{uri}/.\n#{inspect(errors)}") + {:error, nil} + end - user - end + %User{invisible: false} = user -> + set_invisible(user) + + user -> + {:ok, user} + end + + user + end + + @spec set_invisible(User.t()) :: {:ok, User.t()} + defp set_invisible(user) do + user + |> change(%{invisible: true}) + |> update_and_set_cache() + end + + @spec create_service_actor(String.t(), String.t()) :: + {:ok, User.t()} | {:error, Ecto.Changeset.t()} + defp create_service_actor(uri, nickname) do + %User{ + invisible: true, + local: true, + ap_id: uri, + nickname: nickname, + follower_address: uri <> "/followers" + } + |> change + |> unique_constraint(:nickname) + |> Repo.insert() + |> set_cache() end # AP style @@ -1855,9 +1882,9 @@ def admin_api_update(user, params) do ]) with {:ok, updated_user} <- update_and_set_cache(changeset) do - if user.is_admin && !updated_user.is_admin do - # Tokens & authorizations containing any admin scopes must be revoked (revoking all). - # This is an extra safety measure (tokens' admin scopes won't be accepted for non-admins). + if user.is_admin != updated_user.is_admin do + # Admin status change results in change of accessible OAuth scopes, and instead of changing + # already issued tokens we revoke them, requiring user to sign in again global_sign_out(user) end diff --git a/lib/pleroma/utils.ex b/lib/pleroma/utils.ex new file mode 100644 index 000000000..8d36a0001 --- /dev/null +++ b/lib/pleroma/utils.ex @@ -0,0 +1,12 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Utils do + def compile_dir(dir) when is_binary(dir) do + dir + |> File.ls!() + |> Enum.map(&Path.join(dir, &1)) + |> Kernel.ParallelCompiler.compile() + end +end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 16e6b0057..60c9e7e64 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1298,28 +1298,26 @@ defp object_to_user_data(data) do def fetch_follow_information_for_user(user) do with {:ok, following_data} <- Fetcher.fetch_and_contain_remote_object_from_id(user.following_address), - following_count when is_integer(following_count) <- following_data["totalItems"], {:ok, hide_follows} <- collection_private(following_data), {:ok, followers_data} <- Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address), - followers_count when is_integer(followers_count) <- followers_data["totalItems"], {:ok, hide_followers} <- collection_private(followers_data) do {:ok, %{ hide_follows: hide_follows, - follower_count: followers_count, - following_count: following_count, + follower_count: normalize_counter(followers_data["totalItems"]), + following_count: normalize_counter(following_data["totalItems"]), hide_followers: hide_followers }} else - {:error, _} = e -> - e - - e -> - {:error, e} + {:error, _} = e -> e + e -> {:error, e} end end + defp normalize_counter(counter) when is_integer(counter), do: counter + defp normalize_counter(_), do: 0 + defp maybe_update_follow_information(data) do with {:enabled, true} <- {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])}, @@ -1339,24 +1337,18 @@ defp maybe_update_follow_information(data) do end end + defp collection_private(%{"first" => %{"type" => type}}) + when type in ["CollectionPage", "OrderedCollectionPage"], + do: {:ok, false} + defp collection_private(%{"first" => first}) do - if is_map(first) and - first["type"] in ["CollectionPage", "OrderedCollectionPage"] do + with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <- + Fetcher.fetch_and_contain_remote_object_from_id(first) do {:ok, false} else - with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <- - Fetcher.fetch_and_contain_remote_object_from_id(first) do - {:ok, false} - else - {:error, {:ok, %{status: code}}} when code in [401, 403] -> - {:ok, true} - - {:error, _} = e -> - e - - e -> - {:error, e} - end + {:error, {:ok, %{status: code}}} when code in [401, 403] -> {:ok, true} + {:error, _} = e -> e + e -> {:error, e} end end diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index dec5da0d3..5059e3984 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -257,7 +257,7 @@ def inbox(%{assigns: %{valid_signature: true}} = conn, params) do # only accept relayed Creates def inbox(conn, %{"type" => "Create"} = params) do - Logger.info( + Logger.debug( "Signature missing or not from author, relayed Create message, fetching object from source" ) @@ -270,11 +270,11 @@ def inbox(conn, params) do headers = Enum.into(conn.req_headers, %{}) if String.contains?(headers["signature"], params["actor"]) do - Logger.info( + Logger.debug( "Signature validation error for: #{params["actor"]}, make sure you are forwarding the HTTP Host header!" ) - Logger.info(inspect(conn.req_headers)) + Logger.debug(inspect(conn.req_headers)) end json(conn, dgettext("errors", "error")) diff --git a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex index f7831bc3e..4a5709974 100644 --- a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do @impl true def filter(object) do - Logger.info("REJECTING #{inspect(object)}") + Logger.debug("REJECTING #{inspect(object)}") {:reject, object} end diff --git a/lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex index 26b8539fe..df774b0f7 100644 --- a/lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex @@ -18,7 +18,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do ] def perform(:prefetch, url) do - Logger.info("Prefetching #{inspect(url)}") + Logger.debug("Prefetching #{inspect(url)}") url |> MediaProxy.url() diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 4073d3d63..e4e3ab44a 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -48,7 +48,7 @@ def is_representable?(%Activity{} = activity) do * `id`: the ActivityStreams URI of the message """ def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = params) do - Logger.info("Federating #{id} to #{inbox}") + Logger.debug("Federating #{id} to #{inbox}") %{host: host, path: path} = URI.parse(inbox) digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64()) @@ -228,7 +228,7 @@ def publish(%User{} = actor, %Activity{} = activity) do public = is_public?(activity) if public && Config.get([:instance, :allow_relay]) do - Logger.info(fn -> "Relaying #{activity.data["id"]} out" end) + Logger.debug(fn -> "Relaying #{activity.data["id"]} out" end) Relay.publish(activity) end @@ -264,6 +264,10 @@ def gather_webfinger_links(%User{} = user) do "rel" => "self", "type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "href" => user.ap_id + }, + %{ + "rel" => "http://ostatus.org/schema/1.0/subscribe", + "template" => "#{Pleroma.Web.base_url()}/ostatus_subscribe?acct={uri}" } ] end diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index 99a804568..48a1b71e0 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -9,10 +9,12 @@ defmodule Pleroma.Web.ActivityPub.Relay do alias Pleroma.Web.ActivityPub.ActivityPub require Logger + @relay_nickname "relay" + def get_actor do actor = relay_ap_id() - |> User.get_or_create_service_actor_by_ap_id() + |> User.get_or_create_service_actor_by_ap_id(@relay_nickname) actor end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index ecba27bef..3fa789d53 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -397,7 +397,7 @@ def handle_incoming( %{"type" => "Create", "object" => %{"type" => objtype} = object} = data, options ) - when objtype in ["Article", "Note", "Video", "Page", "Question", "Answer"] do + when objtype in ["Article", "Event", "Note", "Video", "Page", "Question", "Answer"] do actor = Containment.get_actor(data) data = diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index e87d09134..db7084246 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -22,7 +22,16 @@ defmodule Pleroma.Web.ActivityPub.Utils do require Logger require Pleroma.Constants - @supported_object_types ["Article", "Note", "Video", "Page", "Question", "Answer", "Audio"] + @supported_object_types [ + "Article", + "Note", + "Event", + "Video", + "Page", + "Question", + "Answer", + "Audio" + ] @strip_status_report_states ~w(closed resolved) @supported_report_states ~w(open closed resolved) @valid_visibilities ~w(public unlisted private direct) diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 9059aa634..350c4391d 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -201,7 +201,6 @@ def render("followers.json", %{user: user} = opts) do %{ "id" => "#{user.ap_id}/followers", "type" => "OrderedCollection", - "totalItems" => total, "first" => if showing_items do collection(followers, "#{user.ap_id}/followers", 1, showing_items, total) @@ -209,6 +208,7 @@ def render("followers.json", %{user: user} = opts) do "#{user.ap_id}/followers?page=1" end } + |> maybe_put_total_items(showing_count, total) |> Map.merge(Utils.make_json_ld_header()) end @@ -251,6 +251,12 @@ def render("activity_collection_page.json", %{activities: activities, iri: iri}) |> Map.merge(Utils.make_json_ld_header()) end + defp maybe_put_total_items(map, false, _total), do: map + + defp maybe_put_total_items(map, true, total) do + Map.put(map, "totalItems", total) + end + def collection(collection, iri, page, show_items \\ true, total \\ nil) do offset = (page - 1) * 10 items = Enum.slice(collection, offset, 10) diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index bbea31682..d32c38a05 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -59,7 +59,7 @@ defmodule Pleroma.Web.Endpoint do plug(Pleroma.Plugs.TrailingFormatPlug) plug(Plug.RequestId) - plug(Plug.Logger) + plug(Plug.Logger, log: :debug) plug(Pleroma.Plugs.Parsers) diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index e8a56ebd7..f506a7d24 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -58,7 +58,7 @@ def perform(:publish, activity) do end def perform(:incoming_ap_doc, params) do - Logger.info("Handling incoming AP activity") + Logger.debug("Handling incoming AP activity") params = Utils.normalize_params(params) @@ -71,13 +71,13 @@ def perform(:incoming_ap_doc, params) do {:ok, activity} else %Activity{} -> - Logger.info("Already had #{params["id"]}") + Logger.debug("Already had #{params["id"]}") :error _e -> # Just drop those for now - Logger.info("Unhandled activity") - Logger.info(Jason.encode!(params, pretty: true)) + Logger.debug("Unhandled activity") + Logger.debug(Jason.encode!(params, pretty: true)) :error end end diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex index fb9b26649..1d045c644 100644 --- a/lib/pleroma/web/federator/publisher.ex +++ b/lib/pleroma/web/federator/publisher.ex @@ -47,7 +47,7 @@ def publish(%User{} = user, %Activity{} = activity) do Config.get([:instance, :federation_publisher_modules]) |> Enum.each(fn module -> if module.is_representable?(activity) do - Logger.info("Publishing #{activity.data["id"]} using #{inspect(module)}") + Logger.debug("Publishing #{activity.data["id"]} using #{inspect(module)}") module.publish(user, activity) end end) diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex index ca261ad6e..9f7e4943c 100644 --- a/lib/pleroma/web/masto_fe_controller.ex +++ b/lib/pleroma/web/masto_fe_controller.ex @@ -20,18 +20,21 @@ defmodule Pleroma.Web.MastoFEController do plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :index) @doc "GET /web/*path" - def index(%{assigns: %{user: user}} = conn, _params) do - token = get_session(conn, :oauth_token) + def index(%{assigns: %{user: user, token: token}} = conn, _params) + when not is_nil(user) and not is_nil(token) do + conn + |> put_layout(false) + |> render("index.html", + token: token.token, + user: user, + custom_emojis: Pleroma.Emoji.get_all() + ) + end - if user && token do - conn - |> put_layout(false) - |> render("index.html", token: token, user: user, custom_emojis: Pleroma.Emoji.get_all()) - else - conn - |> put_session(:return_to, conn.request_path) - |> redirect(to: "/web/login") - end + def index(conn, _params) do + conn + |> put_session(:return_to, conn.request_path) + |> redirect(to: "/web/login") end @doc "GET /web/manifest.json" diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index a0257dfa6..e9590224b 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -421,7 +421,8 @@ def get_reply_to(%{data: %{"object" => _object}} = activity, _) do end end - def render_content(%{data: %{"type" => "Video"}} = object) do + def render_content(%{data: %{"type" => object_type}} = object) + when object_type in ["Video", "Event"] do with name when not is_nil(name) and name != "" <- object.data["name"] do "

#{name}

#{object.data["content"]}" else diff --git a/lib/pleroma/web/metadata/twitter_card.ex b/lib/pleroma/web/metadata/twitter_card.ex index d6a6049b3..67419a666 100644 --- a/lib/pleroma/web/metadata/twitter_card.ex +++ b/lib/pleroma/web/metadata/twitter_card.ex @@ -31,7 +31,7 @@ def build_tags(%{activity_id: id, object: object, user: user}) do if attachments == [] or Metadata.activity_nsfw?(object) do [ image_tag(user), - {:meta, [property: "twitter:card", content: "summary_large_image"], []} + {:meta, [property: "twitter:card", content: "summary"], []} ] else attachments diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex index c0dae1b7e..000bd9f66 100644 --- a/lib/pleroma/web/metadata/utils.ex +++ b/lib/pleroma/web/metadata/utils.ex @@ -15,6 +15,7 @@ def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do |> String.replace(~r//, " ") |> HTML.get_cached_stripped_html_for_activity(object, "metadata") |> Emoji.Formatter.demojify() + |> HtmlEntities.decode() |> Formatter.truncate() end @@ -22,6 +23,7 @@ def scrub_html_and_truncate(content, max_length \\ 200) when is_binary(content) content |> scrub_html |> Emoji.Formatter.demojify() + |> HtmlEntities.decode() |> Formatter.truncate(max_length) end diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex index 69dfa92e3..0bbf84fd3 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex @@ -52,7 +52,7 @@ def list_from(conn, %{"instance_address" => address}) do @doc """ Lists the packs available on the instance as JSON. - The information is public and does not require authentification. The format is + The information is public and does not require authentication. The format is a map of "pack directory name" to pack.json contents. """ def list_packs(conn, _params) do diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex index 8fed3f5bb..772c535a4 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -22,7 +22,14 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do plug( OAuthScopesPlug, - %{scopes: ["read:statuses"]} when action in [:conversation, :conversation_statuses] + %{scopes: ["read:statuses"]} + when action in [:conversation, :conversation_statuses, :emoji_reactions_by] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:statuses"]} + when action in [:react_with_emoji, :unreact_with_emoji] ) plug( diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index b32cd7c90..787d300fb 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -229,9 +229,9 @@ defmodule Pleroma.Web.Router do pipe_through(:pleroma_html) post("/main/ostatus", UtilController, :remote_subscribe) - get("/ostatus_subscribe", UtilController, :remote_follow) + get("/ostatus_subscribe", RemoteFollowController, :follow) - post("/ostatus_subscribe", UtilController, :do_remote_follow) + post("/ostatus_subscribe", RemoteFollowController, :do_follow) end scope "/api/pleroma", Pleroma.Web.TwitterAPI do diff --git a/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex new file mode 100644 index 000000000..5ba192cd7 --- /dev/null +++ b/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex @@ -0,0 +1,11 @@ +<%= if @error == :error do %> +

Error fetching user

+<% else %> +

Remote follow

+ +

<%= @followee.nickname %>

+ <%= form_for @conn, remote_follow_path(@conn, :do_follow), [as: "user"], fn f -> %> + <%= hidden_input f, :id, value: @followee.id %> + <%= submit "Authorize" %> + <% end %> +<% end %> diff --git a/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex new file mode 100644 index 000000000..df44988ee --- /dev/null +++ b/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex @@ -0,0 +1,14 @@ +<%= if @error do %> +

<%= @error %>

+<% end %> +

Log in to follow

+

<%= @followee.nickname %>

+ +<%= form_for @conn, remote_follow_path(@conn, :do_follow), [as: "authorization"], fn f -> %> +<%= text_input f, :name, placeholder: "Username", required: true %> +
+<%= password_input f, :password, placeholder: "Password", required: true %> +
+<%= hidden_input f, :id, value: @followee.id %> +<%= submit "Authorize" %> +<% end %> diff --git a/lib/pleroma/web/templates/twitter_api/util/followed.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex similarity index 100% rename from lib/pleroma/web/templates/twitter_api/util/followed.html.eex rename to lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex diff --git a/lib/pleroma/web/templates/twitter_api/util/follow.html.eex b/lib/pleroma/web/templates/twitter_api/util/follow.html.eex deleted file mode 100644 index 06359fa6c..000000000 --- a/lib/pleroma/web/templates/twitter_api/util/follow.html.eex +++ /dev/null @@ -1,11 +0,0 @@ -<%= if @error == :error do %> -

Error fetching user

-<% else %> -

Remote follow

- -

<%= @name %>

- <%= form_for @conn, util_path(@conn, :do_remote_follow), [as: "user"], fn f -> %> - <%= hidden_input f, :id, value: @id %> - <%= submit "Authorize" %> - <% end %> -<% end %> diff --git a/lib/pleroma/web/templates/twitter_api/util/follow_login.html.eex b/lib/pleroma/web/templates/twitter_api/util/follow_login.html.eex deleted file mode 100644 index 4e3a2be67..000000000 --- a/lib/pleroma/web/templates/twitter_api/util/follow_login.html.eex +++ /dev/null @@ -1,14 +0,0 @@ -<%= if @error do %> -

<%= @error %>

-<% end %> -

Log in to follow

-

<%= @name %>

- -<%= form_for @conn, util_path(@conn, :do_remote_follow), [as: "authorization"], fn f -> %> -<%= text_input f, :name, placeholder: "Username" %> -
-<%= password_input f, :password, placeholder: "Password" %> -
-<%= hidden_input f, :id, value: @id %> -<%= submit "Authorize" %> -<% end %> diff --git a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex new file mode 100644 index 000000000..e0d4d5632 --- /dev/null +++ b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex @@ -0,0 +1,112 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do + use Pleroma.Web, :controller + + require Logger + + alias Pleroma.Activity + alias Pleroma.Object.Fetcher + alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.User + alias Pleroma.Web.Auth.Authenticator + alias Pleroma.Web.CommonAPI + + @status_types ["Article", "Event", "Note", "Video", "Page", "Question"] + + # Note: follower can submit the form (with password auth) not being signed in (having no token) + plug( + OAuthScopesPlug, + %{fallback: :proceed_unauthenticated, scopes: ["follow", "write:follows"]} + when action in [:do_follow] + ) + + # GET /ostatus_subscribe + # + def follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do + case is_status?(acct) do + true -> follow_status(conn, user, acct) + _ -> follow_account(conn, user, acct) + end + end + + defp follow_status(conn, _user, acct) do + with {:ok, object} <- Fetcher.fetch_object_from_id(acct), + %Activity{id: activity_id} <- Activity.get_create_by_object_ap_id(object.data["id"]) do + redirect(conn, to: o_status_path(conn, :notice, activity_id)) + else + error -> + handle_follow_error(conn, error) + end + end + + defp follow_account(conn, user, acct) do + with {:ok, followee} <- User.get_or_fetch(acct) do + render(conn, follow_template(user), %{error: false, followee: followee, acct: acct}) + else + {:error, _reason} -> + render(conn, follow_template(user), %{error: :error}) + end + end + + defp follow_template(%User{} = _user), do: "follow.html" + defp follow_template(_), do: "follow_login.html" + + defp is_status?(acct) do + case Fetcher.fetch_and_contain_remote_object_from_id(acct) do + {:ok, %{"type" => type}} when type in @status_types -> + true + + _ -> + false + end + end + + # POST /ostatus_subscribe + # + def do_follow(%{assigns: %{user: %User{} = user}} = conn, %{"user" => %{"id" => id}}) do + with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)}, + {:ok, _, _, _} <- CommonAPI.follow(user, followee) do + render(conn, "followed.html", %{error: false}) + else + error -> + handle_follow_error(conn, error) + end + end + + def do_follow(conn, %{"authorization" => %{"name" => _, "password" => _, "id" => id}}) do + with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)}, + {_, {:ok, user}, _} <- {:auth, Authenticator.get_user(conn), followee}, + {:ok, _, _, _} <- CommonAPI.follow(user, followee) do + render(conn, "followed.html", %{error: false}) + else + error -> + handle_follow_error(conn, error) + end + end + + def do_follow(%{assigns: %{user: nil}} = conn, _) do + Logger.debug("Insufficient permissions: follow | write:follows.") + render(conn, "followed.html", %{error: "Insufficient permissions: follow | write:follows."}) + end + + defp handle_follow_error(conn, {:auth, _, followee} = _) do + render(conn, "follow_login.html", %{error: "Wrong username or password", followee: followee}) + end + + defp handle_follow_error(conn, {:fetch_user, error} = _) do + Logger.debug("Remote follow failed with error #{inspect(error)}") + render(conn, "followed.html", %{error: "Could not find user"}) + end + + defp handle_follow_error(conn, {:error, "Could not follow user:" <> _} = _) do + render(conn, "followed.html", %{error: "Error following account"}) + end + + defp handle_follow_error(conn, error) do + Logger.debug("Remote follow failed with error #{inspect(error)}") + render(conn, "followed.html", %{error: "Something went wrong."}) + end +end diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 2305bb413..f08b9d28c 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -7,12 +7,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do require Logger - alias Pleroma.Activity alias Pleroma.Config alias Pleroma.Emoji alias Pleroma.Healthcheck alias Pleroma.Notification - alias Pleroma.Plugs.AuthenticationPlug alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web @@ -22,7 +20,14 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do plug( OAuthScopesPlug, %{scopes: ["follow", "write:follows"]} - when action in [:do_remote_follow, :follow_import] + when action == :follow_import + ) + + # Note: follower can submit the form (with password auth) not being signed in (having no token) + plug( + OAuthScopesPlug, + %{fallback: :proceed_unauthenticated, scopes: ["follow", "write:follows"]} + when action == :do_remote_follow ) plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks_import) @@ -77,94 +82,6 @@ def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profil end end - def remote_follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do - if is_status?(acct) do - {:ok, object} = Pleroma.Object.Fetcher.fetch_object_from_id(acct) - %Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"]) - redirect(conn, to: "/notice/#{activity_id}") - else - with {:ok, followee} <- User.get_or_fetch(acct) do - conn - |> render(follow_template(user), %{ - error: false, - acct: acct, - avatar: User.avatar_url(followee), - name: followee.nickname, - id: followee.id - }) - else - {:error, _reason} -> - render(conn, follow_template(user), %{error: :error}) - end - end - end - - defp follow_template(%User{} = _user), do: "follow.html" - defp follow_template(_), do: "follow_login.html" - - defp is_status?(acct) do - case Pleroma.Object.Fetcher.fetch_and_contain_remote_object_from_id(acct) do - {:ok, %{"type" => type}} when type in ["Article", "Note", "Video", "Page", "Question"] -> - true - - _ -> - false - end - end - - def do_remote_follow(conn, %{ - "authorization" => %{"name" => username, "password" => password, "id" => id} - }) do - with %User{} = followee <- User.get_cached_by_id(id), - {_, %User{} = user, _} <- {:auth, User.get_cached_by_nickname(username), followee}, - {_, true, _} <- { - :auth, - AuthenticationPlug.checkpw(password, user.password_hash), - followee - }, - {:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do - conn - |> render("followed.html", %{error: false}) - else - # Was already following user - {:error, "Could not follow user:" <> _rest} -> - render(conn, "followed.html", %{error: "Error following account"}) - - {:auth, _, followee} -> - conn - |> render("follow_login.html", %{ - error: "Wrong username or password", - id: id, - name: followee.nickname, - avatar: User.avatar_url(followee) - }) - - e -> - Logger.debug("Remote follow failed with error #{inspect(e)}") - render(conn, "followed.html", %{error: "Something went wrong."}) - end - end - - def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do - with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)}, - {:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do - conn - |> render("followed.html", %{error: false}) - else - # Was already following user - {:error, "Could not follow user:" <> _rest} -> - render(conn, "followed.html", %{error: "Error following account"}) - - {:fetch_user, error} -> - Logger.debug("Remote follow failed with error #{inspect(error)}") - render(conn, "followed.html", %{error: "Could not find user"}) - - e -> - Logger.debug("Remote follow failed with error #{inspect(e)}") - render(conn, "followed.html", %{error: "Something went wrong."}) - end - end - def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do with {:ok, _} <- Notification.read_one(user, notification_id) do json(conn, %{status: "success"}) @@ -345,7 +262,9 @@ def change_email(%{assigns: %{user: user}} = conn, params) do end def delete_account(%{assigns: %{user: user}} = conn, params) do - case CommonAPI.Utils.confirm_current_password(user, params["password"]) do + password = params["password"] || "" + + case CommonAPI.Utils.confirm_current_password(user, password) do {:ok, user} -> User.delete(user) json(conn, %{status: "success"}) diff --git a/lib/pleroma/web/twitter_api/views/remote_follow_view.ex b/lib/pleroma/web/twitter_api/views/remote_follow_view.ex new file mode 100644 index 000000000..d469c4726 --- /dev/null +++ b/lib/pleroma/web/twitter_api/views/remote_follow_view.ex @@ -0,0 +1,10 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.TwitterAPI.RemoteFollowView do + use Pleroma.Web, :view + import Phoenix.HTML.Form + + defdelegate avatar_url(user), to: Pleroma.User +end diff --git a/mix.exs b/mix.exs index f1a62f6e3..c2e0b940f 100644 --- a/mix.exs +++ b/mix.exs @@ -165,7 +165,7 @@ defp deps do ref: "825dc00aaba5a1b7c4202a532b696b595dd3bcb3"}, {:captcha, git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", - ref: "c3c795c55f6b49d79d6ac70a0f91e525099fc3e2"}, + ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"}, {:mox, "~> 0.5", only: :test} ] ++ oauth_deps() end diff --git a/mix.lock b/mix.lock index 401bcdb6e..ff22791a4 100644 --- a/mix.lock +++ b/mix.lock @@ -8,7 +8,7 @@ "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, "cachex": {:hex, :cachex, "3.0.3", "4e2d3e05814a5738f5ff3903151d5c25636d72a3527251b753f501ad9c657967", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"}, "calendar": {:hex, :calendar, "0.17.6", "ec291cb2e4ba499c2e8c0ef5f4ace974e2f9d02ae9e807e711a9b0c7850b9aee", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"}, - "captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "c3c795c55f6b49d79d6ac70a0f91e525099fc3e2", [ref: "c3c795c55f6b49d79d6ac70a0f91e525099fc3e2"]}, + "captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]}, "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm"}, "comeonin": {:hex, :comeonin, "4.1.2", "3eb5620fd8e35508991664b4c2b04dd41e52f1620b36957be837c1d7784b7592", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"}, diff --git a/priv/static/adminfe/app.8589ec81.css b/priv/static/adminfe/app.fdd73ce4.css similarity index 50% rename from priv/static/adminfe/app.8589ec81.css rename to priv/static/adminfe/app.fdd73ce4.css index b82fcc39e..473ec1b86 100644 Binary files a/priv/static/adminfe/app.8589ec81.css and b/priv/static/adminfe/app.fdd73ce4.css differ diff --git a/priv/static/adminfe/chunk-0cb6.8d811a09.css b/priv/static/adminfe/chunk-0cb6.8d811a09.css deleted file mode 100644 index cbf59cfb5..000000000 Binary files a/priv/static/adminfe/chunk-0cb6.8d811a09.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-0cc4.571d0025.css b/priv/static/adminfe/chunk-0cc4.571d0025.css new file mode 100644 index 000000000..8bd6a2e50 Binary files /dev/null and b/priv/static/adminfe/chunk-0cc4.571d0025.css differ diff --git a/priv/static/adminfe/chunk-15fa.6e185c68.css b/priv/static/adminfe/chunk-15fa.2246593e.css similarity index 100% rename from priv/static/adminfe/chunk-15fa.6e185c68.css rename to priv/static/adminfe/chunk-15fa.2246593e.css diff --git a/priv/static/adminfe/chunk-18e1.5bd2ca85.css b/priv/static/adminfe/chunk-18e1.ed715f8d.css similarity index 100% rename from priv/static/adminfe/chunk-18e1.5bd2ca85.css rename to priv/static/adminfe/chunk-18e1.ed715f8d.css diff --git a/priv/static/adminfe/chunk-7c6b.c7882778.css b/priv/static/adminfe/chunk-1c46.f36071a4.css similarity index 100% rename from priv/static/adminfe/chunk-7c6b.c7882778.css rename to priv/static/adminfe/chunk-1c46.f36071a4.css diff --git a/priv/static/adminfe/chunk-2943.1b6fd9a7.css b/priv/static/adminfe/chunk-2943.1b6fd9a7.css deleted file mode 100644 index 0c9284744..000000000 Binary files a/priv/static/adminfe/chunk-2943.1b6fd9a7.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-3d1c.b2eb7234.css b/priv/static/adminfe/chunk-3d1c.b2eb7234.css deleted file mode 100644 index ba85e77d5..000000000 Binary files a/priv/static/adminfe/chunk-3d1c.b2eb7234.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-4df4.e217dea0.css b/priv/static/adminfe/chunk-4df4.e217dea0.css deleted file mode 100644 index 4672a9f75..000000000 Binary files a/priv/static/adminfe/chunk-4df4.e217dea0.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-7de9.889d1da1.css b/priv/static/adminfe/chunk-7de9.889d1da1.css new file mode 100644 index 000000000..29f7b475d Binary files /dev/null and b/priv/static/adminfe/chunk-7de9.889d1da1.css differ diff --git a/priv/static/adminfe/chunk-7f8e.b6944d38.css b/priv/static/adminfe/chunk-7f8e.d508c376.css similarity index 100% rename from priv/static/adminfe/chunk-7f8e.b6944d38.css rename to priv/static/adminfe/chunk-7f8e.d508c376.css diff --git a/priv/static/adminfe/chunk-538a.062aa087.css b/priv/static/adminfe/chunk-9bb0.2a82c722.css similarity index 62% rename from priv/static/adminfe/chunk-538a.062aa087.css rename to priv/static/adminfe/chunk-9bb0.2a82c722.css index 9e23d0fdb..c0074e6f7 100644 Binary files a/priv/static/adminfe/chunk-538a.062aa087.css and b/priv/static/adminfe/chunk-9bb0.2a82c722.css differ diff --git a/priv/static/adminfe/chunk-a601.62c86eea.css b/priv/static/adminfe/chunk-a601.62c86eea.css new file mode 100644 index 000000000..a036b0253 Binary files /dev/null and b/priv/static/adminfe/chunk-a601.62c86eea.css differ diff --git a/priv/static/adminfe/chunk-23b2.723b6cc5.css b/priv/static/adminfe/chunk-d01a.03fe0a3f.css similarity index 86% rename from priv/static/adminfe/chunk-23b2.723b6cc5.css rename to priv/static/adminfe/chunk-d01a.03fe0a3f.css index 172bce317..f0b3bf144 100644 Binary files a/priv/static/adminfe/chunk-23b2.723b6cc5.css and b/priv/static/adminfe/chunk-d01a.03fe0a3f.css differ diff --git a/priv/static/adminfe/chunk-f3c9.155bfc51.css b/priv/static/adminfe/chunk-f3c9.155bfc51.css new file mode 100644 index 000000000..1cb3e5949 Binary files /dev/null and b/priv/static/adminfe/chunk-f3c9.155bfc51.css differ diff --git a/priv/static/adminfe/index.html b/priv/static/adminfe/index.html index 70bb8bd3b..d238accb5 100644 --- a/priv/static/adminfe/index.html +++ b/priv/static/adminfe/index.html @@ -1 +1 @@ -Admin FE
\ No newline at end of file +Admin FE
\ No newline at end of file diff --git a/priv/static/adminfe/static/js/ZhIB.861df339.js b/priv/static/adminfe/static/js/ZhIB.861df339.js new file mode 100644 index 000000000..aeec873c8 Binary files /dev/null and b/priv/static/adminfe/static/js/ZhIB.861df339.js differ diff --git a/priv/static/adminfe/static/js/ZhIB.861df339.js.map b/priv/static/adminfe/static/js/ZhIB.861df339.js.map new file mode 100644 index 000000000..ff11a2e71 Binary files /dev/null and b/priv/static/adminfe/static/js/ZhIB.861df339.js.map differ diff --git a/priv/static/adminfe/static/js/app.19b7049e.js b/priv/static/adminfe/static/js/app.19b7049e.js new file mode 100644 index 000000000..d33589df4 Binary files /dev/null and b/priv/static/adminfe/static/js/app.19b7049e.js differ diff --git a/priv/static/adminfe/static/js/app.19b7049e.js.map b/priv/static/adminfe/static/js/app.19b7049e.js.map new file mode 100644 index 000000000..90c7816c0 Binary files /dev/null and b/priv/static/adminfe/static/js/app.19b7049e.js.map differ diff --git a/priv/static/adminfe/static/js/app.9c4316f1.js b/priv/static/adminfe/static/js/app.9c4316f1.js deleted file mode 100644 index 6af94c36b..000000000 Binary files a/priv/static/adminfe/static/js/app.9c4316f1.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/app.9c4316f1.js.map b/priv/static/adminfe/static/js/app.9c4316f1.js.map deleted file mode 100644 index 4b729c61a..000000000 Binary files a/priv/static/adminfe/static/js/app.9c4316f1.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0620.c765c190.js b/priv/static/adminfe/static/js/chunk-0620.c765c190.js deleted file mode 100644 index 72077a5ac..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0620.c765c190.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0620.c765c190.js.map b/priv/static/adminfe/static/js/chunk-0620.c765c190.js.map deleted file mode 100644 index f39b8237f..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0620.c765c190.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0cb6.b9f32e0c.js b/priv/static/adminfe/static/js/chunk-0cb6.b9f32e0c.js deleted file mode 100644 index 967eefab2..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0cb6.b9f32e0c.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0cb6.b9f32e0c.js.map b/priv/static/adminfe/static/js/chunk-0cb6.b9f32e0c.js.map deleted file mode 100644 index c8ade8253..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0cb6.b9f32e0c.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0cc4.35b47d0a.js b/priv/static/adminfe/static/js/chunk-0cc4.35b47d0a.js new file mode 100644 index 000000000..71b39bb28 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0cc4.35b47d0a.js differ diff --git a/priv/static/adminfe/static/js/chunk-0cc4.35b47d0a.js.map b/priv/static/adminfe/static/js/chunk-0cc4.35b47d0a.js.map new file mode 100644 index 000000000..4935ac41f Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0cc4.35b47d0a.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-15fa.34dcb9d8.js b/priv/static/adminfe/static/js/chunk-15fa.10871dbf.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-15fa.34dcb9d8.js rename to priv/static/adminfe/static/js/chunk-15fa.10871dbf.js index b0819b138..8c7b033cf 100644 Binary files a/priv/static/adminfe/static/js/chunk-15fa.34dcb9d8.js and b/priv/static/adminfe/static/js/chunk-15fa.10871dbf.js differ diff --git a/priv/static/adminfe/static/js/chunk-15fa.34dcb9d8.js.map b/priv/static/adminfe/static/js/chunk-15fa.10871dbf.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-15fa.34dcb9d8.js.map rename to priv/static/adminfe/static/js/chunk-15fa.10871dbf.js.map index 2ec54c8aa..a4b89ba68 100644 Binary files a/priv/static/adminfe/static/js/chunk-15fa.34dcb9d8.js.map and b/priv/static/adminfe/static/js/chunk-15fa.10871dbf.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-18e1.f8bb78f3.js b/priv/static/adminfe/static/js/chunk-18e1.9f7c9b0f.js similarity index 97% rename from priv/static/adminfe/static/js/chunk-18e1.f8bb78f3.js rename to priv/static/adminfe/static/js/chunk-18e1.9f7c9b0f.js index 4ddfe2bc2..237c35b90 100644 Binary files a/priv/static/adminfe/static/js/chunk-18e1.f8bb78f3.js and b/priv/static/adminfe/static/js/chunk-18e1.9f7c9b0f.js differ diff --git a/priv/static/adminfe/static/js/chunk-18e1.f8bb78f3.js.map b/priv/static/adminfe/static/js/chunk-18e1.9f7c9b0f.js.map similarity index 98% rename from priv/static/adminfe/static/js/chunk-18e1.f8bb78f3.js.map rename to priv/static/adminfe/static/js/chunk-18e1.9f7c9b0f.js.map index b61e3bc20..2cfd1cfcc 100644 Binary files a/priv/static/adminfe/static/js/chunk-18e1.f8bb78f3.js.map and b/priv/static/adminfe/static/js/chunk-18e1.9f7c9b0f.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-1c46.b92c7c1b.js b/priv/static/adminfe/static/js/chunk-1c46.b92c7c1b.js new file mode 100644 index 000000000..c63a38759 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-1c46.b92c7c1b.js differ diff --git a/priv/static/adminfe/static/js/chunk-1c46.b92c7c1b.js.map b/priv/static/adminfe/static/js/chunk-1c46.b92c7c1b.js.map new file mode 100644 index 000000000..a0d4a6655 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-1c46.b92c7c1b.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-23b2.442bb8df.js b/priv/static/adminfe/static/js/chunk-23b2.442bb8df.js deleted file mode 100644 index 61cfc7826..000000000 Binary files a/priv/static/adminfe/static/js/chunk-23b2.442bb8df.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-23b2.442bb8df.js.map b/priv/static/adminfe/static/js/chunk-23b2.442bb8df.js.map deleted file mode 100644 index 474d1086e..000000000 Binary files a/priv/static/adminfe/static/js/chunk-23b2.442bb8df.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-2943.8ab5d0d9.js b/priv/static/adminfe/static/js/chunk-2943.8ab5d0d9.js deleted file mode 100644 index 85b40b995..000000000 Binary files a/priv/static/adminfe/static/js/chunk-2943.8ab5d0d9.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-2943.8ab5d0d9.js.map b/priv/static/adminfe/static/js/chunk-2943.8ab5d0d9.js.map deleted file mode 100644 index 0ecc45de4..000000000 Binary files a/priv/static/adminfe/static/js/chunk-2943.8ab5d0d9.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-3d1c.3334d3f1.js b/priv/static/adminfe/static/js/chunk-3d1c.3334d3f1.js deleted file mode 100644 index 9a9c3b049..000000000 Binary files a/priv/static/adminfe/static/js/chunk-3d1c.3334d3f1.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-3d1c.3334d3f1.js.map b/priv/static/adminfe/static/js/chunk-3d1c.3334d3f1.js.map deleted file mode 100644 index 3dd0d77a9..000000000 Binary files a/priv/static/adminfe/static/js/chunk-3d1c.3334d3f1.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-4df4.9655f394.js b/priv/static/adminfe/static/js/chunk-4df4.9655f394.js deleted file mode 100644 index afed4bab6..000000000 Binary files a/priv/static/adminfe/static/js/chunk-4df4.9655f394.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-4df4.9655f394.js.map b/priv/static/adminfe/static/js/chunk-4df4.9655f394.js.map deleted file mode 100644 index a1e9bca7a..000000000 Binary files a/priv/static/adminfe/static/js/chunk-4df4.9655f394.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-538a.04530055.js b/priv/static/adminfe/static/js/chunk-538a.04530055.js deleted file mode 100644 index 2455b9a9a..000000000 Binary files a/priv/static/adminfe/static/js/chunk-538a.04530055.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-538a.04530055.js.map b/priv/static/adminfe/static/js/chunk-538a.04530055.js.map deleted file mode 100644 index d3741c30a..000000000 Binary files a/priv/static/adminfe/static/js/chunk-538a.04530055.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-7c6b.5240e052.js b/priv/static/adminfe/static/js/chunk-7c6b.5240e052.js deleted file mode 100644 index 12eb54a32..000000000 Binary files a/priv/static/adminfe/static/js/chunk-7c6b.5240e052.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-7c6b.5240e052.js.map b/priv/static/adminfe/static/js/chunk-7c6b.5240e052.js.map deleted file mode 100644 index 1463b8ba4..000000000 Binary files a/priv/static/adminfe/static/js/chunk-7c6b.5240e052.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-7de9.7b8cda50.js b/priv/static/adminfe/static/js/chunk-7de9.7b8cda50.js new file mode 100644 index 000000000..6311e5aa9 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-7de9.7b8cda50.js differ diff --git a/priv/static/adminfe/static/js/chunk-7de9.7b8cda50.js.map b/priv/static/adminfe/static/js/chunk-7de9.7b8cda50.js.map new file mode 100644 index 000000000..2172dcc92 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-7de9.7b8cda50.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-7f8e.c1eb619d.js b/priv/static/adminfe/static/js/chunk-7f8e.2c3e63e9.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-7f8e.c1eb619d.js rename to priv/static/adminfe/static/js/chunk-7f8e.2c3e63e9.js index 56ce1d5ef..06988ef73 100644 Binary files a/priv/static/adminfe/static/js/chunk-7f8e.c1eb619d.js and b/priv/static/adminfe/static/js/chunk-7f8e.2c3e63e9.js differ diff --git a/priv/static/adminfe/static/js/chunk-7f8e.c1eb619d.js.map b/priv/static/adminfe/static/js/chunk-7f8e.2c3e63e9.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-7f8e.c1eb619d.js.map rename to priv/static/adminfe/static/js/chunk-7f8e.2c3e63e9.js.map index 459e7f785..bf96082a8 100644 Binary files a/priv/static/adminfe/static/js/chunk-7f8e.c1eb619d.js.map and b/priv/static/adminfe/static/js/chunk-7f8e.2c3e63e9.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-9bb0.9c56835f.js b/priv/static/adminfe/static/js/chunk-9bb0.9c56835f.js new file mode 100644 index 000000000..3e9a3e795 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-9bb0.9c56835f.js differ diff --git a/priv/static/adminfe/static/js/chunk-9bb0.9c56835f.js.map b/priv/static/adminfe/static/js/chunk-9bb0.9c56835f.js.map new file mode 100644 index 000000000..929c7ed14 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-9bb0.9c56835f.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-a601.cc880efe.js b/priv/static/adminfe/static/js/chunk-a601.cc880efe.js new file mode 100644 index 000000000..b30fe3033 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-a601.cc880efe.js differ diff --git a/priv/static/adminfe/static/js/chunk-a601.cc880efe.js.map b/priv/static/adminfe/static/js/chunk-a601.cc880efe.js.map new file mode 100644 index 000000000..5d2358da2 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-a601.cc880efe.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-d01a.970cf312.js b/priv/static/adminfe/static/js/chunk-d01a.970cf312.js new file mode 100644 index 000000000..7bbd51e24 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-d01a.970cf312.js differ diff --git a/priv/static/adminfe/static/js/chunk-d01a.970cf312.js.map b/priv/static/adminfe/static/js/chunk-d01a.970cf312.js.map new file mode 100644 index 000000000..da3fbc95a Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-d01a.970cf312.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-f3c9.b3de53e2.js b/priv/static/adminfe/static/js/chunk-f3c9.b3de53e2.js new file mode 100644 index 000000000..1c301e14a Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-f3c9.b3de53e2.js differ diff --git a/priv/static/adminfe/static/js/chunk-f3c9.b3de53e2.js.map b/priv/static/adminfe/static/js/chunk-f3c9.b3de53e2.js.map new file mode 100644 index 000000000..3b53caf53 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-f3c9.b3de53e2.js.map differ diff --git a/priv/static/adminfe/static/js/runtime.46db235c.js b/priv/static/adminfe/static/js/runtime.46db235c.js deleted file mode 100644 index 898c5b505..000000000 Binary files a/priv/static/adminfe/static/js/runtime.46db235c.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/runtime.46db235c.js.map b/priv/static/adminfe/static/js/runtime.46db235c.js.map deleted file mode 100644 index 33791c005..000000000 Binary files a/priv/static/adminfe/static/js/runtime.46db235c.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/runtime.d6d1aaab.js b/priv/static/adminfe/static/js/runtime.d6d1aaab.js new file mode 100644 index 000000000..ca1a91002 Binary files /dev/null and b/priv/static/adminfe/static/js/runtime.d6d1aaab.js differ diff --git a/priv/static/adminfe/static/js/runtime.d6d1aaab.js.map b/priv/static/adminfe/static/js/runtime.d6d1aaab.js.map new file mode 100644 index 000000000..b938f1ba6 Binary files /dev/null and b/priv/static/adminfe/static/js/runtime.d6d1aaab.js.map differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/langs/zh_CN.js b/priv/static/adminfe/static/tinymce4.7.5/langs/zh_CN.js deleted file mode 100644 index e11f322cc..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/langs/zh_CN.js and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/codesample/css/prism.css b/priv/static/adminfe/static/tinymce4.7.5/plugins/codesample/css/prism.css deleted file mode 100644 index 128237fba..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/codesample/css/prism.css and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-cool.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-cool.gif deleted file mode 100644 index ba90cc36f..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-cool.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-cry.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-cry.gif deleted file mode 100644 index 74d897a4f..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-cry.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-embarassed.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-embarassed.gif deleted file mode 100644 index 963a96b8a..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-embarassed.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-foot-in-mouth.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-foot-in-mouth.gif deleted file mode 100644 index c7cf1011d..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-foot-in-mouth.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-frown.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-frown.gif deleted file mode 100644 index 716f55e16..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-frown.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-innocent.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-innocent.gif deleted file mode 100644 index 334d49e0e..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-innocent.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-kiss.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-kiss.gif deleted file mode 100644 index 4efd549ed..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-kiss.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-laughing.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-laughing.gif deleted file mode 100644 index 82c5b182e..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-laughing.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-money-mouth.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-money-mouth.gif deleted file mode 100644 index ca2451e10..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-money-mouth.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-sealed.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-sealed.gif deleted file mode 100644 index fe66220c2..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-sealed.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-smile.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-smile.gif deleted file mode 100644 index fd27edfaa..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-smile.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-surprised.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-surprised.gif deleted file mode 100644 index 0cc9bb71c..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-surprised.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-tongue-out.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-tongue-out.gif deleted file mode 100644 index 2075dc160..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-tongue-out.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-undecided.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-undecided.gif deleted file mode 100644 index bef7e2573..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-undecided.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-wink.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-wink.gif deleted file mode 100644 index 0631c7616..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-wink.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-yell.gif b/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-yell.gif deleted file mode 100644 index 648e6e879..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/emoticons/img/smiley-yell.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/plugins/visualblocks/css/visualblocks.css b/priv/static/adminfe/static/tinymce4.7.5/plugins/visualblocks/css/visualblocks.css deleted file mode 100644 index 96e4d7c5d..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/plugins/visualblocks/css/visualblocks.css and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/content.inline.min.css b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/content.inline.min.css deleted file mode 100644 index 7b45d3397..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/content.inline.min.css and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/content.min.css b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/content.min.css deleted file mode 100644 index bad168cfe..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/content.min.css and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-mobile.woff b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-mobile.woff deleted file mode 100644 index 1e3be038a..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-mobile.woff and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.eot b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.eot deleted file mode 100644 index b144ba0bd..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.eot and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.svg b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.svg deleted file mode 100644 index b4ee6f408..000000000 --- a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - -Generated by IcoMoon - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.ttf b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.ttf deleted file mode 100644 index a983e2dc4..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.ttf and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.woff b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.woff deleted file mode 100644 index d8962df76..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.woff and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.eot b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.eot deleted file mode 100644 index 5336c38ff..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.eot and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.svg b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.svg deleted file mode 100644 index 9fa215f3d..000000000 --- a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.svg +++ /dev/null @@ -1,131 +0,0 @@ - - - -Generated by IcoMoon - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.ttf b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.ttf deleted file mode 100644 index 61a48a511..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.ttf and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.woff b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.woff deleted file mode 100644 index aace5d9c5..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.woff and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/img/anchor.gif b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/img/anchor.gif deleted file mode 100644 index 606348c7f..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/img/anchor.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/img/loader.gif b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/img/loader.gif deleted file mode 100644 index c69e93723..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/img/loader.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/img/object.gif b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/img/object.gif deleted file mode 100644 index cccd7f023..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/img/object.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/img/trans.gif b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/img/trans.gif deleted file mode 100644 index 388486517..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/img/trans.gif and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/skin.min.css b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/skin.min.css deleted file mode 100644 index 4ad815bf5..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/skin.min.css and /dev/null differ diff --git a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/skin.min.css.map b/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/skin.min.css.map deleted file mode 100644 index c8763dcc3..000000000 --- a/priv/static/adminfe/static/tinymce4.7.5/skins/lightgray/skin.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["./src/skins/lightgray/main/less/desktop/Reset.less","./src/skins/lightgray/main/less/desktop/Variables.less","./src/skins/lightgray/main/less/desktop/Mixins.less","./src/skins/lightgray/main/less/desktop/Animations.less","./src/skins/lightgray/main/less/desktop/TinyMCE.less","./src/skins/lightgray/main/less/desktop/CropRect.less","./src/skins/lightgray/main/less/desktop/ImagePanel.less","./src/skins/lightgray/main/less/desktop/Arrows.less","./src/skins/lightgray/main/less/desktop/Sidebar.less","./src/skins/lightgray/main/less/desktop/Container.less","./src/skins/lightgray/main/less/desktop/Scrollable.less","./src/skins/lightgray/main/less/desktop/Panel.less","./src/skins/lightgray/main/less/desktop/FloatPanel.less","./src/skins/lightgray/main/less/desktop/Window.less","./src/skins/lightgray/main/less/desktop/ToolTip.less","./src/skins/lightgray/main/less/desktop/Progress.less","./src/skins/lightgray/main/less/desktop/Notification.less","./src/skins/lightgray/main/less/desktop/AbsoluteLayout.less","./src/skins/lightgray/main/less/desktop/Button.less","./src/skins/lightgray/main/less/desktop/ButtonGroup.less","./src/skins/lightgray/main/less/desktop/Checkbox.less","./src/skins/lightgray/main/less/desktop/ComboBox.less","./src/skins/lightgray/main/less/desktop/ColorBox.less","./src/skins/lightgray/main/less/desktop/ColorButton.less","./src/skins/lightgray/main/less/desktop/ColorPicker.less","./src/skins/lightgray/main/less/desktop/Path.less","./src/skins/lightgray/main/less/desktop/FieldSet.less","./src/skins/lightgray/main/less/desktop/FitLayout.less","./src/skins/lightgray/main/less/desktop/FlowLayout.less","./src/skins/lightgray/main/less/desktop/Iframe.less","./src/skins/lightgray/main/less/desktop/InfoBox.less","./src/skins/lightgray/main/less/desktop/Label.less","./src/skins/lightgray/main/less/desktop/MenuBar.less","./src/skins/lightgray/main/less/desktop/MenuButton.less","./src/skins/lightgray/main/less/desktop/MenuItem.less","./src/skins/lightgray/main/less/desktop/Throbber.less","./src/skins/lightgray/main/less/desktop/Menu.less","./src/skins/lightgray/main/less/desktop/ListBox.less","./src/skins/lightgray/main/less/desktop/ResizeHandle.less","./src/skins/lightgray/main/less/desktop/SelectBox.less","./src/skins/lightgray/main/less/desktop/Slider.less","./src/skins/lightgray/main/less/desktop/Spacer.less","./src/skins/lightgray/main/less/desktop/SplitButton.less","./src/skins/lightgray/main/less/desktop/StackLayout.less","./src/skins/lightgray/main/less/desktop/TabPanel.less","./src/skins/lightgray/main/less/desktop/TextBox.less","./src/skins/lightgray/main/less/desktop/DropZone.less","./src/skins/lightgray/main/less/desktop/BrowseButton.less","./src/skins/lightgray/main/less/desktop/Icons.less","./src/skins/lightgray/main/less/desktop/FilePicker.less"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/priv/static/adminfe/static/tinymce4.7.5/tinymce.min.js b/priv/static/adminfe/static/tinymce4.7.5/tinymce.min.js deleted file mode 100644 index d7fcac80b..000000000 Binary files a/priv/static/adminfe/static/tinymce4.7.5/tinymce.min.js and /dev/null differ diff --git a/test/conversation/participation_test.exs b/test/conversation/participation_test.exs index ba81c0d4b..ab9f27b2f 100644 --- a/test/conversation/participation_test.exs +++ b/test/conversation/participation_test.exs @@ -125,9 +125,10 @@ test "recreating an existing participations sets it to unread" do test "it marks a participation as read" do participation = insert(:participation, %{read: false}) - {:ok, participation} = Participation.mark_as_read(participation) + {:ok, updated_participation} = Participation.mark_as_read(participation) - assert participation.read + assert updated_participation.read + assert updated_participation.updated_at == participation.updated_at end test "it marks a participation as unread" do diff --git a/test/fixtures/modules/runtime_module.ex b/test/fixtures/modules/runtime_module.ex new file mode 100644 index 000000000..4711c3532 --- /dev/null +++ b/test/fixtures/modules/runtime_module.ex @@ -0,0 +1,9 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule RuntimeModule do + @moduledoc """ + This is a dummy module to test custom runtime modules. + """ +end diff --git a/test/fixtures/tesla_mock/mobilizon.org-event.json b/test/fixtures/tesla_mock/mobilizon.org-event.json new file mode 100644 index 000000000..7411cf817 --- /dev/null +++ b/test/fixtures/tesla_mock/mobilizon.org-event.json @@ -0,0 +1 @@ +{"@context":["https://www.w3.org/ns/activitystreams","https://litepub.social/litepub/context.jsonld",{"GeoCoordinates":"sc:GeoCoordinates","Hashtag":"as:Hashtag","Place":"sc:Place","PostalAddress":"sc:PostalAddress","address":{"@id":"sc:address","@type":"sc:PostalAddress"},"addressCountry":"sc:addressCountry","addressLocality":"sc:addressLocality","addressRegion":"sc:addressRegion","category":"sc:category","commentsEnabled":{"@id":"pt:commentsEnabled","@type":"sc:Boolean"},"geo":{"@id":"sc:geo","@type":"sc:GeoCoordinates"},"ical":"http://www.w3.org/2002/12/cal/ical#","joinMode":{"@id":"mz:joinMode","@type":"mz:joinModeType"},"joinModeType":{"@id":"mz:joinModeType","@type":"rdfs:Class"},"location":{"@id":"sc:location","@type":"sc:Place"},"maximumAttendeeCapacity":"sc:maximumAttendeeCapacity","mz":"https://joinmobilizon.org/ns#","postalCode":"sc:postalCode","pt":"https://joinpeertube.org/ns#","repliesModerationOption":{"@id":"mz:repliesModerationOption","@type":"mz:repliesModerationOptionType"},"repliesModerationOptionType":{"@id":"mz:repliesModerationOptionType","@type":"rdfs:Class"},"sc":"http://schema.org#","streetAddress":"sc:streetAddress","uuid":"sc:identifier"}],"actor":"https://mobilizon.org/@tcit","attributedTo":"https://mobilizon.org/@tcit","category":"meeting","cc":[],"commentsEnabled":true,"content":"

Mobilizon is now federated! 🎉

You can view this event from other instances if they are subscribed to mobilizon.org, and soon directly from Mastodon and Pleroma. It is possible that you may see some comments from other instances, including Mastodon ones, just below.

With a Mobilizon account on an instance, you may participate at events from other instances and add comments on events.

Of course, it's still a work in progress: if reports made from an instance on events and comments can be federated, you can't block people right now, and moderators actions are rather limited, but this will definitely get fixed over time until first stable version next year.

Anyway, if you want to come up with some feedback, head over to our forum or - if you feel you have technical skills and are familiar with it - on our Gitlab repository.

Also, to people that want to set Mobilizon themselves even though we really don't advise to do that for now, we have a little documentation but it's quite the early days and you'll probably need some help. No worries, you can chat with us on our Forum or though our Matrix channel.

Check our website for more informations and follow us on Twitter or Mastodon.

","endTime":"2019-12-18T14:00:00Z","ical:status":"CONFIRMED","id":"https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39","joinMode":"free","location":{"address":{"addressCountry":"France","addressLocality":"Nantes","addressRegion":"Pays de la Loire","postalCode":null,"streetAddress":" ","type":"PostalAddress"},"geo":{"latitude":-1.54939699141711,"longitude":47.21617415,"type":"GeoCoordinates"},"id":"https://mobilizon.org/address/1368fdab-1e2c-4de6-bcff-a90c84abdee1","name":"Cour du Château des Ducs de Bretagne","type":"Place"},"maximumAttendeeCapacity":0,"mediaType":"text/html","name":"Mobilizon Launching Party","published":"2019-12-17T11:33:56Z","repliesModerationOption":"allow_all","startTime":"2019-12-18T13:00:00Z","tag":[{"href":"https://mobilizon.org/tags/mobilizon","name":"#Mobilizon","type":"Hashtag"},{"href":"https://mobilizon.org/tags/federation","name":"#Federation","type":"Hashtag"},{"href":"https://mobilizon.org/tags/activitypub","name":"#ActivityPub","type":"Hashtag"},{"href":"https://mobilizon.org/tags/party","name":"#Party","type":"Hashtag"}],"to":["https://www.w3.org/ns/activitystreams#Public"],"type":"Event","updated":"2019-12-17T12:25:01Z","url":"https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39","uuid":"252d5816-00a3-4a89-a66f-15bf65c33e39"} \ No newline at end of file diff --git a/test/fixtures/tesla_mock/mobilizon.org-user.json b/test/fixtures/tesla_mock/mobilizon.org-user.json new file mode 100644 index 000000000..f948ae5f0 --- /dev/null +++ b/test/fixtures/tesla_mock/mobilizon.org-user.json @@ -0,0 +1 @@ +{"@context":["https://www.w3.org/ns/activitystreams","https://litepub.social/litepub/context.jsonld",{"GeoCoordinates":"sc:GeoCoordinates","Hashtag":"as:Hashtag","Place":"sc:Place","PostalAddress":"sc:PostalAddress","address":{"@id":"sc:address","@type":"sc:PostalAddress"},"addressCountry":"sc:addressCountry","addressLocality":"sc:addressLocality","addressRegion":"sc:addressRegion","category":"sc:category","commentsEnabled":{"@id":"pt:commentsEnabled","@type":"sc:Boolean"},"geo":{"@id":"sc:geo","@type":"sc:GeoCoordinates"},"ical":"http://www.w3.org/2002/12/cal/ical#","joinMode":{"@id":"mz:joinMode","@type":"mz:joinModeType"},"joinModeType":{"@id":"mz:joinModeType","@type":"rdfs:Class"},"location":{"@id":"sc:location","@type":"sc:Place"},"maximumAttendeeCapacity":"sc:maximumAttendeeCapacity","mz":"https://joinmobilizon.org/ns#","postalCode":"sc:postalCode","pt":"https://joinpeertube.org/ns#","repliesModerationOption":{"@id":"mz:repliesModerationOption","@type":"mz:repliesModerationOptionType"},"repliesModerationOptionType":{"@id":"mz:repliesModerationOptionType","@type":"rdfs:Class"},"sc":"http://schema.org#","streetAddress":"sc:streetAddress","uuid":"sc:identifier"}],"endpoints":{"sharedInbox":"https://mobilizon.org/inbox"},"followers":"https://mobilizon.org/@tcit/followers","following":"https://mobilizon.org/@tcit/following","icon":{"mediaType":null,"type":"Image","url":"https://mobilizon.org/media/3a5f18c058a8193b1febfaf561f94ae8b91f85ac64c01ddf5ad7b251fb43baf5.jpg?name=profil.jpg"},"id":"https://mobilizon.org/@tcit","inbox":"https://mobilizon.org/@tcit/inbox","manuallyApprovesFollowers":false,"name":"Thomas Citharel","outbox":"https://mobilizon.org/@tcit/outbox","preferredUsername":"tcit","publicKey":{"id":"https://mobilizon.org/@tcit#main-key","owner":"https://mobilizon.org/@tcit","publicKeyPem":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAtzuZFviv5f12SuA0wZFMuwKS8RIlT3IjPCMLRDhiorZeV3UJ1lik\nDYO6mEh22KDXYgJtNVSYGF0Q5LJivgcvuvU+VQ048iTB1B2x0rHMr47KPByPjfVb\nKDeHt6fkHcLY0JK8UkIxW542wXAg4jX5w3gJi3pgTQrCT8VNyPbH1CaA0uW//9jc\nqzZQVFzpfdJoVOM9E3Urc/u58HC4xOptlM7+B/594ZI9drYwy5m+ZxHwlQUYCva4\n34dvwsfOGxkQyIrzXoep80EnWnFpYCLMcCiz+sEhPYxqLgNE+Cmn/6pv7SIscz6p\neVlQXIchdw+J4yl07paJDkFc6CNTCmaIHQIDAQAB\n-----END RSA PUBLIC KEY-----\n\n"},"summary":"Main profile","type":"Person","url":"https://mobilizon.org/@tcit"} \ No newline at end of file diff --git a/test/notification_test.exs b/test/notification_test.exs index ffa3d4b8c..f5f23bb5a 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -98,7 +98,7 @@ test "it creates a notification for user if the user blocks the activity author" assert Notification.create_notification(activity, user) end - test "it creates a notificatin for the user if the user mutes the activity author" do + test "it creates a notification for the user if the user mutes the activity author" do muter = insert(:user) muted = insert(:user) {:ok, _} = User.mute(muter, muted) diff --git a/test/object/fetcher_test.exs b/test/object/fetcher_test.exs index 9ae6b015d..2aad7a588 100644 --- a/test/object/fetcher_test.exs +++ b/test/object/fetcher_test.exs @@ -77,6 +77,15 @@ test "it can fetch peertube videos" do assert object end + test "it can fetch Mobilizon events" do + {:ok, object} = + Fetcher.fetch_object_from_id( + "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" + ) + + assert object + end + test "it can fetch wedistribute articles" do {:ok, object} = Fetcher.fetch_object_from_id("https://wedistribute.org/wp-json/pterotype/v1/object/85810") diff --git a/test/object_test.exs b/test/object_test.exs index 9247a6d84..b002c2bae 100644 --- a/test/object_test.exs +++ b/test/object_test.exs @@ -71,6 +71,74 @@ test "ensures cache is cleared for the object" do end end + describe "delete attachments" do + clear_config([Pleroma.Upload]) + + test "in subdirectories" do + Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) + + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + user = insert(:user) + + {:ok, %Object{} = attachment} = + Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id) + + %{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} = + note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}}) + + uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads]) + + path = href |> Path.dirname() |> Path.basename() + + assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}") + + Object.delete(note) + + assert Object.get_by_id(attachment.id) == nil + + assert {:ok, []} == File.ls("#{uploads_dir}/#{path}") + end + + test "with dedupe enabled" do + Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) + Pleroma.Config.put([Pleroma.Upload, :filters], [Pleroma.Upload.Filter.Dedupe]) + + uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads]) + + File.mkdir_p!(uploads_dir) + + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + user = insert(:user) + + {:ok, %Object{} = attachment} = + Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id) + + %{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} = + note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}}) + + filename = Path.basename(href) + + assert {:ok, files} = File.ls(uploads_dir) + assert filename in files + + Object.delete(note) + + assert Object.get_by_id(attachment.id) == nil + assert {:ok, files} = File.ls(uploads_dir) + refute filename in files + end + end + describe "normalizer" do test "fetches unknown objects by default" do %Object{} = diff --git a/test/plugs/oauth_scopes_plug_test.exs b/test/plugs/oauth_scopes_plug_test.exs index 89f32f43a..ce426677b 100644 --- a/test/plugs/oauth_scopes_plug_test.exs +++ b/test/plugs/oauth_scopes_plug_test.exs @@ -16,34 +16,6 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do :ok end - describe "when `assigns[:token]` is nil, " do - test "with :skip_instance_privacy_check option, proceeds with no op", %{conn: conn} do - conn = - conn - |> assign(:user, insert(:user)) - |> OAuthScopesPlug.call(%{scopes: ["read"], skip_instance_privacy_check: true}) - - refute conn.halted - assert conn.assigns[:user] - - refute called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) - end - - test "without :skip_instance_privacy_check option, calls EnsurePublicOrAuthenticatedPlug", %{ - conn: conn - } do - conn = - conn - |> assign(:user, insert(:user)) - |> OAuthScopesPlug.call(%{scopes: ["read"]}) - - refute conn.halted - assert conn.assigns[:user] - - assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) - end - end - test "if `token.scopes` fulfills specified 'any of' conditions, " <> "proceeds with no op", %{conn: conn} do @@ -75,64 +47,56 @@ test "if `token.scopes` fulfills specified 'all of' conditions, " <> end describe "with `fallback: :proceed_unauthenticated` option, " do - test "if `token.scopes` doesn't fulfill specified 'any of' conditions, " <> - "clears `assigns[:user]` and calls EnsurePublicOrAuthenticatedPlug", + test "if `token.scopes` doesn't fulfill specified conditions, " <> + "clears :user and :token assigns and calls EnsurePublicOrAuthenticatedPlug", %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + user = insert(:user) + token1 = insert(:oauth_token, scopes: ["read", "write"], user: user) - conn = - conn - |> assign(:user, token.user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: ["follow"], fallback: :proceed_unauthenticated}) + for token <- [token1, nil], op <- [:|, :&] do + ret_conn = + conn + |> assign(:user, user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{ + scopes: ["follow"], + op: op, + fallback: :proceed_unauthenticated + }) - refute conn.halted - refute conn.assigns[:user] + refute ret_conn.halted + refute ret_conn.assigns[:user] + refute ret_conn.assigns[:token] - assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) - end - - test "if `token.scopes` doesn't fulfill specified 'all of' conditions, " <> - "clears `assigns[:user] and calls EnsurePublicOrAuthenticatedPlug", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) - - conn = - conn - |> assign(:user, token.user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{ - scopes: ["read", "follow"], - op: :&, - fallback: :proceed_unauthenticated - }) - - refute conn.halted - refute conn.assigns[:user] - - assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + assert called(EnsurePublicOrAuthenticatedPlug.call(ret_conn, :_)) + end end test "with :skip_instance_privacy_check option, " <> "if `token.scopes` doesn't fulfill specified conditions, " <> - "clears `assigns[:user]` and does not call EnsurePublicOrAuthenticatedPlug", + "clears :user and :token assigns and does NOT call EnsurePublicOrAuthenticatedPlug", %{conn: conn} do - token = insert(:oauth_token, scopes: ["read:statuses", "write"]) |> Repo.preload(:user) + user = insert(:user) + token1 = insert(:oauth_token, scopes: ["read:statuses", "write"], user: user) - conn = - conn - |> assign(:user, token.user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{ - scopes: ["read"], - fallback: :proceed_unauthenticated, - skip_instance_privacy_check: true - }) + for token <- [token1, nil], op <- [:|, :&] do + ret_conn = + conn + |> assign(:user, user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{ + scopes: ["read"], + op: op, + fallback: :proceed_unauthenticated, + skip_instance_privacy_check: true + }) - refute conn.halted - refute conn.assigns[:user] + refute ret_conn.halted + refute ret_conn.assigns[:user] + refute ret_conn.assigns[:token] - refute called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + refute called(EnsurePublicOrAuthenticatedPlug.call(ret_conn, :_)) + end end end @@ -140,39 +104,42 @@ test "with :skip_instance_privacy_check option, " <> test "if `token.scopes` does not fulfill specified 'any of' conditions, " <> "returns 403 and halts", %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) - any_of_scopes = ["follow"] + for token <- [insert(:oauth_token, scopes: ["read", "write"]), nil] do + any_of_scopes = ["follow", "push"] - conn = - conn - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: any_of_scopes}) + ret_conn = + conn + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: any_of_scopes}) - assert conn.halted - assert 403 == conn.status + assert ret_conn.halted + assert 403 == ret_conn.status - expected_error = "Insufficient permissions: #{Enum.join(any_of_scopes, ", ")}." - assert Jason.encode!(%{error: expected_error}) == conn.resp_body + expected_error = "Insufficient permissions: #{Enum.join(any_of_scopes, " | ")}." + assert Jason.encode!(%{error: expected_error}) == ret_conn.resp_body + end end test "if `token.scopes` does not fulfill specified 'all of' conditions, " <> "returns 403 and halts", %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) - all_of_scopes = ["write", "follow"] + for token <- [insert(:oauth_token, scopes: ["read", "write"]), nil] do + token_scopes = (token && token.scopes) || [] + all_of_scopes = ["write", "follow"] - conn = - conn - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: all_of_scopes, op: :&}) + conn = + conn + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: all_of_scopes, op: :&}) - assert conn.halted - assert 403 == conn.status + assert conn.halted + assert 403 == conn.status - expected_error = - "Insufficient permissions: #{Enum.join(all_of_scopes -- token.scopes, ", ")}." + expected_error = + "Insufficient permissions: #{Enum.join(all_of_scopes -- token_scopes, " & ")}." - assert Jason.encode!(%{error: expected_error}) == conn.resp_body + assert Jason.encode!(%{error: expected_error}) == conn.resp_body + end end end diff --git a/test/runtime_test.exs b/test/runtime_test.exs new file mode 100644 index 000000000..f7b6f23d4 --- /dev/null +++ b/test/runtime_test.exs @@ -0,0 +1,11 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.RuntimeTest do + use ExUnit.Case, async: true + + test "it loads custom runtime modules" do + assert Code.ensure_compiled?(RuntimeModule) + end +end diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index 9897f72ce..22e72fc09 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -28,6 +28,26 @@ defmodule Pleroma.Web.ConnCase do # The default endpoint for testing @endpoint Pleroma.Web.Endpoint + + # Sets up OAuth access with specified scopes + defp oauth_access(scopes, opts \\ []) do + user = + Keyword.get_lazy(opts, :user, fn -> + Pleroma.Factory.insert(:user) + end) + + token = + Keyword.get_lazy(opts, :oauth_token, fn -> + Pleroma.Factory.insert(:oauth_token, user: user, scopes: scopes) + end) + + conn = + build_conn() + |> assign(:user, user) + |> assign(:token, token) + + %{user: user, token: token, conn: conn} + end end end diff --git a/test/support/factory.ex b/test/support/factory.ex index 314f26ec9..100864055 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -296,7 +296,7 @@ def oauth_app_factory do %Pleroma.Web.OAuth.App{ client_name: "Some client", redirect_uris: "https://example.com/callback", - scopes: ["read", "write", "follow", "push"], + scopes: ["read", "write", "follow", "push", "admin"], website: "https://example.com", client_id: Ecto.UUID.generate(), client_secret: "aaa;/&bbb" @@ -310,19 +310,37 @@ def instance_factory do } end - def oauth_token_factory do - oauth_app = insert(:oauth_app) + def oauth_token_factory(attrs \\ %{}) do + scopes = Map.get(attrs, :scopes, ["read"]) + oauth_app = Map.get_lazy(attrs, :app, fn -> insert(:oauth_app, scopes: scopes) end) + user = Map.get_lazy(attrs, :user, fn -> build(:user) end) + + valid_until = + Map.get(attrs, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10)) %Pleroma.Web.OAuth.Token{ token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(), - scopes: ["read"], refresh_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(), - user: build(:user), - app_id: oauth_app.id, - valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10) + scopes: scopes, + user: user, + app: oauth_app, + valid_until: valid_until } end + def oauth_admin_token_factory(attrs \\ %{}) do + user = Map.get_lazy(attrs, :user, fn -> build(:user, is_admin: true) end) + + scopes = + attrs + |> Map.get(:scopes, ["admin"]) + |> Kernel.++(["admin"]) + |> Enum.uniq() + + attrs = Map.merge(attrs, %{user: user, scopes: scopes}) + oauth_token_factory(attrs) + end + def oauth_authorization_factory do %Pleroma.Web.OAuth.Authorization{ token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false), diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index e3a621f49..f43de700d 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -308,6 +308,24 @@ def get("https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" }} end + def get("https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39", _, _, + Accept: "application/activity+json" + ) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json") + }} + end + + def get("https://mobilizon.org/@tcit", _, _, Accept: "application/activity+json") do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json") + }} + end + def get("https://baptiste.gelez.xyz/@/BaptisteGelez", _, _, _) do {:ok, %Tesla.Env{ diff --git a/test/uploaders/local_test.exs b/test/uploaders/local_test.exs index fc442d0f1..1963dac23 100644 --- a/test/uploaders/local_test.exs +++ b/test/uploaders/local_test.exs @@ -29,4 +29,25 @@ test "put file to local folder" do |> File.exists?() end end + + describe "delete_file/1" do + test "deletes local file" do + file_path = "local_upload/files/image.jpg" + + file = %Pleroma.Upload{ + name: "image.jpg", + content_type: "image/jpg", + path: file_path, + tempfile: Path.absname("test/fixtures/image_tmp.jpg") + } + + :ok = Local.put_file(file) + local_path = Path.join([Local.upload_path(), file_path]) + assert File.exists?(local_path) + + Local.delete_file(file_path) + + refute File.exists?(local_path) + end + end end diff --git a/test/uploaders/mdii_test.exs b/test/uploaders/mdii_test.exs deleted file mode 100644 index d432d40f0..000000000 --- a/test/uploaders/mdii_test.exs +++ /dev/null @@ -1,50 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Uploaders.MDIITest do - use Pleroma.DataCase - alias Pleroma.Uploaders.MDII - import Tesla.Mock - - describe "get_file/1" do - test "it returns path to local folder for files" do - assert MDII.get_file("") == {:ok, {:static_dir, "test/uploads"}} - end - end - - describe "put_file/1" do - setup do - file_upload = %Pleroma.Upload{ - name: "mdii-image.jpg", - content_type: "image/jpg", - path: "test_folder/mdii-image.jpg", - tempfile: Path.absname("test/fixtures/image_tmp.jpg") - } - - [file_upload: file_upload] - end - - test "save file", %{file_upload: file_upload} do - mock(fn - %{method: :post, url: "https://mdii.sakura.ne.jp/mdii-post.cgi?jpg"} -> - %Tesla.Env{status: 200, body: "mdii-image"} - end) - - assert MDII.put_file(file_upload) == - {:ok, {:url, "https://mdii.sakura.ne.jp/mdii-image.jpg"}} - end - - test "save file to local if MDII isn`t available", %{file_upload: file_upload} do - mock(fn - %{method: :post, url: "https://mdii.sakura.ne.jp/mdii-post.cgi?jpg"} -> - %Tesla.Env{status: 500} - end) - - assert MDII.put_file(file_upload) == :ok - - assert Path.join([Pleroma.Uploaders.Local.upload_path(), file_upload.path]) - |> File.exists?() - end - end -end diff --git a/test/uploaders/s3_test.exs b/test/uploaders/s3_test.exs index 171316340..ab7795c3b 100644 --- a/test/uploaders/s3_test.exs +++ b/test/uploaders/s3_test.exs @@ -79,4 +79,11 @@ test "returns error", %{file_upload: file_upload} do end end end + + describe "delete_file/1" do + test_with_mock "deletes file", ExAws, request: fn _req -> {:ok, %{status_code: 204}} end do + assert :ok = S3.delete_file("image.jpg") + assert_called(ExAws.request(:_)) + end + end end diff --git a/test/user_test.exs b/test/user_test.exs index 6be2281ac..190b03645 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -17,6 +17,7 @@ defmodule Pleroma.UserTest do import Mock import Pleroma.Factory + import ExUnit.CaptureLog setup_all do Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) @@ -26,6 +27,42 @@ defmodule Pleroma.UserTest do clear_config([:instance, :account_activation_required]) describe "service actors" do + test "returns updated invisible actor" do + uri = "#{Pleroma.Web.Endpoint.url()}/relay" + followers_uri = "#{uri}/followers" + + insert( + :user, + %{ + nickname: "relay", + invisible: false, + local: true, + ap_id: uri, + follower_address: followers_uri + } + ) + + actor = User.get_or_create_service_actor_by_ap_id(uri, "relay") + assert actor.invisible + end + + test "returns relay user" do + uri = "#{Pleroma.Web.Endpoint.url()}/relay" + followers_uri = "#{uri}/followers" + + assert %User{ + nickname: "relay", + invisible: true, + local: true, + ap_id: ^uri, + follower_address: ^followers_uri + } = User.get_or_create_service_actor_by_ap_id(uri, "relay") + + assert capture_log(fn -> + refute User.get_or_create_service_actor_by_ap_id("/relay", "relay") + end) =~ "Cannot create service actor:" + end + test "returns invisible actor" do uri = "#{Pleroma.Web.Endpoint.url()}/internal/fetch-test" followers_uri = "#{uri}/followers" diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 1520c8a9b..ad6b9810c 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -1623,6 +1623,44 @@ test "detects hidden follows/followers for friendica" do assert follow_info.following_count == 32 assert follow_info.hide_follows == true end + + test "doesn't crash when follower and following counters are hidden" do + mock(fn env -> + case env.url do + "http://localhost:4001/users/masto_hidden_counters/following" -> + json(%{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "http://localhost:4001/users/masto_hidden_counters/followers" + }) + + "http://localhost:4001/users/masto_hidden_counters/following?page=1" -> + %Tesla.Env{status: 403, body: ""} + + "http://localhost:4001/users/masto_hidden_counters/followers" -> + json(%{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "http://localhost:4001/users/masto_hidden_counters/following" + }) + + "http://localhost:4001/users/masto_hidden_counters/followers?page=1" -> + %Tesla.Env{status: 403, body: ""} + end + end) + + user = + insert(:user, + local: false, + follower_address: "http://localhost:4001/users/masto_hidden_counters/followers", + following_address: "http://localhost:4001/users/masto_hidden_counters/following" + ) + + {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user) + + assert follow_info.hide_followers == true + assert follow_info.follower_count == 0 + assert follow_info.hide_follows == true + assert follow_info.following_count == 0 + end end describe "fetch_favourites/3" do diff --git a/test/web/activity_pub/publisher_test.exs b/test/web/activity_pub/publisher_test.exs index e885e5a5a..015af19ab 100644 --- a/test/web/activity_pub/publisher_test.exs +++ b/test/web/activity_pub/publisher_test.exs @@ -23,6 +23,27 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do :ok end + describe "gather_webfinger_links/1" do + test "it returns links" do + user = insert(:user) + + expected_links = [ + %{"href" => user.ap_id, "rel" => "self", "type" => "application/activity+json"}, + %{ + "href" => user.ap_id, + "rel" => "self", + "type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" + }, + %{ + "rel" => "http://ostatus.org/schema/1.0/subscribe", + "template" => "#{Pleroma.Web.base_url()}/ostatus_subscribe?acct={uri}" + } + ] + + assert expected_links == Publisher.gather_webfinger_links(user) + end + end + describe "determine_inbox/2" do test "it returns sharedInbox for messages involving as:Public in to" do user = diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs index 3299be2d5..8374b8d23 100644 --- a/test/web/activity_pub/views/user_view_test.exs +++ b/test/web/activity_pub/views/user_view_test.exs @@ -126,7 +126,7 @@ test "sets totalItems to zero when followers are hidden" do {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user}) user = Map.merge(user, %{hide_followers_count: true, hide_followers: true}) - assert %{"totalItems" => 0} = UserView.render("followers.json", %{user: user}) + refute UserView.render("followers.json", %{user: user}) |> Map.has_key?("totalItems") end test "sets correct totalItems when followers are hidden but the follower counter is not" do diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 49ff005b6..a3fbb6041 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -26,8 +26,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do :ok end - clear_config([:auth, :enforce_oauth_admin_scope_usage]) do - Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false) + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} end describe "with [:auth, :enforce_oauth_admin_scope_usage]," do @@ -35,9 +43,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], true) end - test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope" do + test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope", + %{admin: admin} do user = insert(:user) - admin = insert(:user, is_admin: true) url = "/api/pleroma/admin/users/#{user.nickname}" good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"]) @@ -80,14 +88,67 @@ test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or bro end end + describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do + clear_config([:auth, :enforce_oauth_admin_scope_usage]) do + Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false) + end + + test "GET /api/pleroma/admin/users/:nickname requires " <> + "read:accounts or admin:read:accounts or broader scope", + %{admin: admin} do + user = insert(:user) + url = "/api/pleroma/admin/users/#{user.nickname}" + + good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"]) + good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"]) + good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"]) + good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"]) + good_token5 = insert(:oauth_token, user: admin, scopes: ["read"]) + + good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5] + + bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"]) + bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"]) + bad_token3 = nil + + for good_token <- good_tokens do + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, good_token) + |> get(url) + + assert json_response(conn, 200) + end + + for good_token <- good_tokens do + conn = + build_conn() + |> assign(:user, nil) + |> assign(:token, good_token) + |> get(url) + + assert json_response(conn, :forbidden) + end + + for bad_token <- [bad_token1, bad_token2, bad_token3] do + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, bad_token) + |> get(url) + + assert json_response(conn, :forbidden) + end + end + end + describe "DELETE /api/pleroma/admin/users" do - test "single user" do - admin = insert(:user, is_admin: true) + test "single user", %{admin: admin, conn: conn} do user = insert(:user) conn = - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}") @@ -99,14 +160,12 @@ test "single user" do assert json_response(conn, 200) == user.nickname end - test "multiple users" do - admin = insert(:user, is_admin: true) + test "multiple users", %{admin: admin, conn: conn} do user_one = insert(:user) user_two = insert(:user) conn = - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> delete("/api/pleroma/admin/users", %{ nicknames: [user_one.nickname, user_two.nickname] @@ -123,12 +182,9 @@ test "multiple users" do end describe "/api/pleroma/admin/users" do - test "Create" do - admin = insert(:user, is_admin: true) - + test "Create", %{conn: conn} do conn = - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> post("/api/pleroma/admin/users", %{ "users" => [ @@ -153,13 +209,11 @@ test "Create" do assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == [] end - test "Cannot create user with existing email" do - admin = insert(:user, is_admin: true) + test "Cannot create user with existing email", %{conn: conn} do user = insert(:user) conn = - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> post("/api/pleroma/admin/users", %{ "users" => [ @@ -184,13 +238,11 @@ test "Cannot create user with existing email" do ] end - test "Cannot create user with existing nickname" do - admin = insert(:user, is_admin: true) + test "Cannot create user with existing nickname", %{conn: conn} do user = insert(:user) conn = - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> post("/api/pleroma/admin/users", %{ "users" => [ @@ -215,13 +267,11 @@ test "Cannot create user with existing nickname" do ] end - test "Multiple user creation works in transaction" do - admin = insert(:user, is_admin: true) + test "Multiple user creation works in transaction", %{conn: conn} do user = insert(:user) conn = - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> post("/api/pleroma/admin/users", %{ "users" => [ @@ -265,13 +315,9 @@ test "Multiple user creation works in transaction" do describe "/api/pleroma/admin/users/:nickname" do test "Show", %{conn: conn} do - admin = insert(:user, is_admin: true) user = insert(:user) - conn = - conn - |> assign(:user, admin) - |> get("/api/pleroma/admin/users/#{user.nickname}") + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") expected = %{ "deactivated" => false, @@ -289,26 +335,20 @@ test "Show", %{conn: conn} do end test "when the user doesn't exist", %{conn: conn} do - admin = insert(:user, is_admin: true) user = build(:user) - conn = - conn - |> assign(:user, admin) - |> get("/api/pleroma/admin/users/#{user.nickname}") + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") assert "Not found" == json_response(conn, 404) end end describe "/api/pleroma/admin/users/follow" do - test "allows to force-follow another user" do - admin = insert(:user, is_admin: true) + test "allows to force-follow another user", %{admin: admin, conn: conn} do user = insert(:user) follower = insert(:user) - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> post("/api/pleroma/admin/users/follow", %{ "follower" => follower.nickname, @@ -328,15 +368,13 @@ test "allows to force-follow another user" do end describe "/api/pleroma/admin/users/unfollow" do - test "allows to force-unfollow another user" do - admin = insert(:user, is_admin: true) + test "allows to force-unfollow another user", %{admin: admin, conn: conn} do user = insert(:user) follower = insert(:user) User.follow(follower, user) - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> post("/api/pleroma/admin/users/unfollow", %{ "follower" => follower.nickname, @@ -356,23 +394,20 @@ test "allows to force-unfollow another user" do end describe "PUT /api/pleroma/admin/users/tag" do - setup do - admin = insert(:user, is_admin: true) + setup %{conn: conn} do user1 = insert(:user, %{tags: ["x"]}) user2 = insert(:user, %{tags: ["y"]}) user3 = insert(:user, %{tags: ["unchanged"]}) conn = - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> put( - "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=#{ - user2.nickname - }&tags[]=foo&tags[]=bar" + "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <> + "#{user2.nickname}&tags[]=foo&tags[]=bar" ) - %{conn: conn, admin: admin, user1: user1, user2: user2, user3: user3} + %{conn: conn, user1: user1, user2: user2, user3: user3} end test "it appends specified tags to users with specified nicknames", %{ @@ -405,23 +440,20 @@ test "it does not modify tags of not specified users", %{conn: conn, user3: user end describe "DELETE /api/pleroma/admin/users/tag" do - setup do - admin = insert(:user, is_admin: true) + setup %{conn: conn} do user1 = insert(:user, %{tags: ["x"]}) user2 = insert(:user, %{tags: ["y", "z"]}) user3 = insert(:user, %{tags: ["unchanged"]}) conn = - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> delete( - "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=#{ - user2.nickname - }&tags[]=x&tags[]=z" + "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <> + "#{user2.nickname}&tags[]=x&tags[]=z" ) - %{conn: conn, admin: admin, user1: user1, user2: user2, user3: user3} + %{conn: conn, user1: user1, user2: user2, user3: user3} end test "it removes specified tags from users with specified nicknames", %{ @@ -454,12 +486,9 @@ test "it does not modify tags of not specified users", %{conn: conn, user3: user end describe "/api/pleroma/admin/users/:nickname/permission_group" do - test "GET is giving user_info" do - admin = insert(:user, is_admin: true) - + test "GET is giving user_info", %{admin: admin, conn: conn} do conn = - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/") @@ -469,13 +498,11 @@ test "GET is giving user_info" do } end - test "/:right POST, can add to a permission group" do - admin = insert(:user, is_admin: true) + test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do user = insert(:user) conn = - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin") @@ -489,22 +516,18 @@ test "/:right POST, can add to a permission group" do "@#{admin.nickname} made @#{user.nickname} admin" end - test "/:right POST, can add to a permission group (multiple)" do - admin = insert(:user, is_admin: true) + test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do user_one = insert(:user) user_two = insert(:user) conn = - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> post("/api/pleroma/admin/users/permission_group/admin", %{ nicknames: [user_one.nickname, user_two.nickname] }) - assert json_response(conn, 200) == %{ - "is_admin" => true - } + assert json_response(conn, 200) == %{"is_admin" => true} log_entry = Repo.one(ModerationLog) @@ -512,19 +535,15 @@ test "/:right POST, can add to a permission group (multiple)" do "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin" end - test "/:right DELETE, can remove from a permission group" do - admin = insert(:user, is_admin: true) + test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do user = insert(:user, is_admin: true) conn = - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin") - assert json_response(conn, 200) == %{ - "is_admin" => false - } + assert json_response(conn, 200) == %{"is_admin" => false} log_entry = Repo.one(ModerationLog) @@ -532,22 +551,21 @@ test "/:right DELETE, can remove from a permission group" do "@#{admin.nickname} revoked admin role from @#{user.nickname}" end - test "/:right DELETE, can remove from a permission group (multiple)" do - admin = insert(:user, is_admin: true) + test "/:right DELETE, can remove from a permission group (multiple)", %{ + admin: admin, + conn: conn + } do user_one = insert(:user, is_admin: true) user_two = insert(:user, is_admin: true) conn = - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> delete("/api/pleroma/admin/users/permission_group/admin", %{ nicknames: [user_one.nickname, user_two.nickname] }) - assert json_response(conn, 200) == %{ - "is_admin" => false - } + assert json_response(conn, 200) == %{"is_admin" => false} log_entry = Repo.one(ModerationLog) @@ -559,10 +577,6 @@ test "/:right DELETE, can remove from a permission group (multiple)" do end describe "POST /api/pleroma/admin/email_invite, with valid config" do - setup do - [user: insert(:user, is_admin: true)] - end - clear_config([:instance, :registrations_open]) do Pleroma.Config.put([:instance, :registrations_open], false) end @@ -571,14 +585,13 @@ test "/:right DELETE, can remove from a permission group (multiple)" do Pleroma.Config.put([:instance, :invites_enabled], true) end - test "sends invitation and returns 204", %{conn: conn, user: user} do + test "sends invitation and returns 204", %{admin: admin, conn: conn} do recipient_email = "foo@bar.com" recipient_name = "J. D." conn = - conn - |> assign(:user, user) - |> post( + post( + conn, "/api/pleroma/admin/users/email_invite?email=#{recipient_email}&name=#{recipient_name}" ) @@ -593,7 +606,7 @@ test "sends invitation and returns 204", %{conn: conn, user: user} do email = Pleroma.Emails.UserEmail.user_invitation_email( - user, + admin, token_record, recipient_email, recipient_name @@ -606,12 +619,14 @@ test "sends invitation and returns 204", %{conn: conn, user: user} do ) end - test "it returns 403 if requested by a non-admin", %{conn: conn} do + test "it returns 403 if requested by a non-admin" do non_admin_user = insert(:user) + token = insert(:oauth_token, user: non_admin_user) conn = - conn + build_conn() |> assign(:user, non_admin_user) + |> assign(:token, token) |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") assert json_response(conn, :forbidden) @@ -619,45 +634,33 @@ test "it returns 403 if requested by a non-admin", %{conn: conn} do end describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do - setup do - [user: insert(:user, is_admin: true)] - end - clear_config([:instance, :registrations_open]) clear_config([:instance, :invites_enabled]) - test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn, user: user} do + test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do Pleroma.Config.put([:instance, :registrations_open], false) Pleroma.Config.put([:instance, :invites_enabled], false) - conn = - conn - |> assign(:user, user) - |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") + conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") assert json_response(conn, :internal_server_error) end - test "it returns 500 if `registrations_open` is enabled", %{conn: conn, user: user} do + test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do Pleroma.Config.put([:instance, :registrations_open], true) Pleroma.Config.put([:instance, :invites_enabled], true) - conn = - conn - |> assign(:user, user) - |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") + conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") assert json_response(conn, :internal_server_error) end end - test "/api/pleroma/admin/users/:nickname/password_reset" do - admin = insert(:user, is_admin: true) + test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do user = insert(:user) conn = - build_conn() - |> assign(:user, admin) + conn |> put_req_header("accept", "application/json") |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset") @@ -667,16 +670,6 @@ test "/api/pleroma/admin/users/:nickname/password_reset" do end describe "GET /api/pleroma/admin/users" do - setup do - admin = insert(:user, is_admin: true) - - conn = - build_conn() - |> assign(:user, admin) - - {:ok, conn: conn, admin: admin} - end - test "renders users array for the first page", %{conn: conn, admin: admin} do user = insert(:user, local: false, tags: ["foo", "bar"]) conn = get(conn, "/api/pleroma/admin/users?page=1") @@ -898,6 +891,7 @@ test "regular search with page size", %{conn: conn} do test "only local users" do admin = insert(:user, is_admin: true, nickname: "john") + token = insert(:oauth_admin_token, user: admin) user = insert(:user, nickname: "bob") insert(:user, nickname: "bobb", local: false) @@ -905,6 +899,7 @@ test "only local users" do conn = build_conn() |> assign(:user, admin) + |> assign(:token, token) |> get("/api/pleroma/admin/users?query=bo&filters=local") assert json_response(conn, 200) == %{ @@ -926,16 +921,13 @@ test "only local users" do } end - test "only local users with no query", %{admin: old_admin} do + test "only local users with no query", %{conn: conn, admin: old_admin} do admin = insert(:user, is_admin: true, nickname: "john") user = insert(:user, nickname: "bob") insert(:user, nickname: "bobb", local: false) - conn = - build_conn() - |> assign(:user, admin) - |> get("/api/pleroma/admin/users?filters=local") + conn = get(conn, "/api/pleroma/admin/users?filters=local") users = [ @@ -1093,6 +1085,7 @@ test "load users with tags list", %{conn: conn} do test "it works with multiple filters" do admin = insert(:user, nickname: "john", is_admin: true) + token = insert(:oauth_admin_token, user: admin) user = insert(:user, nickname: "bob", local: false, deactivated: true) insert(:user, nickname: "ken", local: true, deactivated: true) @@ -1101,6 +1094,7 @@ test "it works with multiple filters" do conn = build_conn() |> assign(:user, admin) + |> assign(:token, token) |> get("/api/pleroma/admin/users?filters=deactivated,external") assert json_response(conn, 200) == %{ @@ -1122,13 +1116,10 @@ test "it works with multiple filters" do } end - test "it omits relay user", %{admin: admin} do + test "it omits relay user", %{admin: admin, conn: conn} do assert %User{} = Relay.get_actor() - conn = - build_conn() - |> assign(:user, admin) - |> get("/api/pleroma/admin/users") + conn = get(conn, "/api/pleroma/admin/users") assert json_response(conn, 200) == %{ "count" => 1, @@ -1150,15 +1141,13 @@ test "it omits relay user", %{admin: admin} do end end - test "PATCH /api/pleroma/admin/users/activate" do - admin = insert(:user, is_admin: true) + test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do user_one = insert(:user, deactivated: true) user_two = insert(:user, deactivated: true) conn = - build_conn() - |> assign(:user, admin) - |> patch( + patch( + conn, "/api/pleroma/admin/users/activate", %{nicknames: [user_one.nickname, user_two.nickname]} ) @@ -1172,15 +1161,13 @@ test "PATCH /api/pleroma/admin/users/activate" do "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}" end - test "PATCH /api/pleroma/admin/users/deactivate" do - admin = insert(:user, is_admin: true) + test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do user_one = insert(:user, deactivated: false) user_two = insert(:user, deactivated: false) conn = - build_conn() - |> assign(:user, admin) - |> patch( + patch( + conn, "/api/pleroma/admin/users/deactivate", %{nicknames: [user_one.nickname, user_two.nickname]} ) @@ -1194,14 +1181,10 @@ test "PATCH /api/pleroma/admin/users/deactivate" do "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}" end - test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do - admin = insert(:user, is_admin: true) + test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do user = insert(:user) - conn = - build_conn() - |> assign(:user, admin) - |> patch("/api/pleroma/admin/users/#{user.nickname}/toggle_activation") + conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation") assert json_response(conn, 200) == %{ @@ -1223,16 +1206,6 @@ test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do end describe "POST /api/pleroma/admin/users/invite_token" do - setup do - admin = insert(:user, is_admin: true) - - conn = - build_conn() - |> assign(:user, admin) - - {:ok, conn: conn} - end - test "without options", %{conn: conn} do conn = post(conn, "/api/pleroma/admin/users/invite_token") @@ -1287,16 +1260,6 @@ test "with max use and expires_at", %{conn: conn} do end describe "GET /api/pleroma/admin/users/invites" do - setup do - admin = insert(:user, is_admin: true) - - conn = - build_conn() - |> assign(:user, admin) - - {:ok, conn: conn} - end - test "no invites", %{conn: conn} do conn = get(conn, "/api/pleroma/admin/users/invites") @@ -1325,14 +1288,10 @@ test "with invite", %{conn: conn} do end describe "POST /api/pleroma/admin/users/revoke_invite" do - test "with token" do - admin = insert(:user, is_admin: true) + test "with token", %{conn: conn} do {:ok, invite} = UserInviteToken.create_invite() - conn = - build_conn() - |> assign(:user, admin) - |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token}) + conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token}) assert json_response(conn, 200) == %{ "expires_at" => nil, @@ -1345,25 +1304,14 @@ test "with token" do } end - test "with invalid token" do - admin = insert(:user, is_admin: true) - - conn = - build_conn() - |> assign(:user, admin) - |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"}) + test "with invalid token", %{conn: conn} do + conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"}) assert json_response(conn, :not_found) == "Not found" end end describe "GET /api/pleroma/admin/reports/:id" do - setup %{conn: conn} do - admin = insert(:user, is_admin: true) - - %{conn: assign(conn, :user, admin)} - end - test "returns report by its id", %{conn: conn} do [reporter, target_user] = insert_pair(:user) activity = insert(:note_activity, user: target_user) @@ -1391,8 +1339,7 @@ test "returns 404 when report id is invalid", %{conn: conn} do end describe "PATCH /api/pleroma/admin/reports" do - setup %{conn: conn} do - admin = insert(:user, is_admin: true) + setup do [reporter, target_user] = insert_pair(:user) activity = insert(:note_activity, user: target_user) @@ -1411,9 +1358,7 @@ test "returns 404 when report id is invalid", %{conn: conn} do }) %{ - conn: assign(conn, :user, admin), id: report_id, - admin: admin, second_report_id: second_report_id } end @@ -1509,12 +1454,6 @@ test "updates state of multiple reports", %{ end describe "GET /api/pleroma/admin/reports" do - setup %{conn: conn} do - admin = insert(:user, is_admin: true) - - %{conn: assign(conn, :user, admin)} - end - test "returns empty response when no reports created", %{conn: conn} do response = conn @@ -1609,10 +1548,12 @@ test "returns reports with specified state", %{conn: conn} do test "returns 403 when requested by a non-admin" do user = insert(:user) + token = insert(:oauth_token, user: user) conn = build_conn() |> assign(:user, user) + |> assign(:token, token) |> get("/api/pleroma/admin/reports") assert json_response(conn, :forbidden) == @@ -1620,17 +1561,14 @@ test "returns 403 when requested by a non-admin" do end test "returns 403 when requested by anonymous" do - conn = - build_conn() - |> get("/api/pleroma/admin/reports") + conn = get(build_conn(), "/api/pleroma/admin/reports") assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."} end end describe "GET /api/pleroma/admin/grouped_reports" do - setup %{conn: conn} do - admin = insert(:user, is_admin: true) + setup do [reporter, target_user] = insert_pair(:user) date1 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!() @@ -1665,7 +1603,6 @@ test "returns 403 when requested by anonymous" do }) %{ - conn: assign(conn, :user, admin), first_status: Activity.get_by_ap_id_with_object(first_status.data["id"]), second_status: Activity.get_by_ap_id_with_object(second_status.data["id"]), third_status: Activity.get_by_ap_id_with_object(third_status.data["id"]), @@ -1833,11 +1770,10 @@ test "account not empty if status was deleted", %{ end describe "PUT /api/pleroma/admin/statuses/:id" do - setup %{conn: conn} do - admin = insert(:user, is_admin: true) + setup do activity = insert(:note_activity) - %{conn: assign(conn, :user, admin), id: activity.id, admin: admin} + %{id: activity.id} end test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do @@ -1890,20 +1826,17 @@ test "change visibility flag", %{conn: conn, id: id, admin: admin} do end test "returns 400 when visibility is unknown", %{conn: conn, id: id} do - conn = - conn - |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "test"}) + conn = put(conn, "/api/pleroma/admin/statuses/#{id}", %{"visibility" => "test"}) assert json_response(conn, :bad_request) == "Unsupported visibility" end end describe "DELETE /api/pleroma/admin/statuses/:id" do - setup %{conn: conn} do - admin = insert(:user, is_admin: true) + setup do activity = insert(:note_activity) - %{conn: assign(conn, :user, admin), id: activity.id, admin: admin} + %{id: activity.id} end test "deletes status", %{conn: conn, id: id, admin: admin} do @@ -1920,21 +1853,13 @@ test "deletes status", %{conn: conn, id: id, admin: admin} do end test "returns error when status is not exist", %{conn: conn} do - conn = - conn - |> delete("/api/pleroma/admin/statuses/test") + conn = delete(conn, "/api/pleroma/admin/statuses/test") assert json_response(conn, :bad_request) == "Could not delete" end end describe "GET /api/pleroma/admin/config" do - setup %{conn: conn} do - admin = insert(:user, is_admin: true) - - %{conn: assign(conn, :user, admin)} - end - test "without any settings in db", %{conn: conn} do conn = get(conn, "/api/pleroma/admin/config") @@ -1966,9 +1891,7 @@ test "with settings in db", %{conn: conn} do end describe "POST /api/pleroma/admin/config" do - setup %{conn: conn} do - admin = insert(:user, is_admin: true) - + setup do temp_file = "config/test.exported_from_db.secret.exs" on_exit(fn -> @@ -1982,8 +1905,6 @@ test "with settings in db", %{conn: conn} do Application.delete_env(:pleroma, Pleroma.Captcha.NotReal) :ok = File.rm(temp_file) end) - - %{conn: assign(conn, :user, admin)} end clear_config([:instance, :dynamic_configuration]) do @@ -2535,9 +2456,7 @@ test "delete part of settings by atom subkeys", %{conn: conn} do end describe "config mix tasks run" do - setup %{conn: conn} do - admin = insert(:user, is_admin: true) - + setup do temp_file = "config/test.exported_from_db.secret.exs" Mix.shell(Mix.Shell.Quiet) @@ -2547,7 +2466,7 @@ test "delete part of settings by atom subkeys", %{conn: conn} do :ok = File.rm(temp_file) end) - %{conn: assign(conn, :user, admin), admin: admin} + :ok end clear_config([:instance, :dynamic_configuration]) do @@ -2558,25 +2477,21 @@ test "delete part of settings by atom subkeys", %{conn: conn} do Pleroma.Config.put([:feed, :post_title], %{max_length: 100, omission: "…"}) end - test "transfer settings to DB and to file", %{conn: conn, admin: admin} do + test "transfer settings to DB and to file", %{conn: conn} do assert Pleroma.Repo.all(Pleroma.Web.AdminAPI.Config) == [] - conn = get(conn, "/api/pleroma/admin/config/migrate_to_db") - assert json_response(conn, 200) == %{} + ret_conn = get(conn, "/api/pleroma/admin/config/migrate_to_db") + assert json_response(ret_conn, 200) == %{} assert Pleroma.Repo.all(Pleroma.Web.AdminAPI.Config) > 0 - conn = - build_conn() - |> assign(:user, admin) - |> get("/api/pleroma/admin/config/migrate_from_db") + ret_conn = get(conn, "/api/pleroma/admin/config/migrate_from_db") - assert json_response(conn, 200) == %{} + assert json_response(ret_conn, 200) == %{} assert Pleroma.Repo.all(Pleroma.Web.AdminAPI.Config) == [] end end describe "GET /api/pleroma/admin/users/:nickname/statuses" do setup do - admin = insert(:user, is_admin: true) user = insert(:user) date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!() @@ -2587,11 +2502,7 @@ test "transfer settings to DB and to file", %{conn: conn, admin: admin} do insert(:note_activity, user: user, published: date2) insert(:note_activity, user: user, published: date3) - conn = - build_conn() - |> assign(:user, admin) - - {:ok, conn: conn, user: user} + %{user: user} end test "renders user's statuses", %{conn: conn, user: user} do @@ -2632,11 +2543,10 @@ test "returns private statuses with godmode on", %{conn: conn, user: user} do end describe "GET /api/pleroma/admin/moderation_log" do - setup %{conn: conn} do - admin = insert(:user, is_admin: true) + setup do moderator = insert(:user, is_moderator: true) - %{conn: assign(conn, :user, admin), admin: admin, moderator: moderator} + %{moderator: moderator} end test "returns the log", %{conn: conn, admin: admin} do @@ -2841,20 +2751,12 @@ test "returns log filtered by search", %{conn: conn, moderator: moderator} do end describe "PATCH /users/:nickname/force_password_reset" do - setup %{conn: conn} do - admin = insert(:user, is_admin: true) + test "sets password_reset_pending to true", %{conn: conn} do user = insert(:user) - - %{conn: assign(conn, :user, admin), admin: admin, user: user} - end - - test "sets password_reset_pending to true", %{admin: admin, user: user} do assert user.password_reset_pending == false conn = - build_conn() - |> assign(:user, admin) - |> patch("/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]}) + patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]}) assert json_response(conn, 204) == "" @@ -2865,17 +2767,9 @@ test "sets password_reset_pending to true", %{admin: admin, user: user} do end describe "relays" do - setup %{conn: conn} do - admin = insert(:user, is_admin: true) - - %{conn: assign(conn, :user, admin), admin: admin} - end - - test "POST /relay", %{admin: admin} do + test "POST /relay", %{conn: conn, admin: admin} do conn = - build_conn() - |> assign(:user, admin) - |> post("/api/pleroma/admin/relay", %{ + post(conn, "/api/pleroma/admin/relay", %{ relay_url: "http://mastodon.example.org/users/admin" }) @@ -2887,7 +2781,7 @@ test "POST /relay", %{admin: admin} do "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin" end - test "GET /relay", %{admin: admin} do + test "GET /relay", %{conn: conn} do relay_user = Pleroma.Web.ActivityPub.Relay.get_actor() ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"] @@ -2896,25 +2790,18 @@ test "GET /relay", %{admin: admin} do User.follow(relay_user, user) end) - conn = - build_conn() - |> assign(:user, admin) - |> get("/api/pleroma/admin/relay") + conn = get(conn, "/api/pleroma/admin/relay") assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == [] end - test "DELETE /relay", %{admin: admin} do - build_conn() - |> assign(:user, admin) - |> post("/api/pleroma/admin/relay", %{ + test "DELETE /relay", %{conn: conn, admin: admin} do + post(conn, "/api/pleroma/admin/relay", %{ relay_url: "http://mastodon.example.org/users/admin" }) conn = - build_conn() - |> assign(:user, admin) - |> delete("/api/pleroma/admin/relay", %{ + delete(conn, "/api/pleroma/admin/relay", %{ relay_url: "http://mastodon.example.org/users/admin" }) @@ -2931,63 +2818,48 @@ test "DELETE /relay", %{admin: admin} do end describe "instances" do - test "GET /instances/:instance/statuses" do - admin = insert(:user, is_admin: true) + test "GET /instances/:instance/statuses", %{conn: conn} do user = insert(:user, local: false, nickname: "archaeme@archae.me") user2 = insert(:user, local: false, nickname: "test@test.com") insert_pair(:note_activity, user: user) insert(:note_activity, user: user2) - conn = - build_conn() - |> assign(:user, admin) - |> get("/api/pleroma/admin/instances/archae.me/statuses") + ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses") - response = json_response(conn, 200) + response = json_response(ret_conn, 200) assert length(response) == 2 - conn = - build_conn() - |> assign(:user, admin) - |> get("/api/pleroma/admin/instances/test.com/statuses") + ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses") - response = json_response(conn, 200) + response = json_response(ret_conn, 200) assert length(response) == 1 - conn = - build_conn() - |> assign(:user, admin) - |> get("/api/pleroma/admin/instances/nonexistent.com/statuses") + ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses") - response = json_response(conn, 200) + response = json_response(ret_conn, 200) assert length(response) == 0 end end describe "PATCH /confirm_email" do - setup %{conn: conn} do - admin = insert(:user, is_admin: true) - - %{conn: assign(conn, :user, admin), admin: admin} - end - - test "it confirms emails of two users", %{admin: admin} do + test "it confirms emails of two users", %{conn: conn, admin: admin} do [first_user, second_user] = insert_pair(:user, confirmation_pending: true) assert first_user.confirmation_pending == true assert second_user.confirmation_pending == true - build_conn() - |> assign(:user, admin) - |> patch("/api/pleroma/admin/users/confirm_email", %{ - nicknames: [ - first_user.nickname, - second_user.nickname - ] - }) + ret_conn = + patch(conn, "/api/pleroma/admin/users/confirm_email", %{ + nicknames: [ + first_user.nickname, + second_user.nickname + ] + }) + + assert ret_conn.status == 200 assert first_user.confirmation_pending == true assert second_user.confirmation_pending == true @@ -3002,23 +2874,18 @@ test "it confirms emails of two users", %{admin: admin} do end describe "PATCH /resend_confirmation_email" do - setup %{conn: conn} do - admin = insert(:user, is_admin: true) - - %{conn: assign(conn, :user, admin), admin: admin} - end - - test "it resend emails for two users", %{admin: admin} do + test "it resend emails for two users", %{conn: conn, admin: admin} do [first_user, second_user] = insert_pair(:user, confirmation_pending: true) - build_conn() - |> assign(:user, admin) - |> patch("/api/pleroma/admin/users/resend_confirmation_email", %{ - nicknames: [ - first_user.nickname, - second_user.nickname - ] - }) + ret_conn = + patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{ + nicknames: [ + first_user.nickname, + second_user.nickname + ] + }) + + assert ret_conn.status == 200 log_entry = Repo.one(ModerationLog) @@ -3030,8 +2897,7 @@ test "it resend emails for two users", %{admin: admin} do end describe "POST /reports/:id/notes" do - setup do - admin = insert(:user, is_admin: true) + setup %{conn: conn, admin: admin} do [reporter, target_user] = insert_pair(:user) activity = insert(:note_activity, user: target_user) @@ -3042,22 +2908,17 @@ test "it resend emails for two users", %{admin: admin} do "status_ids" => [activity.id] }) - build_conn() - |> assign(:user, admin) - |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{ + post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{ content: "this is disgusting!" }) - build_conn() - |> assign(:user, admin) - |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{ + post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{ content: "this is disgusting2!" }) %{ admin_id: admin.id, - report_id: report_id, - admin: admin + report_id: report_id } end @@ -3071,11 +2932,8 @@ test "it creates report note", %{admin_id: admin_id, report_id: report_id} do } = note end - test "it returns reports with notes", %{admin: admin} do - conn = - build_conn() - |> assign(:user, admin) - |> get("/api/pleroma/admin/reports") + test "it returns reports with notes", %{conn: conn, admin: admin} do + conn = get(conn, "/api/pleroma/admin/reports") response = json_response(conn, 200) notes = hd(response["reports"])["notes"] @@ -3087,14 +2945,12 @@ test "it returns reports with notes", %{admin: admin} do assert response["total"] == 1 end - test "it deletes the note", %{admin: admin, report_id: report_id} do + test "it deletes the note", %{conn: conn, report_id: report_id} do assert ReportNote |> Repo.all() |> length() == 2 [note, _] = Repo.all(ReportNote) - build_conn() - |> assign(:user, admin) - |> delete("/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}") + delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}") assert ReportNote |> Repo.all() |> length() == 1 end diff --git a/test/web/feed/tag_controller_test.exs b/test/web/feed/tag_controller_test.exs index efc588070..a56a18738 100644 --- a/test/web/feed/tag_controller_test.exs +++ b/test/web/feed/tag_controller_test.exs @@ -85,5 +85,17 @@ test "gets a feed", %{conn: conn} do HtmlEntities.decode(FeedView.activity_content(obj2)), HtmlEntities.decode(FeedView.activity_content(obj1)) ] + + response = + conn + |> put_req_header("content-type", "application/atom+xml") + |> get(tag_feed_path(conn, :feed, "pleromaart")) + |> response(200) + + xml = parse(response) + assert xpath(xml, ~x"//channel/title/text()") == '#pleromaart' + + assert xpath(xml, ~x"//channel/description/text()"s) == + "These are public toots tagged with #pleromaart. You can interact with them if you have an account anywhere in the fediverse." end end diff --git a/test/web/masto_fe_controller_test.exs b/test/web/masto_fe_controller_test.exs index b5dbd4a25..f9870a852 100644 --- a/test/web/masto_fe_controller_test.exs +++ b/test/web/masto_fe_controller_test.exs @@ -18,6 +18,7 @@ test "put settings", %{conn: conn} do conn = conn |> assign(:user, user) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:accounts"])) |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}}) assert _result = json_response(conn, 200) @@ -63,12 +64,12 @@ test "redirects not logged-in users to the login page on private instances", %{ end test "does not redirect logged in users to the login page", %{conn: conn, path: path} do - token = insert(:oauth_token) + token = insert(:oauth_token, scopes: ["read"]) conn = conn |> assign(:user, token.user) - |> put_session(:oauth_token, token.token) + |> assign(:token, token) |> get(path) assert conn.status == 200 diff --git a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs index 77cfce4fa..09bdc46e0 100644 --- a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs +++ b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs @@ -12,13 +12,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do clear_config([:instance, :max_account_fields]) describe "updating credentials" do - test "sets user settings in a generic way", %{conn: conn} do - user = insert(:user) + setup do: oauth_access(["write:accounts"]) + test "sets user settings in a generic way", %{conn: conn} do res_conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{ + patch(conn, "/api/v1/accounts/update_credentials", %{ "pleroma_settings_store" => %{ pleroma_fe: %{ theme: "bla" @@ -26,10 +24,10 @@ test "sets user settings in a generic way", %{conn: conn} do } }) - assert user = json_response(res_conn, 200) - assert user["pleroma"]["settings_store"] == %{"pleroma_fe" => %{"theme" => "bla"}} + assert user_data = json_response(res_conn, 200) + assert user_data["pleroma"]["settings_store"] == %{"pleroma_fe" => %{"theme" => "bla"}} - user = Repo.get(User, user["id"]) + user = Repo.get(User, user_data["id"]) res_conn = conn @@ -42,15 +40,15 @@ test "sets user settings in a generic way", %{conn: conn} do } }) - assert user = json_response(res_conn, 200) + assert user_data = json_response(res_conn, 200) - assert user["pleroma"]["settings_store"] == + assert user_data["pleroma"]["settings_store"] == %{ "pleroma_fe" => %{"theme" => "bla"}, "masto_fe" => %{"theme" => "bla"} } - user = Repo.get(User, user["id"]) + user = Repo.get(User, user_data["id"]) res_conn = conn @@ -63,9 +61,9 @@ test "sets user settings in a generic way", %{conn: conn} do } }) - assert user = json_response(res_conn, 200) + assert user_data = json_response(res_conn, 200) - assert user["pleroma"]["settings_store"] == + assert user_data["pleroma"]["settings_store"] == %{ "pleroma_fe" => %{"theme" => "bla"}, "masto_fe" => %{"theme" => "blub"} @@ -73,97 +71,67 @@ test "sets user settings in a generic way", %{conn: conn} do end test "updates the user's bio", %{conn: conn} do - user = insert(:user) user2 = insert(:user) conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{ + patch(conn, "/api/v1/accounts/update_credentials", %{ "note" => "I drink #cofe with @#{user2.nickname}" }) - assert user = json_response(conn, 200) + assert user_data = json_response(conn, 200) - assert user["note"] == + assert user_data["note"] == ~s(I drink #cofe with @#{user2.nickname}) end test "updates the user's locking status", %{conn: conn} do - user = insert(:user) + conn = patch(conn, "/api/v1/accounts/update_credentials", %{locked: "true"}) - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{locked: "true"}) - - assert user = json_response(conn, 200) - assert user["locked"] == true + assert user_data = json_response(conn, 200) + assert user_data["locked"] == true end - test "updates the user's allow_following_move", %{conn: conn} do - user = insert(:user) - + test "updates the user's allow_following_move", %{user: user, conn: conn} do assert user.allow_following_move == true - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{allow_following_move: "false"}) + conn = patch(conn, "/api/v1/accounts/update_credentials", %{allow_following_move: "false"}) assert refresh_record(user).allow_following_move == false - assert user = json_response(conn, 200) - assert user["pleroma"]["allow_following_move"] == false + assert user_data = json_response(conn, 200) + assert user_data["pleroma"]["allow_following_move"] == false end test "updates the user's default scope", %{conn: conn} do - user = insert(:user) + conn = patch(conn, "/api/v1/accounts/update_credentials", %{default_scope: "cofe"}) - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"}) - - assert user = json_response(conn, 200) - assert user["source"]["privacy"] == "cofe" + assert user_data = json_response(conn, 200) + assert user_data["source"]["privacy"] == "cofe" end test "updates the user's hide_followers status", %{conn: conn} do - user = insert(:user) + conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_followers: "true"}) - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"}) - - assert user = json_response(conn, 200) - assert user["pleroma"]["hide_followers"] == true + assert user_data = json_response(conn, 200) + assert user_data["pleroma"]["hide_followers"] == true end test "updates the user's hide_followers_count and hide_follows_count", %{conn: conn} do - user = insert(:user) - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{ + patch(conn, "/api/v1/accounts/update_credentials", %{ hide_followers_count: "true", hide_follows_count: "true" }) - assert user = json_response(conn, 200) - assert user["pleroma"]["hide_followers_count"] == true - assert user["pleroma"]["hide_follows_count"] == true + assert user_data = json_response(conn, 200) + assert user_data["pleroma"]["hide_followers_count"] == true + assert user_data["pleroma"]["hide_follows_count"] == true end - test "updates the user's skip_thread_containment option", %{conn: conn} do - user = insert(:user) - + test "updates the user's skip_thread_containment option", %{user: user, conn: conn} do response = conn - |> assign(:user, user) |> patch("/api/v1/accounts/update_credentials", %{skip_thread_containment: "true"}) |> json_response(200) @@ -172,104 +140,68 @@ test "updates the user's skip_thread_containment option", %{conn: conn} do end test "updates the user's hide_follows status", %{conn: conn} do - user = insert(:user) + conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_follows: "true"}) - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"}) - - assert user = json_response(conn, 200) - assert user["pleroma"]["hide_follows"] == true + assert user_data = json_response(conn, 200) + assert user_data["pleroma"]["hide_follows"] == true end test "updates the user's hide_favorites status", %{conn: conn} do - user = insert(:user) + conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_favorites: "true"}) - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"}) - - assert user = json_response(conn, 200) - assert user["pleroma"]["hide_favorites"] == true + assert user_data = json_response(conn, 200) + assert user_data["pleroma"]["hide_favorites"] == true end test "updates the user's show_role status", %{conn: conn} do - user = insert(:user) + conn = patch(conn, "/api/v1/accounts/update_credentials", %{show_role: "false"}) - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{show_role: "false"}) - - assert user = json_response(conn, 200) - assert user["source"]["pleroma"]["show_role"] == false + assert user_data = json_response(conn, 200) + assert user_data["source"]["pleroma"]["show_role"] == false end test "updates the user's no_rich_text status", %{conn: conn} do - user = insert(:user) + conn = patch(conn, "/api/v1/accounts/update_credentials", %{no_rich_text: "true"}) - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"}) - - assert user = json_response(conn, 200) - assert user["source"]["pleroma"]["no_rich_text"] == true + assert user_data = json_response(conn, 200) + assert user_data["source"]["pleroma"]["no_rich_text"] == true end test "updates the user's name", %{conn: conn} do - user = insert(:user) - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"}) + patch(conn, "/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"}) - assert user = json_response(conn, 200) - assert user["display_name"] == "markorepairs" + assert user_data = json_response(conn, 200) + assert user_data["display_name"] == "markorepairs" end - test "updates the user's avatar", %{conn: conn} do - user = insert(:user) - + test "updates the user's avatar", %{user: user, conn: conn} do new_avatar = %Plug.Upload{ content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar}) + conn = patch(conn, "/api/v1/accounts/update_credentials", %{"avatar" => new_avatar}) assert user_response = json_response(conn, 200) assert user_response["avatar"] != User.avatar_url(user) end - test "updates the user's banner", %{conn: conn} do - user = insert(:user) - + test "updates the user's banner", %{user: user, conn: conn} do new_header = %Plug.Upload{ content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header}) + conn = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => new_header}) assert user_response = json_response(conn, 200) assert user_response["header"] != User.banner_url(user) end test "updates the user's background", %{conn: conn} do - user = insert(:user) - new_header = %Plug.Upload{ content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), @@ -277,9 +209,7 @@ test "updates the user's background", %{conn: conn} do } conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{ + patch(conn, "/api/v1/accounts/update_credentials", %{ "pleroma_background_image" => new_header }) @@ -287,13 +217,13 @@ test "updates the user's background", %{conn: conn} do assert user_response["pleroma"]["background_image"] end - test "requires 'write:accounts' permission", %{conn: conn} do + test "requires 'write:accounts' permission" do token1 = insert(:oauth_token, scopes: ["read"]) token2 = insert(:oauth_token, scopes: ["write", "follow"]) for token <- [token1, token2] do conn = - conn + build_conn() |> put_req_header("authorization", "Bearer #{token.token}") |> patch("/api/v1/accounts/update_credentials", %{}) @@ -306,53 +236,44 @@ test "requires 'write:accounts' permission", %{conn: conn} do end end - test "updates profile emojos", %{conn: conn} do - user = insert(:user) - + test "updates profile emojos", %{user: user, conn: conn} do note = "*sips :blank:*" name = "I am :firefox:" - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{ + ret_conn = + patch(conn, "/api/v1/accounts/update_credentials", %{ "note" => note, "display_name" => name }) - assert json_response(conn, 200) + assert json_response(ret_conn, 200) - conn = - conn - |> get("/api/v1/accounts/#{user.id}") + conn = get(conn, "/api/v1/accounts/#{user.id}") - assert user = json_response(conn, 200) + assert user_data = json_response(conn, 200) - assert user["note"] == note - assert user["display_name"] == name - assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user["emojis"] + assert user_data["note"] == note + assert user_data["display_name"] == name + assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user_data["emojis"] end test "update fields", %{conn: conn} do - user = insert(:user) - fields = [ %{"name" => "foo", "value" => ""}, %{"name" => "link", "value" => "cofe.io"} ] - account = + account_data = conn - |> assign(:user, user) |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response(200) - assert account["fields"] == [ + assert account_data["fields"] == [ %{"name" => "foo", "value" => "bar"}, %{"name" => "link", "value" => ~S(cofe.io)} ] - assert account["source"]["fields"] == [ + assert account_data["source"]["fields"] == [ %{ "name" => "foo", "value" => "" @@ -372,7 +293,6 @@ test "update fields", %{conn: conn} do account = conn |> put_req_header("content-type", "application/x-www-form-urlencoded") - |> assign(:user, user) |> patch("/api/v1/accounts/update_credentials", fields) |> json_response(200) @@ -398,7 +318,6 @@ test "update fields", %{conn: conn} do assert %{"error" => "Invalid request"} == conn - |> assign(:user, user) |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response(403) @@ -408,7 +327,6 @@ test "update fields", %{conn: conn} do assert %{"error" => "Invalid request"} == conn - |> assign(:user, user) |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response(403) @@ -421,7 +339,6 @@ test "update fields", %{conn: conn} do assert %{"error" => "Invalid request"} == conn - |> assign(:user, user) |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response(403) @@ -432,7 +349,6 @@ test "update fields", %{conn: conn} do account = conn - |> assign(:user, user) |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response(200) diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs index fa08ae4df..0d4860a42 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -87,6 +87,7 @@ test "respects limit_to_local_content == :unauthenticated for remote user nickna conn = build_conn() |> assign(:user, reading_user) + |> assign(:token, insert(:oauth_token, user: reading_user, scopes: ["read:accounts"])) |> get("/api/v1/accounts/#{user.nickname}") Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) @@ -144,8 +145,9 @@ test "returns 404 for internal.fetch actor", %{conn: conn} do end describe "user timelines" do - test "respects blocks", %{conn: conn} do - user_one = insert(:user) + setup do: oauth_access(["read:statuses"]) + + test "respects blocks", %{user: user_one, conn: conn} do user_two = insert(:user) user_three = insert(:user) @@ -154,46 +156,35 @@ test "respects blocks", %{conn: conn} do {:ok, activity} = CommonAPI.post(user_two, %{"status" => "User one sux0rz"}) {:ok, repeat, _} = CommonAPI.repeat(activity.id, user_three) - resp = - conn - |> get("/api/v1/accounts/#{user_two.id}/statuses") + resp = get(conn, "/api/v1/accounts/#{user_two.id}/statuses") assert [%{"id" => id}] = json_response(resp, 200) assert id == activity.id # Even a blocked user will deliver the full user timeline, there would be - # no point in looking at a blocked users timeline otherwise - resp = - conn - |> assign(:user, user_one) - |> get("/api/v1/accounts/#{user_two.id}/statuses") + # no point in looking at a blocked users timeline otherwise + resp = get(conn, "/api/v1/accounts/#{user_two.id}/statuses") assert [%{"id" => id}] = json_response(resp, 200) assert id == activity.id - resp = - conn - |> get("/api/v1/accounts/#{user_three.id}/statuses") - + # Third user's timeline includes the repeat when viewed by unauthenticated user + resp = get(build_conn(), "/api/v1/accounts/#{user_three.id}/statuses") assert [%{"id" => id}] = json_response(resp, 200) assert id == repeat.id - # When viewing a third user's timeline, the blocked users will NOT be - # shown. - resp = - conn - |> assign(:user, user_one) - |> get("/api/v1/accounts/#{user_three.id}/statuses") + # When viewing a third user's timeline, the blocked users' statuses will NOT be shown + resp = get(conn, "/api/v1/accounts/#{user_three.id}/statuses") assert [] = json_response(resp, 200) end - test "gets a users statuses", %{conn: conn} do + test "gets users statuses", %{conn: conn} do user_one = insert(:user) user_two = insert(:user) user_three = insert(:user) - {:ok, user_three} = User.follow(user_three, user_one) + {:ok, _user_three} = User.follow(user_three, user_one) {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"}) @@ -206,9 +197,7 @@ test "gets a users statuses", %{conn: conn} do {:ok, private_activity} = CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"}) - resp = - conn - |> get("/api/v1/accounts/#{user_one.id}/statuses") + resp = get(conn, "/api/v1/accounts/#{user_one.id}/statuses") assert [%{"id" => id}] = json_response(resp, 200) assert id == to_string(activity.id) @@ -216,6 +205,7 @@ test "gets a users statuses", %{conn: conn} do resp = conn |> assign(:user, user_two) + |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"])) |> get("/api/v1/accounts/#{user_one.id}/statuses") assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200) @@ -225,6 +215,7 @@ test "gets a users statuses", %{conn: conn} do resp = conn |> assign(:user, user_three) + |> assign(:token, insert(:oauth_token, user: user_three, scopes: ["read:statuses"])) |> get("/api/v1/accounts/#{user_one.id}/statuses") assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200) @@ -236,9 +227,7 @@ test "unimplemented pinned statuses feature", %{conn: conn} do note = insert(:note_activity) user = User.get_cached_by_ap_id(note.data["actor"]) - conn = - conn - |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") + conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?pinned=true") assert json_response(conn, 200) == [] end @@ -257,63 +246,51 @@ test "gets an users media", %{conn: conn} do {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]}) - conn = - conn - |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"}) + conn = get(conn, "/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"}) assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(image_post.id) - conn = - build_conn() - |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"}) + conn = get(build_conn(), "/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"}) assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(image_post.id) end - test "gets a user's statuses without reblogs", %{conn: conn} do - user = insert(:user) + test "gets a user's statuses without reblogs", %{user: user, conn: conn} do {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"}) {:ok, _, _} = CommonAPI.repeat(post.id, user) - conn = - conn - |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"}) + conn = get(conn, "/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"}) assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(post.id) - conn = - conn - |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"}) + conn = get(conn, "/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"}) assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(post.id) end - test "filters user's statuses by a hashtag", %{conn: conn} do - user = insert(:user) + test "filters user's statuses by a hashtag", %{user: user, conn: conn} do {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"}) {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"}) - conn = - conn - |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"}) + conn = get(conn, "/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"}) assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(post.id) end - test "the user views their own timelines and excludes direct messages", %{conn: conn} do - user = insert(:user) + test "the user views their own timelines and excludes direct messages", %{ + user: user, + conn: conn + } do {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"}) {:ok, _direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) conn = - conn - |> assign(:user, user) - |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_visibilities" => ["direct"]}) + get(conn, "/api/v1/accounts/#{user.id}/statuses", %{"exclude_visibilities" => ["direct"]}) assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(public_activity.id) @@ -321,46 +298,42 @@ test "the user views their own timelines and excludes direct messages", %{conn: end describe "followers" do - test "getting followers", %{conn: conn} do - user = insert(:user) + setup do: oauth_access(["read:accounts"]) + + test "getting followers", %{user: user, conn: conn} do other_user = insert(:user) {:ok, user} = User.follow(user, other_user) - conn = - conn - |> get("/api/v1/accounts/#{other_user.id}/followers") + conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers") assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(user.id) end - test "getting followers, hide_followers", %{conn: conn} do - user = insert(:user) + test "getting followers, hide_followers", %{user: user, conn: conn} do other_user = insert(:user, hide_followers: true) {:ok, _user} = User.follow(user, other_user) - conn = - conn - |> get("/api/v1/accounts/#{other_user.id}/followers") + conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers") assert [] == json_response(conn, 200) end - test "getting followers, hide_followers, same user requesting", %{conn: conn} do + test "getting followers, hide_followers, same user requesting" do user = insert(:user) other_user = insert(:user, hide_followers: true) {:ok, _user} = User.follow(user, other_user) conn = - conn + build_conn() |> assign(:user, other_user) + |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"])) |> get("/api/v1/accounts/#{other_user.id}/followers") refute [] == json_response(conn, 200) end - test "getting followers, pagination", %{conn: conn} do - user = insert(:user) + test "getting followers, pagination", %{user: user, conn: conn} do follower1 = insert(:user) follower2 = insert(:user) follower3 = insert(:user) @@ -368,29 +341,19 @@ test "getting followers, pagination", %{conn: conn} do {:ok, _} = User.follow(follower2, user) {:ok, _} = User.follow(follower3, user) - conn = - conn - |> assign(:user, user) - - res_conn = - conn - |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}") + res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}") assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200) assert id3 == follower3.id assert id2 == follower2.id - res_conn = - conn - |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}") + res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}") assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200) assert id2 == follower2.id assert id1 == follower1.id - res_conn = - conn - |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}") + res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}") assert [%{"id" => id2}] = json_response(res_conn, 200) assert id2 == follower2.id @@ -402,46 +365,47 @@ test "getting followers, pagination", %{conn: conn} do end describe "following" do - test "getting following", %{conn: conn} do - user = insert(:user) + setup do: oauth_access(["read:accounts"]) + + test "getting following", %{user: user, conn: conn} do other_user = insert(:user) {:ok, user} = User.follow(user, other_user) - conn = - conn - |> get("/api/v1/accounts/#{user.id}/following") + conn = get(conn, "/api/v1/accounts/#{user.id}/following") assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(other_user.id) end - test "getting following, hide_follows", %{conn: conn} do + test "getting following, hide_follows, other user requesting" do user = insert(:user, hide_follows: true) other_user = insert(:user) {:ok, user} = User.follow(user, other_user) conn = - conn + build_conn() + |> assign(:user, other_user) + |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"])) |> get("/api/v1/accounts/#{user.id}/following") assert [] == json_response(conn, 200) end - test "getting following, hide_follows, same user requesting", %{conn: conn} do + test "getting following, hide_follows, same user requesting" do user = insert(:user, hide_follows: true) other_user = insert(:user) {:ok, user} = User.follow(user, other_user) conn = - conn + build_conn() |> assign(:user, user) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["read:accounts"])) |> get("/api/v1/accounts/#{user.id}/following") refute [] == json_response(conn, 200) end - test "getting following, pagination", %{conn: conn} do - user = insert(:user) + test "getting following, pagination", %{user: user, conn: conn} do following1 = insert(:user) following2 = insert(:user) following3 = insert(:user) @@ -449,29 +413,20 @@ test "getting following, pagination", %{conn: conn} do {:ok, _} = User.follow(user, following2) {:ok, _} = User.follow(user, following3) - conn = - conn - |> assign(:user, user) - - res_conn = - conn - |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}") + res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}") assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200) assert id3 == following3.id assert id2 == following2.id - res_conn = - conn - |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}") + res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}") assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200) assert id2 == following2.id assert id1 == following1.id res_conn = - conn - |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}") + get(conn, "/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}") assert [%{"id" => id2}] = json_response(res_conn, 200) assert id2 == following2.id @@ -483,82 +438,52 @@ test "getting following, pagination", %{conn: conn} do end describe "follow/unfollow" do + setup do: oauth_access(["follow"]) + test "following / unfollowing a user", %{conn: conn} do - user = insert(:user) other_user = insert(:user) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/accounts/#{other_user.id}/follow") + ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/follow") - assert %{"id" => _id, "following" => true} = json_response(conn, 200) + assert %{"id" => _id, "following" => true} = json_response(ret_conn, 200) - user = User.get_cached_by_id(user.id) + ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/unfollow") - conn = - build_conn() - |> assign(:user, user) - |> post("/api/v1/accounts/#{other_user.id}/unfollow") + assert %{"id" => _id, "following" => false} = json_response(ret_conn, 200) - assert %{"id" => _id, "following" => false} = json_response(conn, 200) - - user = User.get_cached_by_id(user.id) - - conn = - build_conn() - |> assign(:user, user) - |> post("/api/v1/follows", %{"uri" => other_user.nickname}) + conn = post(conn, "/api/v1/follows", %{"uri" => other_user.nickname}) assert %{"id" => id} = json_response(conn, 200) assert id == to_string(other_user.id) end test "following without reblogs" do - follower = insert(:user) + %{conn: conn} = oauth_access(["follow", "read:statuses"]) followed = insert(:user) other_user = insert(:user) - conn = - build_conn() - |> assign(:user, follower) - |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false") + ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=false") - assert %{"showing_reblogs" => false} = json_response(conn, 200) + assert %{"showing_reblogs" => false} = json_response(ret_conn, 200) {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"}) {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed) - conn = - build_conn() - |> assign(:user, User.get_cached_by_id(follower.id)) - |> get("/api/v1/timelines/home") + ret_conn = get(conn, "/api/v1/timelines/home") - assert [] == json_response(conn, 200) + assert [] == json_response(ret_conn, 200) - conn = - build_conn() - |> assign(:user, User.get_cached_by_id(follower.id)) - |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true") + ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=true") - assert %{"showing_reblogs" => true} = json_response(conn, 200) + assert %{"showing_reblogs" => true} = json_response(ret_conn, 200) - conn = - build_conn() - |> assign(:user, User.get_cached_by_id(follower.id)) - |> get("/api/v1/timelines/home") + conn = get(conn, "/api/v1/timelines/home") expected_activity_id = reblog.id assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200) end - test "following / unfollowing errors" do - user = insert(:user) - - conn = - build_conn() - |> assign(:user, user) - + test "following / unfollowing errors", %{user: user, conn: conn} do # self follow conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow") assert %{"error" => "Record not found"} = json_response(conn_res, 404) @@ -588,47 +513,34 @@ test "following / unfollowing errors" do end describe "mute/unmute" do + setup do: oauth_access(["write:mutes"]) + test "with notifications", %{conn: conn} do - user = insert(:user) other_user = insert(:user) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/accounts/#{other_user.id}/mute") + ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/mute") - response = json_response(conn, 200) + response = json_response(ret_conn, 200) assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response - user = User.get_cached_by_id(user.id) - conn = - build_conn() - |> assign(:user, user) - |> post("/api/v1/accounts/#{other_user.id}/unmute") + conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute") response = json_response(conn, 200) assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response end test "without notifications", %{conn: conn} do - user = insert(:user) other_user = insert(:user) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"}) + ret_conn = + post(conn, "/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"}) - response = json_response(conn, 200) + response = json_response(ret_conn, 200) assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response - user = User.get_cached_by_id(user.id) - conn = - build_conn() - |> assign(:user, user) - |> post("/api/v1/accounts/#{other_user.id}/unmute") + conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute") response = json_response(conn, 200) assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response @@ -639,8 +551,9 @@ test "without notifications", %{conn: conn} do setup do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"}) + %{conn: conn} = oauth_access(["read:statuses"], user: user) - [user: user, activity: activity] + [conn: conn, user: user, activity: activity] end test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do @@ -648,7 +561,6 @@ test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do result = conn - |> assign(:user, user) |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") |> json_response(200) @@ -658,23 +570,15 @@ test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do end end - test "blocking / unblocking a user", %{conn: conn} do - user = insert(:user) + test "blocking / unblocking a user" do + %{conn: conn} = oauth_access(["follow"]) other_user = insert(:user) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/accounts/#{other_user.id}/block") + ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/block") - assert %{"id" => _id, "blocking" => true} = json_response(conn, 200) + assert %{"id" => _id, "blocking" => true} = json_response(ret_conn, 200) - user = User.get_cached_by_id(user.id) - - conn = - build_conn() - |> assign(:user, user) - |> post("/api/v1/accounts/#{other_user.id}/unblock") + conn = post(conn, "/api/v1/accounts/#{other_user.id}/unblock") assert %{"id" => _id, "blocking" => false} = json_response(conn, 200) end @@ -693,8 +597,7 @@ test "blocking / unblocking a user", %{conn: conn} do test "Account registration via Application", %{conn: conn} do conn = - conn - |> post("/api/v1/apps", %{ + post(conn, "/api/v1/apps", %{ client_name: "client_name", redirect_uris: "urn:ietf:wg:oauth:2.0:oob", scopes: "read, write, follow" @@ -711,8 +614,7 @@ test "Account registration via Application", %{conn: conn} do } = json_response(conn, 200) conn = - conn - |> post("/oauth/token", %{ + post(conn, "/oauth/token", %{ grant_type: "client_credentials", client_id: client_id, client_secret: client_secret @@ -769,13 +671,13 @@ test "rate limit", %{conn: conn} do app_token = insert(:oauth_token, user: nil) conn = - put_req_header(conn, "authorization", "Bearer " <> app_token.token) + conn + |> put_req_header("authorization", "Bearer " <> app_token.token) |> Map.put(:remote_ip, {15, 15, 15, 15}) for i <- 1..5 do conn = - conn - |> post("/api/v1/accounts", %{ + post(conn, "/api/v1/accounts", %{ username: "#{i}lain", email: "#{i}lain@example.org", password: "PlzDontHackLain", @@ -798,8 +700,7 @@ test "rate limit", %{conn: conn} do end conn = - conn - |> post("/api/v1/accounts", %{ + post(conn, "/api/v1/accounts", %{ username: "6lain", email: "6lain@example.org", password: "PlzDontHackLain", @@ -815,9 +716,7 @@ test "returns bad_request if missing required params", %{ } do app_token = insert(:oauth_token, user: nil) - conn = - conn - |> put_req_header("authorization", "Bearer " <> app_token.token) + conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token) res = post(conn, "/api/v1/accounts", valid_params) assert json_response(res, 200) @@ -836,9 +735,7 @@ test "returns bad_request if missing required params", %{ end test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do - conn = - conn - |> put_req_header("authorization", "Bearer " <> "invalid-token") + conn = put_req_header(conn, "authorization", "Bearer " <> "invalid-token") res = post(conn, "/api/v1/accounts", valid_params) assert json_response(res, 403) == %{"error" => "Invalid credentials"} @@ -846,15 +743,14 @@ test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_ end describe "GET /api/v1/accounts/:id/lists - account_lists" do - test "returns lists to which the account belongs", %{conn: conn} do - user = insert(:user) + test "returns lists to which the account belongs" do + %{user: user, conn: conn} = oauth_access(["read:lists"]) other_user = insert(:user) assert {:ok, %Pleroma.List{} = list} = Pleroma.List.create("Test List", user) {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user) res = conn - |> assign(:user, user) |> get("/api/v1/accounts/#{other_user.id}/lists") |> json_response(200) @@ -863,13 +759,9 @@ test "returns lists to which the account belongs", %{conn: conn} do end describe "verify_credentials" do - test "verify_credentials", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> get("/api/v1/accounts/verify_credentials") + test "verify_credentials" do + %{user: user, conn: conn} = oauth_access(["read:accounts"]) + conn = get(conn, "/api/v1/accounts/verify_credentials") response = json_response(conn, 200) @@ -878,25 +770,21 @@ test "verify_credentials", %{conn: conn} do assert id == to_string(user.id) end - test "verify_credentials default scope unlisted", %{conn: conn} do + test "verify_credentials default scope unlisted" do user = insert(:user, default_scope: "unlisted") + %{conn: conn} = oauth_access(["read:accounts"], user: user) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/accounts/verify_credentials") + conn = get(conn, "/api/v1/accounts/verify_credentials") assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200) assert id == to_string(user.id) end - test "locked accounts", %{conn: conn} do + test "locked accounts" do user = insert(:user, default_scope: "private") + %{conn: conn} = oauth_access(["read:accounts"], user: user) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/accounts/verify_credentials") + conn = get(conn, "/api/v1/accounts/verify_credentials") assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200) assert id == to_string(user.id) @@ -904,15 +792,13 @@ test "locked accounts", %{conn: conn} do end describe "user relationships" do - test "returns the relationships for the current user", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - {:ok, user} = User.follow(user, other_user) + setup do: oauth_access(["read:follows"]) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]}) + test "returns the relationships for the current user", %{user: user, conn: conn} do + other_user = insert(:user) + {:ok, _user} = User.follow(user, other_user) + + conn = get(conn, "/api/v1/accounts/relationships", %{"id" => [other_user.id]}) assert [relationship] = json_response(conn, 200) @@ -920,34 +806,26 @@ test "returns the relationships for the current user", %{conn: conn} do end test "returns an empty list on a bad request", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> get("/api/v1/accounts/relationships", %{}) + conn = get(conn, "/api/v1/accounts/relationships", %{}) assert [] = json_response(conn, 200) end end - test "getting a list of mutes", %{conn: conn} do - user = insert(:user) + test "getting a list of mutes" do + %{user: user, conn: conn} = oauth_access(["read:mutes"]) other_user = insert(:user) {:ok, _user_relationships} = User.mute(user, other_user) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/mutes") + conn = get(conn, "/api/v1/mutes") other_user_id = to_string(other_user.id) assert [%{"id" => ^other_user_id}] = json_response(conn, 200) end - test "getting a list of blocks", %{conn: conn} do - user = insert(:user) + test "getting a list of blocks" do + %{user: user, conn: conn} = oauth_access(["read:blocks"]) other_user = insert(:user) {:ok, _user_relationship} = User.block(user, other_user) diff --git a/test/web/mastodon_api/controllers/conversation_controller_test.exs b/test/web/mastodon_api/controllers/conversation_controller_test.exs index 2a1223b18..4bb9781a6 100644 --- a/test/web/mastodon_api/controllers/conversation_controller_test.exs +++ b/test/web/mastodon_api/controllers/conversation_controller_test.exs @@ -10,8 +10,9 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do import Pleroma.Factory - test "returns a list of conversations", %{conn: conn} do - user_one = insert(:user) + setup do: oauth_access(["read:statuses"]) + + test "returns a list of conversations", %{user: user_one, conn: conn} do user_two = insert(:user) user_three = insert(:user) @@ -33,10 +34,7 @@ test "returns a list of conversations", %{conn: conn} do "visibility" => "private" }) - res_conn = - conn - |> assign(:user, user_one) - |> get("/api/v1/conversations") + res_conn = get(conn, "/api/v1/conversations") assert response = json_response(res_conn, 200) @@ -59,8 +57,7 @@ test "returns a list of conversations", %{conn: conn} do assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 end - test "filters conversations by recipients", %{conn: conn} do - user_one = insert(:user) + test "filters conversations by recipients", %{user: user_one, conn: conn} do user_two = insert(:user) user_three = insert(:user) @@ -96,7 +93,6 @@ test "filters conversations by recipients", %{conn: conn} do [conversation1, conversation2] = conn - |> assign(:user, user_one) |> get("/api/v1/conversations", %{"recipients" => [user_two.id]}) |> json_response(200) @@ -105,15 +101,13 @@ test "filters conversations by recipients", %{conn: conn} do [conversation1] = conn - |> assign(:user, user_one) |> get("/api/v1/conversations", %{"recipients" => [user_two.id, user_three.id]}) |> json_response(200) assert conversation1["last_status"]["id"] == direct3.id end - test "updates the last_status on reply", %{conn: conn} do - user_one = insert(:user) + test "updates the last_status on reply", %{user: user_one, conn: conn} do user_two = insert(:user) {:ok, direct} = @@ -131,15 +125,13 @@ test "updates the last_status on reply", %{conn: conn} do [%{"last_status" => res_last_status}] = conn - |> assign(:user, user_one) |> get("/api/v1/conversations") |> json_response(200) assert res_last_status["id"] == direct_reply.id end - test "the user marks a conversation as read", %{conn: conn} do - user_one = insert(:user) + test "the user marks a conversation as read", %{user: user_one, conn: conn} do user_two = insert(:user) {:ok, direct} = @@ -151,15 +143,21 @@ test "the user marks a conversation as read", %{conn: conn} do assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1 - [%{"id" => direct_conversation_id, "unread" => true}] = - conn + user_two_conn = + build_conn() |> assign(:user, user_two) + |> assign( + :token, + insert(:oauth_token, user: user_two, scopes: ["read:statuses", "write:conversations"]) + ) + + [%{"id" => direct_conversation_id, "unread" => true}] = + user_two_conn |> get("/api/v1/conversations") |> json_response(200) %{"unread" => false} = - conn - |> assign(:user, user_two) + user_two_conn |> post("/api/v1/conversations/#{direct_conversation_id}/read") |> json_response(200) @@ -176,7 +174,6 @@ test "the user marks a conversation as read", %{conn: conn} do [%{"unread" => true}] = conn - |> assign(:user, user_one) |> get("/api/v1/conversations") |> json_response(200) @@ -195,8 +192,7 @@ test "the user marks a conversation as read", %{conn: conn} do assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 end - test "(vanilla) Mastodon frontend behaviour", %{conn: conn} do - user_one = insert(:user) + test "(vanilla) Mastodon frontend behaviour", %{user: user_one, conn: conn} do user_two = insert(:user) {:ok, direct} = @@ -205,10 +201,7 @@ test "(vanilla) Mastodon frontend behaviour", %{conn: conn} do "visibility" => "direct" }) - res_conn = - conn - |> assign(:user, user_one) - |> get("/api/v1/statuses/#{direct.id}/context") + res_conn = get(conn, "/api/v1/statuses/#{direct.id}/context") assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200) end diff --git a/test/web/mastodon_api/controllers/domain_block_controller_test.exs b/test/web/mastodon_api/controllers/domain_block_controller_test.exs index 25a279cdc..55de625ba 100644 --- a/test/web/mastodon_api/controllers/domain_block_controller_test.exs +++ b/test/web/mastodon_api/controllers/domain_block_controller_test.exs @@ -9,31 +9,25 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do import Pleroma.Factory - test "blocking / unblocking a domain", %{conn: conn} do - user = insert(:user) + test "blocking / unblocking a domain" do + %{user: user, conn: conn} = oauth_access(["write:blocks"]) other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) + ret_conn = post(conn, "/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) - assert %{} = json_response(conn, 200) + assert %{} = json_response(ret_conn, 200) user = User.get_cached_by_ap_id(user.ap_id) assert User.blocks?(user, other_user) - conn = - build_conn() - |> assign(:user, user) - |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) + ret_conn = delete(conn, "/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) - assert %{} = json_response(conn, 200) + assert %{} = json_response(ret_conn, 200) user = User.get_cached_by_ap_id(user.ap_id) refute User.blocks?(user, other_user) end - test "getting a list of domain blocks", %{conn: conn} do - user = insert(:user) + test "getting a list of domain blocks" do + %{user: user, conn: conn} = oauth_access(["read:blocks"]) {:ok, user} = User.block_domain(user, "bad.site") {:ok, user} = User.block_domain(user, "even.worse.site") diff --git a/test/web/mastodon_api/controllers/filter_controller_test.exs b/test/web/mastodon_api/controllers/filter_controller_test.exs index 550689788..3aea17ec7 100644 --- a/test/web/mastodon_api/controllers/filter_controller_test.exs +++ b/test/web/mastodon_api/controllers/filter_controller_test.exs @@ -7,20 +7,15 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do alias Pleroma.Web.MastodonAPI.FilterView - import Pleroma.Factory - - test "creating a filter", %{conn: conn} do - user = insert(:user) + test "creating a filter" do + %{conn: conn} = oauth_access(["write:filters"]) filter = %Pleroma.Filter{ phrase: "knights", context: ["home"] } - conn = - conn - |> assign(:user, user) - |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context}) + conn = post(conn, "/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context}) assert response = json_response(conn, 200) assert response["phrase"] == filter.phrase @@ -30,8 +25,8 @@ test "creating a filter", %{conn: conn} do assert response["id"] != "" end - test "fetching a list of filters", %{conn: conn} do - user = insert(:user) + test "fetching a list of filters" do + %{user: user, conn: conn} = oauth_access(["read:filters"]) query_one = %Pleroma.Filter{ user_id: user.id, @@ -52,7 +47,6 @@ test "fetching a list of filters", %{conn: conn} do response = conn - |> assign(:user, user) |> get("/api/v1/filters") |> json_response(200) @@ -64,8 +58,8 @@ test "fetching a list of filters", %{conn: conn} do ) end - test "get a filter", %{conn: conn} do - user = insert(:user) + test "get a filter" do + %{user: user, conn: conn} = oauth_access(["read:filters"]) query = %Pleroma.Filter{ user_id: user.id, @@ -76,16 +70,13 @@ test "get a filter", %{conn: conn} do {:ok, filter} = Pleroma.Filter.create(query) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/filters/#{filter.filter_id}") + conn = get(conn, "/api/v1/filters/#{filter.filter_id}") assert _response = json_response(conn, 200) end - test "update a filter", %{conn: conn} do - user = insert(:user) + test "update a filter" do + %{user: user, conn: conn} = oauth_access(["write:filters"]) query = %Pleroma.Filter{ user_id: user.id, @@ -102,9 +93,7 @@ test "update a filter", %{conn: conn} do } conn = - conn - |> assign(:user, user) - |> put("/api/v1/filters/#{query.filter_id}", %{ + put(conn, "/api/v1/filters/#{query.filter_id}", %{ phrase: new.phrase, context: new.context }) @@ -114,8 +103,8 @@ test "update a filter", %{conn: conn} do assert response["context"] == new.context end - test "delete a filter", %{conn: conn} do - user = insert(:user) + test "delete a filter" do + %{user: user, conn: conn} = oauth_access(["write:filters"]) query = %Pleroma.Filter{ user_id: user.id, @@ -126,10 +115,7 @@ test "delete a filter", %{conn: conn} do {:ok, filter} = Pleroma.Filter.create(query) - conn = - conn - |> assign(:user, user) - |> delete("/api/v1/filters/#{filter.filter_id}") + conn = delete(conn, "/api/v1/filters/#{filter.filter_id}") assert response = json_response(conn, 200) assert response == %{} diff --git a/test/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/web/mastodon_api/controllers/follow_request_controller_test.exs index 288cd9029..6e4a76501 100644 --- a/test/web/mastodon_api/controllers/follow_request_controller_test.exs +++ b/test/web/mastodon_api/controllers/follow_request_controller_test.exs @@ -11,8 +11,13 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do import Pleroma.Factory describe "locked accounts" do - test "/api/v1/follow_requests works" do + setup do user = insert(:user, locked: true) + %{conn: conn} = oauth_access(["follow"], user: user) + %{user: user, conn: conn} + end + + test "/api/v1/follow_requests works", %{user: user, conn: conn} do other_user = insert(:user) {:ok, _activity} = ActivityPub.follow(other_user, user) @@ -20,17 +25,13 @@ test "/api/v1/follow_requests works" do assert User.following?(other_user, user) == false - conn = - build_conn() - |> assign(:user, user) - |> get("/api/v1/follow_requests") + conn = get(conn, "/api/v1/follow_requests") assert [relationship] = json_response(conn, 200) assert to_string(other_user.id) == relationship["id"] end - test "/api/v1/follow_requests/:id/authorize works" do - user = insert(:user, locked: true) + test "/api/v1/follow_requests/:id/authorize works", %{user: user, conn: conn} do other_user = insert(:user) {:ok, _activity} = ActivityPub.follow(other_user, user) @@ -41,10 +42,7 @@ test "/api/v1/follow_requests/:id/authorize works" do assert User.following?(other_user, user) == false - conn = - build_conn() - |> assign(:user, user) - |> post("/api/v1/follow_requests/#{other_user.id}/authorize") + conn = post(conn, "/api/v1/follow_requests/#{other_user.id}/authorize") assert relationship = json_response(conn, 200) assert to_string(other_user.id) == relationship["id"] @@ -55,18 +53,14 @@ test "/api/v1/follow_requests/:id/authorize works" do assert User.following?(other_user, user) == true end - test "/api/v1/follow_requests/:id/reject works" do - user = insert(:user, locked: true) + test "/api/v1/follow_requests/:id/reject works", %{user: user, conn: conn} do other_user = insert(:user) {:ok, _activity} = ActivityPub.follow(other_user, user) user = User.get_cached_by_id(user.id) - conn = - build_conn() - |> assign(:user, user) - |> post("/api/v1/follow_requests/#{other_user.id}/reject") + conn = post(conn, "/api/v1/follow_requests/#{other_user.id}/reject") assert relationship = json_response(conn, 200) assert to_string(other_user.id) == relationship["id"] diff --git a/test/web/mastodon_api/controllers/list_controller_test.exs b/test/web/mastodon_api/controllers/list_controller_test.exs index 093506309..a6effbb69 100644 --- a/test/web/mastodon_api/controllers/list_controller_test.exs +++ b/test/web/mastodon_api/controllers/list_controller_test.exs @@ -9,44 +9,35 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do import Pleroma.Factory - test "creating a list", %{conn: conn} do - user = insert(:user) + test "creating a list" do + %{conn: conn} = oauth_access(["write:lists"]) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/lists", %{"title" => "cuties"}) + conn = post(conn, "/api/v1/lists", %{"title" => "cuties"}) assert %{"title" => title} = json_response(conn, 200) assert title == "cuties" end - test "renders error for invalid params", %{conn: conn} do - user = insert(:user) + test "renders error for invalid params" do + %{conn: conn} = oauth_access(["write:lists"]) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/lists", %{"title" => nil}) + conn = post(conn, "/api/v1/lists", %{"title" => nil}) assert %{"error" => "can't be blank"} == json_response(conn, :unprocessable_entity) end - test "listing a user's lists", %{conn: conn} do - user = insert(:user) + test "listing a user's lists" do + %{conn: conn} = oauth_access(["read:lists", "write:lists"]) conn - |> assign(:user, user) |> post("/api/v1/lists", %{"title" => "cuties"}) + |> json_response(:ok) conn - |> assign(:user, user) |> post("/api/v1/lists", %{"title" => "cofe"}) + |> json_response(:ok) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/lists") + conn = get(conn, "/api/v1/lists") assert [ %{"id" => _, "title" => "cofe"}, @@ -54,41 +45,35 @@ test "listing a user's lists", %{conn: conn} do ] = json_response(conn, :ok) end - test "adding users to a list", %{conn: conn} do - user = insert(:user) + test "adding users to a list" do + %{user: user, conn: conn} = oauth_access(["write:lists"]) other_user = insert(:user) {:ok, list} = Pleroma.List.create("name", user) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) + conn = post(conn, "/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) assert %{} == json_response(conn, 200) %Pleroma.List{following: following} = Pleroma.List.get(list.id, user) assert following == [other_user.follower_address] end - test "removing users from a list", %{conn: conn} do - user = insert(:user) + test "removing users from a list" do + %{user: user, conn: conn} = oauth_access(["write:lists"]) other_user = insert(:user) third_user = insert(:user) {:ok, list} = Pleroma.List.create("name", user) {:ok, list} = Pleroma.List.follow(list, other_user) {:ok, list} = Pleroma.List.follow(list, third_user) - conn = - conn - |> assign(:user, user) - |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) + conn = delete(conn, "/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) assert %{} == json_response(conn, 200) %Pleroma.List{following: following} = Pleroma.List.get(list.id, user) assert following == [third_user.follower_address] end - test "listing users in a list", %{conn: conn} do - user = insert(:user) + test "listing users in a list" do + %{user: user, conn: conn} = oauth_access(["read:lists"]) other_user = insert(:user) {:ok, list} = Pleroma.List.create("name", user) {:ok, list} = Pleroma.List.follow(list, other_user) @@ -102,8 +87,8 @@ test "listing users in a list", %{conn: conn} do assert id == to_string(other_user.id) end - test "retrieving a list", %{conn: conn} do - user = insert(:user) + test "retrieving a list" do + %{user: user, conn: conn} = oauth_access(["read:lists"]) {:ok, list} = Pleroma.List.create("name", user) conn = @@ -115,32 +100,26 @@ test "retrieving a list", %{conn: conn} do assert id == to_string(list.id) end - test "renders 404 if list is not found", %{conn: conn} do - user = insert(:user) + test "renders 404 if list is not found" do + %{conn: conn} = oauth_access(["read:lists"]) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/lists/666") + conn = get(conn, "/api/v1/lists/666") assert %{"error" => "List not found"} = json_response(conn, :not_found) end - test "renaming a list", %{conn: conn} do - user = insert(:user) + test "renaming a list" do + %{user: user, conn: conn} = oauth_access(["write:lists"]) {:ok, list} = Pleroma.List.create("name", user) - conn = - conn - |> assign(:user, user) - |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"}) + conn = put(conn, "/api/v1/lists/#{list.id}", %{"title" => "newname"}) assert %{"title" => name} = json_response(conn, 200) assert name == "newname" end - test "validates title when renaming a list", %{conn: conn} do - user = insert(:user) + test "validates title when renaming a list" do + %{user: user, conn: conn} = oauth_access(["write:lists"]) {:ok, list} = Pleroma.List.create("name", user) conn = @@ -151,14 +130,11 @@ test "validates title when renaming a list", %{conn: conn} do assert %{"error" => "can't be blank"} == json_response(conn, :unprocessable_entity) end - test "deleting a list", %{conn: conn} do - user = insert(:user) + test "deleting a list" do + %{user: user, conn: conn} = oauth_access(["write:lists"]) {:ok, list} = Pleroma.List.create("name", user) - conn = - conn - |> assign(:user, user) - |> delete("/api/v1/lists/#{list.id}") + conn = delete(conn, "/api/v1/lists/#{list.id}") assert %{} = json_response(conn, 200) assert is_nil(Repo.get(Pleroma.List, list.id)) diff --git a/test/web/mastodon_api/controllers/media_controller_test.exs b/test/web/mastodon_api/controllers/media_controller_test.exs index 06c6a1cb3..042511ca4 100644 --- a/test/web/mastodon_api/controllers/media_controller_test.exs +++ b/test/web/mastodon_api/controllers/media_controller_test.exs @@ -9,23 +9,17 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub - import Pleroma.Factory + setup do: oauth_access(["write:media"]) describe "media upload" do setup do - user = insert(:user) - - conn = - build_conn() - |> assign(:user, user) - image = %Plug.Upload{ content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } - [conn: conn, image: image] + [image: image] end clear_config([:media_proxy]) @@ -49,9 +43,7 @@ test "returns uploaded image", %{conn: conn, image: image} do end describe "PUT /api/v1/media/:id" do - setup do - actor = insert(:user) - + setup %{user: actor} do file = %Plug.Upload{ content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), @@ -65,13 +57,12 @@ test "returns uploaded image", %{conn: conn, image: image} do description: "test-m" ) - [actor: actor, object: object] + [object: object] end - test "updates name of media", %{conn: conn, actor: actor, object: object} do + test "updates name of media", %{conn: conn, object: object} do media = conn - |> assign(:user, actor) |> put("/api/v1/media/#{object.id}", %{"description" => "test-media"}) |> json_response(:ok) @@ -79,10 +70,9 @@ test "updates name of media", %{conn: conn, actor: actor, object: object} do assert refresh_record(object).data["name"] == "test-media" end - test "returns error wheb request is bad", %{conn: conn, actor: actor, object: object} do + test "returns error when request is bad", %{conn: conn, object: object} do media = conn - |> assign(:user, actor) |> put("/api/v1/media/#{object.id}", %{}) |> json_response(400) diff --git a/test/web/mastodon_api/controllers/notification_controller_test.exs b/test/web/mastodon_api/controllers/notification_controller_test.exs index 6635ea7a2..86303f92f 100644 --- a/test/web/mastodon_api/controllers/notification_controller_test.exs +++ b/test/web/mastodon_api/controllers/notification_controller_test.exs @@ -12,8 +12,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do import Pleroma.Factory - test "list of notifications", %{conn: conn} do - user = insert(:user) + test "list of notifications" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) other_user = insert(:user) {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) @@ -34,18 +34,15 @@ test "list of notifications", %{conn: conn} do assert response == expected_response end - test "getting a single notification", %{conn: conn} do - user = insert(:user) + test "getting a single notification" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) other_user = insert(:user) {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) {:ok, [notification]} = Notification.create_notifications(activity) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/notifications/#{notification.id}") + conn = get(conn, "/api/v1/notifications/#{notification.id}") expected_response = "hi "hi @#{user.nickname}"}) @@ -72,32 +69,26 @@ test "dismissing a single notification", %{conn: conn} do assert %{} = json_response(conn, 200) end - test "clearing all notifications", %{conn: conn} do - user = insert(:user) + test "clearing all notifications" do + %{user: user, conn: conn} = oauth_access(["write:notifications", "read:notifications"]) other_user = insert(:user) {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) {:ok, [_notification]} = Notification.create_notifications(activity) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/notifications/clear") + ret_conn = post(conn, "/api/v1/notifications/clear") - assert %{} = json_response(conn, 200) + assert %{} = json_response(ret_conn, 200) - conn = - build_conn() - |> assign(:user, user) - |> get("/api/v1/notifications") + ret_conn = get(conn, "/api/v1/notifications") - assert all = json_response(conn, 200) + assert all = json_response(ret_conn, 200) assert all == [] end - test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do - user = insert(:user) + test "paginates notifications using min_id, since_id, max_id, and limit" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) other_user = insert(:user) {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) @@ -138,8 +129,8 @@ test "paginates notifications using min_id, since_id, max_id, and limit", %{conn end describe "exclude_visibilities" do - test "filters notifications for mentions", %{conn: conn} do - user = insert(:user) + test "filters notifications for mentions" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) other_user = insert(:user) {:ok, public_activity} = @@ -154,8 +145,6 @@ test "filters notifications for mentions", %{conn: conn} do {:ok, private_activity} = CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "private"}) - conn = assign(conn, :user, user) - conn_res = get(conn, "/api/v1/notifications", %{ exclude_visibilities: ["public", "unlisted", "private"] @@ -189,9 +178,9 @@ test "filters notifications for mentions", %{conn: conn} do assert id == public_activity.id end - test "filters notifications for Like activities", %{conn: conn} do + test "filters notifications for Like activities" do user = insert(:user) - other_user = insert(:user) + %{user: other_user, conn: conn} = oauth_access(["read:notifications"]) {:ok, public_activity} = CommonAPI.post(other_user, %{"status" => ".", "visibility" => "public"}) @@ -212,7 +201,6 @@ test "filters notifications for Like activities", %{conn: conn} do activity_ids = conn - |> assign(:user, other_user) |> get("/api/v1/notifications", %{exclude_visibilities: ["direct"]}) |> json_response(200) |> Enum.map(& &1["status"]["id"]) @@ -224,7 +212,6 @@ test "filters notifications for Like activities", %{conn: conn} do activity_ids = conn - |> assign(:user, other_user) |> get("/api/v1/notifications", %{exclude_visibilities: ["unlisted"]}) |> json_response(200) |> Enum.map(& &1["status"]["id"]) @@ -236,7 +223,6 @@ test "filters notifications for Like activities", %{conn: conn} do activity_ids = conn - |> assign(:user, other_user) |> get("/api/v1/notifications", %{exclude_visibilities: ["private"]}) |> json_response(200) |> Enum.map(& &1["status"]["id"]) @@ -248,7 +234,6 @@ test "filters notifications for Like activities", %{conn: conn} do activity_ids = conn - |> assign(:user, other_user) |> get("/api/v1/notifications", %{exclude_visibilities: ["public"]}) |> json_response(200) |> Enum.map(& &1["status"]["id"]) @@ -259,9 +244,9 @@ test "filters notifications for Like activities", %{conn: conn} do assert direct_activity.id in activity_ids end - test "filters notifications for Announce activities", %{conn: conn} do + test "filters notifications for Announce activities" do user = insert(:user) - other_user = insert(:user) + %{user: other_user, conn: conn} = oauth_access(["read:notifications"]) {:ok, public_activity} = CommonAPI.post(other_user, %{"status" => ".", "visibility" => "public"}) @@ -274,7 +259,6 @@ test "filters notifications for Announce activities", %{conn: conn} do activity_ids = conn - |> assign(:user, other_user) |> get("/api/v1/notifications", %{exclude_visibilities: ["unlisted"]}) |> json_response(200) |> Enum.map(& &1["status"]["id"]) @@ -284,8 +268,8 @@ test "filters notifications for Announce activities", %{conn: conn} do end end - test "filters notifications using exclude_types", %{conn: conn} do - user = insert(:user) + test "filters notifications using exclude_types" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) other_user = insert(:user) {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"}) @@ -299,8 +283,6 @@ test "filters notifications using exclude_types", %{conn: conn} do reblog_notification_id = get_notification_id_by_activity(reblog_activity) follow_notification_id = get_notification_id_by_activity(follow_activity) - conn = assign(conn, :user, user) - conn_res = get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]}) @@ -322,8 +304,8 @@ test "filters notifications using exclude_types", %{conn: conn} do assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200) end - test "destroy multiple", %{conn: conn} do - user = insert(:user) + test "destroy multiple" do + %{user: user, conn: conn} = oauth_access(["read:notifications", "write:notifications"]) other_user = insert(:user) {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) @@ -336,8 +318,6 @@ test "destroy multiple", %{conn: conn} do notification3_id = get_notification_id_by_activity(activity3) notification4_id = get_notification_id_by_activity(activity4) - conn = assign(conn, :user, user) - result = conn |> get("/api/v1/notifications") @@ -348,6 +328,7 @@ test "destroy multiple", %{conn: conn} do conn2 = conn |> assign(:user, other_user) + |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:notifications"])) result = conn2 @@ -372,97 +353,110 @@ test "destroy multiple", %{conn: conn} do assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result end - test "doesn't see notifications after muting user with notifications", %{conn: conn} do - user = insert(:user) + test "doesn't see notifications after muting user with notifications" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) user2 = insert(:user) {:ok, _, _, _} = CommonAPI.follow(user, user2) {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"}) - conn = assign(conn, :user, user) + ret_conn = get(conn, "/api/v1/notifications") - conn = get(conn, "/api/v1/notifications") - - assert length(json_response(conn, 200)) == 1 + assert length(json_response(ret_conn, 200)) == 1 {:ok, _user_relationships} = User.mute(user, user2) - conn = assign(build_conn(), :user, user) conn = get(conn, "/api/v1/notifications") assert json_response(conn, 200) == [] end - test "see notifications after muting user without notifications", %{conn: conn} do - user = insert(:user) + test "see notifications after muting user without notifications" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) user2 = insert(:user) {:ok, _, _, _} = CommonAPI.follow(user, user2) {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"}) - conn = assign(conn, :user, user) + ret_conn = get(conn, "/api/v1/notifications") - conn = get(conn, "/api/v1/notifications") - - assert length(json_response(conn, 200)) == 1 + assert length(json_response(ret_conn, 200)) == 1 {:ok, _user_relationships} = User.mute(user, user2, false) - conn = assign(build_conn(), :user, user) conn = get(conn, "/api/v1/notifications") assert length(json_response(conn, 200)) == 1 end - test "see notifications after muting user with notifications and with_muted parameter", %{ - conn: conn - } do - user = insert(:user) + test "see notifications after muting user with notifications and with_muted parameter" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) user2 = insert(:user) {:ok, _, _, _} = CommonAPI.follow(user, user2) {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"}) - conn = assign(conn, :user, user) + ret_conn = get(conn, "/api/v1/notifications") - conn = get(conn, "/api/v1/notifications") - - assert length(json_response(conn, 200)) == 1 + assert length(json_response(ret_conn, 200)) == 1 {:ok, _user_relationships} = User.mute(user, user2) - conn = assign(build_conn(), :user, user) conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"}) assert length(json_response(conn, 200)) == 1 end - test "see move notifications with `with_move` parameter", %{ - conn: conn - } do + test "see move notifications with `with_move` parameter" do old_user = insert(:user) new_user = insert(:user, also_known_as: [old_user.ap_id]) - follower = insert(:user) + %{user: follower, conn: conn} = oauth_access(["read:notifications"]) User.follow(follower, old_user) Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user) Pleroma.Tests.ObanHelpers.perform_all() - conn = - conn - |> assign(:user, follower) - |> get("/api/v1/notifications") + ret_conn = get(conn, "/api/v1/notifications") - assert json_response(conn, 200) == [] + assert json_response(ret_conn, 200) == [] - conn = - build_conn() - |> assign(:user, follower) - |> get("/api/v1/notifications", %{"with_move" => "true"}) + conn = get(conn, "/api/v1/notifications", %{"with_move" => "true"}) assert length(json_response(conn, 200)) == 1 end + describe "link headers" do + test "preserves parameters in link headers" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + other_user = insert(:user) + + {:ok, activity1} = + CommonAPI.post(other_user, %{ + "status" => "hi @#{user.nickname}", + "visibility" => "public" + }) + + {:ok, activity2} = + CommonAPI.post(other_user, %{ + "status" => "hi @#{user.nickname}", + "visibility" => "public" + }) + + notification1 = Repo.get_by(Notification, activity_id: activity1.id) + notification2 = Repo.get_by(Notification, activity_id: activity2.id) + + conn = + conn + |> assign(:user, user) + |> get("/api/v1/notifications", %{media_only: true}) + + assert [link_header] = get_resp_header(conn, "link") + assert link_header =~ ~r/media_only=true/ + assert link_header =~ ~r/min_id=#{notification2.id}/ + assert link_header =~ ~r/max_id=#{notification1.id}/ + end + end + defp get_notification_id_by_activity(%{id: id}) do Notification |> Repo.get_by(activity_id: id) diff --git a/test/web/mastodon_api/controllers/poll_controller_test.exs b/test/web/mastodon_api/controllers/poll_controller_test.exs index 40cf3e879..5a1cea11b 100644 --- a/test/web/mastodon_api/controllers/poll_controller_test.exs +++ b/test/web/mastodon_api/controllers/poll_controller_test.exs @@ -11,9 +11,9 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do import Pleroma.Factory describe "GET /api/v1/polls/:id" do - test "returns poll entity for object id", %{conn: conn} do - user = insert(:user) + setup do: oauth_access(["read:statuses"]) + test "returns poll entity for object id", %{user: user, conn: conn} do {:ok, activity} = CommonAPI.post(user, %{ "status" => "Pleroma does", @@ -22,10 +22,7 @@ test "returns poll entity for object id", %{conn: conn} do object = Object.normalize(activity) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/polls/#{object.id}") + conn = get(conn, "/api/v1/polls/#{object.id}") response = json_response(conn, 200) id = to_string(object.id) @@ -33,11 +30,10 @@ test "returns poll entity for object id", %{conn: conn} do end test "does not expose polls for private statuses", %{conn: conn} do - user = insert(:user) other_user = insert(:user) {:ok, activity} = - CommonAPI.post(user, %{ + CommonAPI.post(other_user, %{ "status" => "Pleroma does", "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}, "visibility" => "private" @@ -45,22 +41,20 @@ test "does not expose polls for private statuses", %{conn: conn} do object = Object.normalize(activity) - conn = - conn - |> assign(:user, other_user) - |> get("/api/v1/polls/#{object.id}") + conn = get(conn, "/api/v1/polls/#{object.id}") assert json_response(conn, 404) end end describe "POST /api/v1/polls/:id/votes" do + setup do: oauth_access(["write:statuses"]) + test "votes are added to the poll", %{conn: conn} do - user = insert(:user) other_user = insert(:user) {:ok, activity} = - CommonAPI.post(user, %{ + CommonAPI.post(other_user, %{ "status" => "A very delicious sandwich", "poll" => %{ "options" => ["Lettuce", "Grilled Bacon", "Tomato"], @@ -71,10 +65,7 @@ test "votes are added to the poll", %{conn: conn} do object = Object.normalize(activity) - conn = - conn - |> assign(:user, other_user) - |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]}) + conn = post(conn, "/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]}) assert json_response(conn, 200) object = Object.get_by_id(object.id) @@ -84,9 +75,7 @@ test "votes are added to the poll", %{conn: conn} do end) end - test "author can't vote", %{conn: conn} do - user = insert(:user) - + test "author can't vote", %{user: user, conn: conn} do {:ok, activity} = CommonAPI.post(user, %{ "status" => "Am I cute?", @@ -96,7 +85,6 @@ test "author can't vote", %{conn: conn} do object = Object.normalize(activity) assert conn - |> assign(:user, user) |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]}) |> json_response(422) == %{"error" => "Poll's author can't vote"} @@ -106,11 +94,10 @@ test "author can't vote", %{conn: conn} do end test "does not allow multiple choices on a single-choice question", %{conn: conn} do - user = insert(:user) other_user = insert(:user) {:ok, activity} = - CommonAPI.post(user, %{ + CommonAPI.post(other_user, %{ "status" => "The glass is", "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20} }) @@ -118,7 +105,6 @@ test "does not allow multiple choices on a single-choice question", %{conn: conn object = Object.normalize(activity) assert conn - |> assign(:user, other_user) |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]}) |> json_response(422) == %{"error" => "Too many choices"} @@ -130,42 +116,32 @@ test "does not allow multiple choices on a single-choice question", %{conn: conn end test "does not allow choice index to be greater than options count", %{conn: conn} do - user = insert(:user) other_user = insert(:user) {:ok, activity} = - CommonAPI.post(user, %{ + CommonAPI.post(other_user, %{ "status" => "Am I cute?", "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20} }) object = Object.normalize(activity) - conn = - conn - |> assign(:user, other_user) - |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]}) + conn = post(conn, "/api/v1/polls/#{object.id}/votes", %{"choices" => [2]}) assert json_response(conn, 422) == %{"error" => "Invalid indices"} end test "returns 404 error when object is not exist", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/polls/1/votes", %{"choices" => [0]}) + conn = post(conn, "/api/v1/polls/1/votes", %{"choices" => [0]}) assert json_response(conn, 404) == %{"error" => "Record not found"} end test "returns 404 when poll is private and not available for user", %{conn: conn} do - user = insert(:user) other_user = insert(:user) {:ok, activity} = - CommonAPI.post(user, %{ + CommonAPI.post(other_user, %{ "status" => "Am I cute?", "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}, "visibility" => "private" @@ -173,10 +149,7 @@ test "returns 404 when poll is private and not available for user", %{conn: conn object = Object.normalize(activity) - conn = - conn - |> assign(:user, other_user) - |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]}) + conn = post(conn, "/api/v1/polls/#{object.id}/votes", %{"choices" => [0]}) assert json_response(conn, 404) == %{"error" => "Record not found"} end diff --git a/test/web/mastodon_api/controllers/report_controller_test.exs b/test/web/mastodon_api/controllers/report_controller_test.exs index 979ca48f3..53c132ff4 100644 --- a/test/web/mastodon_api/controllers/report_controller_test.exs +++ b/test/web/mastodon_api/controllers/report_controller_test.exs @@ -9,32 +9,30 @@ defmodule Pleroma.Web.MastodonAPI.ReportControllerTest do import Pleroma.Factory + setup do: oauth_access(["write:reports"]) + setup do - reporter = insert(:user) target_user = insert(:user) {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"}) - [reporter: reporter, target_user: target_user, activity: activity] + [target_user: target_user, activity: activity] end - test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do + test "submit a basic report", %{conn: conn, target_user: target_user} do assert %{"action_taken" => false, "id" => _} = conn - |> assign(:user, reporter) |> post("/api/v1/reports", %{"account_id" => target_user.id}) |> json_response(200) end test "submit a report with statuses and comment", %{ conn: conn, - reporter: reporter, target_user: target_user, activity: activity } do assert %{"action_taken" => false, "id" => _} = conn - |> assign(:user, reporter) |> post("/api/v1/reports", %{ "account_id" => target_user.id, "status_ids" => [activity.id], @@ -46,19 +44,16 @@ test "submit a report with statuses and comment", %{ test "account_id is required", %{ conn: conn, - reporter: reporter, activity: activity } do assert %{"error" => "Valid `account_id` required"} = conn - |> assign(:user, reporter) |> post("/api/v1/reports", %{"status_ids" => [activity.id]}) |> json_response(400) end test "comment must be up to the size specified in the config", %{ conn: conn, - reporter: reporter, target_user: target_user } do max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000) @@ -68,20 +63,15 @@ test "comment must be up to the size specified in the config", %{ assert ^error = conn - |> assign(:user, reporter) |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment}) |> json_response(400) end test "returns error when account is not exist", %{ conn: conn, - reporter: reporter, activity: activity } do - conn = - conn - |> assign(:user, reporter) - |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"}) + conn = post(conn, "/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"}) assert json_response(conn, 400) == %{"error" => "Account not found"} end diff --git a/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs b/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs index ae5fee2bc..9666a7f2e 100644 --- a/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs +++ b/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs @@ -10,89 +10,69 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do import Pleroma.Factory - test "shows scheduled activities", %{conn: conn} do - user = insert(:user) + test "shows scheduled activities" do + %{user: user, conn: conn} = oauth_access(["read:statuses"]) + scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string() scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string() scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string() scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string() - conn = - conn - |> assign(:user, user) - # min_id - conn_res = - conn - |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}") + conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}") result = json_response(conn_res, 200) assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result # since_id - conn_res = - conn - |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}") + conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}") result = json_response(conn_res, 200) assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result # max_id - conn_res = - conn - |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}") + conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}") result = json_response(conn_res, 200) assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result end - test "shows a scheduled activity", %{conn: conn} do - user = insert(:user) + test "shows a scheduled activity" do + %{user: user, conn: conn} = oauth_access(["read:statuses"]) scheduled_activity = insert(:scheduled_activity, user: user) - res_conn = - conn - |> assign(:user, user) - |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}") + res_conn = get(conn, "/api/v1/scheduled_statuses/#{scheduled_activity.id}") assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200) assert scheduled_activity_id == scheduled_activity.id |> to_string() - res_conn = - conn - |> assign(:user, user) - |> get("/api/v1/scheduled_statuses/404") + res_conn = get(conn, "/api/v1/scheduled_statuses/404") assert %{"error" => "Record not found"} = json_response(res_conn, 404) end - test "updates a scheduled activity", %{conn: conn} do - user = insert(:user) + test "updates a scheduled activity" do + %{user: user, conn: conn} = oauth_access(["write:statuses"]) scheduled_activity = insert(:scheduled_activity, user: user) new_scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) res_conn = - conn - |> assign(:user, user) - |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{ + put(conn, "/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{ scheduled_at: new_scheduled_at }) assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200) assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at) - res_conn = - conn - |> assign(:user, user) - |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at}) + res_conn = put(conn, "/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at}) assert %{"error" => "Record not found"} = json_response(res_conn, 404) end - test "deletes a scheduled activity", %{conn: conn} do - user = insert(:user) + test "deletes a scheduled activity" do + %{user: user, conn: conn} = oauth_access(["write:statuses"]) scheduled_activity = insert(:scheduled_activity, user: user) res_conn = diff --git a/test/web/mastodon_api/controllers/search_controller_test.exs b/test/web/mastodon_api/controllers/search_controller_test.exs index 34deeba47..7fedf42e5 100644 --- a/test/web/mastodon_api/controllers/search_controller_test.exs +++ b/test/web/mastodon_api/controllers/search_controller_test.exs @@ -77,13 +77,11 @@ test "search", %{conn: conn} do describe ".account_search" do test "account search", %{conn: conn} do - user = insert(:user) user_two = insert(:user, %{nickname: "shp@shitposter.club"}) user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) results = conn - |> assign(:user, user) |> get("/api/v1/accounts/search", %{"q" => "shp"}) |> json_response(200) @@ -94,7 +92,6 @@ test "account search", %{conn: conn} do results = conn - |> assign(:user, user) |> get("/api/v1/accounts/search", %{"q" => "2hu"}) |> json_response(200) @@ -104,11 +101,10 @@ test "account search", %{conn: conn} do end test "returns account if query contains a space", %{conn: conn} do - user = insert(:user, %{nickname: "shp@shitposter.club"}) + insert(:user, %{nickname: "shp@shitposter.club"}) results = conn - |> assign(:user, user) |> get("/api/v1/accounts/search", %{"q" => "shp@shitposter.club xxx "}) |> json_response(200) @@ -209,6 +205,7 @@ test "search fetches remote accounts", %{conn: conn} do conn = conn |> assign(:user, user) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"])) |> get("/api/v1/search", %{"q" => "mike@osada.macgirvin.com", "resolve" => "true"}) assert results = json_response(conn, 200) diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index 5fbe947ba..307221c5d 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -23,24 +23,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do clear_config([:instance, :allow_relay]) describe "posting statuses" do - setup do - user = insert(:user) - - conn = - build_conn() - |> assign(:user, user) - - [conn: conn] - end + setup do: oauth_access(["write:statuses"]) test "posting a status does not increment reblog_count when relaying", %{conn: conn} do Pleroma.Config.put([:instance, :federating], true) Pleroma.Config.get([:instance, :allow_relay], true) - user = insert(:user) response = conn - |> assign(:user, user) |> post("api/v1/statuses", %{ "content_type" => "text/plain", "source" => "Pleroma FE", @@ -54,7 +44,6 @@ test "posting a status does not increment reblog_count when relaying", %{conn: c response = conn - |> assign(:user, user) |> get("api/v1/statuses/#{response["id"]}", %{}) |> json_response(200) @@ -132,9 +121,7 @@ test "posting a status", %{conn: conn} do NaiveDateTime.to_iso8601(expiration.scheduled_at) end - test "posting an undefined status with an attachment", %{conn: conn} do - user = insert(:user) - + test "posting an undefined status with an attachment", %{user: user, conn: conn} do file = %Plug.Upload{ content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), @@ -144,17 +131,14 @@ test "posting an undefined status with an attachment", %{conn: conn} do {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{ + post(conn, "/api/v1/statuses", %{ "media_ids" => [to_string(upload.id)] }) assert json_response(conn, 200) end - test "replying to a status", %{conn: conn} do - user = insert(:user) + test "replying to a status", %{user: user, conn: conn} do {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"}) conn = @@ -169,8 +153,10 @@ test "replying to a status", %{conn: conn} do assert Activity.get_in_reply_to_activity(activity).id == replied_to.id end - test "replying to a direct message with visibility other than direct", %{conn: conn} do - user = insert(:user) + test "replying to a direct message with visibility other than direct", %{ + user: user, + conn: conn + } do {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"}) Enum.each(["public", "private", "unlisted"], fn visibility -> @@ -187,18 +173,14 @@ test "replying to a direct message with visibility other than direct", %{conn: c end test "posting a status with an invalid in_reply_to_id", %{conn: conn} do - conn = - conn - |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""}) + conn = post(conn, "/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""}) assert %{"content" => "xD", "id" => id} = json_response(conn, 200) assert Activity.get_by_id(id) end test "posting a sensitive status", %{conn: conn} do - conn = - conn - |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true}) + conn = post(conn, "/api/v1/statuses", %{"status" => "cofe", "sensitive" => true}) assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200) assert Activity.get_by_id(id) @@ -206,8 +188,7 @@ test "posting a sensitive status", %{conn: conn} do test "posting a fake status", %{conn: conn} do real_conn = - conn - |> post("/api/v1/statuses", %{ + post(conn, "/api/v1/statuses", %{ "status" => "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it" }) @@ -226,8 +207,7 @@ test "posting a fake status", %{conn: conn} do |> Kernel.put_in(["pleroma", "conversation_id"], nil) fake_conn = - conn - |> post("/api/v1/statuses", %{ + post(conn, "/api/v1/statuses", %{ "status" => "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it", "preview" => true @@ -254,8 +234,7 @@ test "posting a status with OGP link preview", %{conn: conn} do Config.put([:rich_media, :enabled], true) conn = - conn - |> post("/api/v1/statuses", %{ + post(conn, "/api/v1/statuses", %{ "status" => "https://example.com/ogp" }) @@ -267,9 +246,7 @@ test "posting a direct status", %{conn: conn} do user2 = insert(:user) content = "direct cofe @#{user2.nickname}" - conn = - conn - |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"}) + conn = post(conn, "api/v1/statuses", %{"status" => content, "visibility" => "direct"}) assert %{"id" => id} = response = json_response(conn, 200) assert response["visibility"] == "direct" @@ -282,14 +259,13 @@ test "posting a direct status", %{conn: conn} do end describe "posting scheduled statuses" do + setup do: oauth_access(["write:statuses"]) + test "creates a scheduled activity", %{conn: conn} do - user = insert(:user) scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{ + post(conn, "/api/v1/statuses", %{ "status" => "scheduled", "scheduled_at" => scheduled_at }) @@ -299,8 +275,7 @@ test "creates a scheduled activity", %{conn: conn} do assert [] == Repo.all(Activity) end - test "creates a scheduled activity with a media attachment", %{conn: conn} do - user = insert(:user) + test "creates a scheduled activity with a media attachment", %{user: user, conn: conn} do scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) file = %Plug.Upload{ @@ -312,9 +287,7 @@ test "creates a scheduled activity with a media attachment", %{conn: conn} do {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{ + post(conn, "/api/v1/statuses", %{ "media_ids" => [to_string(upload.id)], "status" => "scheduled", "scheduled_at" => scheduled_at @@ -326,15 +299,11 @@ test "creates a scheduled activity with a media attachment", %{conn: conn} do test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now", %{conn: conn} do - user = insert(:user) - scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond) conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{ + post(conn, "/api/v1/statuses", %{ "status" => "not scheduled", "scheduled_at" => scheduled_at }) @@ -343,9 +312,7 @@ test "skips the scheduling and creates the activity if scheduled_at is earlier t assert [] == Repo.all(ScheduledActivity) end - test "returns error when daily user limit is exceeded", %{conn: conn} do - user = insert(:user) - + test "returns error when daily user limit is exceeded", %{user: user, conn: conn} do today = NaiveDateTime.utc_now() |> NaiveDateTime.add(:timer.minutes(6), :millisecond) @@ -355,17 +322,12 @@ test "returns error when daily user limit is exceeded", %{conn: conn} do {:ok, _} = ScheduledActivity.create(user, attrs) {:ok, _} = ScheduledActivity.create(user, attrs) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today}) + conn = post(conn, "/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today}) assert %{"error" => "daily limit exceeded"} == json_response(conn, 422) end - test "returns error when total user limit is exceeded", %{conn: conn} do - user = insert(:user) - + test "returns error when total user limit is exceeded", %{user: user, conn: conn} do today = NaiveDateTime.utc_now() |> NaiveDateTime.add(:timer.minutes(6), :millisecond) @@ -382,23 +344,20 @@ test "returns error when total user limit is exceeded", %{conn: conn} do {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow}) conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow}) + post(conn, "/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow}) assert %{"error" => "total limit exceeded"} == json_response(conn, 422) end end describe "posting polls" do + setup do: oauth_access(["write:statuses"]) + test "posting a poll", %{conn: conn} do - user = insert(:user) time = NaiveDateTime.utc_now() conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{ + post(conn, "/api/v1/statuses", %{ "status" => "Who is the #bestgrill?", "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420} }) @@ -414,13 +373,10 @@ test "posting a poll", %{conn: conn} do end test "option limit is enforced", %{conn: conn} do - user = insert(:user) limit = Config.get([:instance, :poll_limits, :max_options]) conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{ + post(conn, "/api/v1/statuses", %{ "status" => "desu~", "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1} }) @@ -430,13 +386,10 @@ test "option limit is enforced", %{conn: conn} do end test "option character limit is enforced", %{conn: conn} do - user = insert(:user) limit = Config.get([:instance, :poll_limits, :max_option_chars]) conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{ + post(conn, "/api/v1/statuses", %{ "status" => "...", "poll" => %{ "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)], @@ -449,13 +402,10 @@ test "option character limit is enforced", %{conn: conn} do end test "minimal date limit is enforced", %{conn: conn} do - user = insert(:user) limit = Config.get([:instance, :poll_limits, :min_expiration]) conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{ + post(conn, "/api/v1/statuses", %{ "status" => "imagine arbitrary limits", "poll" => %{ "options" => ["this post was made by pleroma gang"], @@ -468,13 +418,10 @@ test "minimal date limit is enforced", %{conn: conn} do end test "maximum date limit is enforced", %{conn: conn} do - user = insert(:user) limit = Config.get([:instance, :poll_limits, :max_expiration]) conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{ + post(conn, "/api/v1/statuses", %{ "status" => "imagine arbitrary limits", "poll" => %{ "options" => ["this post was made by pleroma gang"], @@ -487,19 +434,18 @@ test "maximum date limit is enforced", %{conn: conn} do end end - test "get a status", %{conn: conn} do + test "get a status" do + %{conn: conn} = oauth_access(["read:statuses"]) activity = insert(:note_activity) - conn = - conn - |> get("/api/v1/statuses/#{activity.id}") + conn = get(conn, "/api/v1/statuses/#{activity.id}") assert %{"id" => id} = json_response(conn, 200) assert id == to_string(activity.id) end - test "get a direct status", %{conn: conn} do - user = insert(:user) + test "get a direct status" do + %{user: user, conn: conn} = oauth_access(["read:statuses"]) other_user = insert(:user) {:ok, activity} = @@ -516,7 +462,8 @@ test "get a direct status", %{conn: conn} do assert res["pleroma"]["direct_conversation_id"] == participation.id end - test "get statuses by IDs", %{conn: conn} do + test "get statuses by IDs" do + %{conn: conn} = oauth_access(["read:statuses"]) %{id: id1} = insert(:note_activity) %{id: id2} = insert(:note_activity) @@ -527,9 +474,9 @@ test "get statuses by IDs", %{conn: conn} do end describe "deleting a status" do - test "when you created it", %{conn: conn} do - activity = insert(:note_activity) - author = User.get_cached_by_ap_id(activity.data["actor"]) + test "when you created it" do + %{user: author, conn: conn} = oauth_access(["write:statuses"]) + activity = insert(:note_activity, user: author) conn = conn @@ -541,14 +488,11 @@ test "when you created it", %{conn: conn} do refute Activity.get_by_id(activity.id) end - test "when you didn't create it", %{conn: conn} do + test "when you didn't create it" do + %{conn: conn} = oauth_access(["write:statuses"]) activity = insert(:note_activity) - user = insert(:user) - conn = - conn - |> assign(:user, user) - |> delete("/api/v1/statuses/#{activity.id}") + conn = delete(conn, "/api/v1/statuses/#{activity.id}") assert %{"error" => _} = json_response(conn, 403) @@ -564,6 +508,7 @@ test "when you're an admin or moderator", %{conn: conn} do res_conn = conn |> assign(:user, admin) + |> assign(:token, insert(:oauth_token, user: admin, scopes: ["write:statuses"])) |> delete("/api/v1/statuses/#{activity1.id}") assert %{} = json_response(res_conn, 200) @@ -571,6 +516,7 @@ test "when you're an admin or moderator", %{conn: conn} do res_conn = conn |> assign(:user, moderator) + |> assign(:token, insert(:oauth_token, user: moderator, scopes: ["write:statuses"])) |> delete("/api/v1/statuses/#{activity2.id}") assert %{} = json_response(res_conn, 200) @@ -581,14 +527,12 @@ test "when you're an admin or moderator", %{conn: conn} do end describe "reblogging" do + setup do: oauth_access(["write:statuses"]) + test "reblogs and returns the reblogged status", %{conn: conn} do activity = insert(:note_activity) - user = insert(:user) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/reblog") + conn = post(conn, "/api/v1/statuses/#{activity.id}/reblog") assert %{ "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}, @@ -600,12 +544,8 @@ test "reblogs and returns the reblogged status", %{conn: conn} do test "reblogs privately and returns the reblogged status", %{conn: conn} do activity = insert(:note_activity) - user = insert(:user) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/reblog", %{"visibility" => "private"}) + conn = post(conn, "/api/v1/statuses/#{activity.id}/reblog", %{"visibility" => "private"}) assert %{ "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}, @@ -616,7 +556,7 @@ test "reblogs privately and returns the reblogged status", %{conn: conn} do assert to_string(activity.id) == id end - test "reblogged status for another user", %{conn: conn} do + test "reblogged status for another user" do activity = insert(:note_activity) user1 = insert(:user) user2 = insert(:user) @@ -627,8 +567,9 @@ test "reblogged status for another user", %{conn: conn} do {:ok, _, _object} = CommonAPI.repeat(activity.id, user2) conn_res = - conn + build_conn() |> assign(:user, user3) + |> assign(:token, insert(:oauth_token, user: user3, scopes: ["read:statuses"])) |> get("/api/v1/statuses/#{reblog_activity1.id}") assert %{ @@ -639,8 +580,9 @@ test "reblogged status for another user", %{conn: conn} do } = json_response(conn_res, 200) conn_res = - conn + build_conn() |> assign(:user, user2) + |> assign(:token, insert(:oauth_token, user: user2, scopes: ["read:statuses"])) |> get("/api/v1/statuses/#{reblog_activity1.id}") assert %{ @@ -654,28 +596,21 @@ test "reblogged status for another user", %{conn: conn} do end test "returns 400 error when activity is not exist", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/foo/reblog") + conn = post(conn, "/api/v1/statuses/foo/reblog") assert json_response(conn, 400) == %{"error" => "Could not repeat"} end end describe "unreblogging" do - test "unreblogs and returns the unreblogged status", %{conn: conn} do + setup do: oauth_access(["write:statuses"]) + + test "unreblogs and returns the unreblogged status", %{user: user, conn: conn} do activity = insert(:note_activity) - user = insert(:user) {:ok, _, _} = CommonAPI.repeat(activity.id, user) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/unreblog") + conn = post(conn, "/api/v1/statuses/#{activity.id}/unreblog") assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200) @@ -683,26 +618,19 @@ test "unreblogs and returns the unreblogged status", %{conn: conn} do end test "returns 400 error when activity is not exist", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/foo/unreblog") + conn = post(conn, "/api/v1/statuses/foo/unreblog") assert json_response(conn, 400) == %{"error" => "Could not unrepeat"} end end describe "favoriting" do + setup do: oauth_access(["write:favourites"]) + test "favs a status and returns it", %{conn: conn} do activity = insert(:note_activity) - user = insert(:user) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/favourite") + conn = post(conn, "/api/v1/statuses/#{activity.id}/favourite") assert %{"id" => id, "favourites_count" => 1, "favourited" => true} = json_response(conn, 200) @@ -711,28 +639,21 @@ test "favs a status and returns it", %{conn: conn} do end test "returns 400 error for a wrong id", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/1/favourite") + conn = post(conn, "/api/v1/statuses/1/favourite") assert json_response(conn, 400) == %{"error" => "Could not favorite"} end end describe "unfavoriting" do - test "unfavorites a status and returns it", %{conn: conn} do + setup do: oauth_access(["write:favourites"]) + + test "unfavorites a status and returns it", %{user: user, conn: conn} do activity = insert(:note_activity) - user = insert(:user) {:ok, _, _} = CommonAPI.favorite(activity.id, user) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/unfavourite") + conn = post(conn, "/api/v1/statuses/#{activity.id}/unfavourite") assert %{"id" => id, "favourites_count" => 0, "favourited" => false} = json_response(conn, 200) @@ -741,23 +662,19 @@ test "unfavorites a status and returns it", %{conn: conn} do end test "returns 400 error for a wrong id", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/1/unfavourite") + conn = post(conn, "/api/v1/statuses/1/unfavourite") assert json_response(conn, 400) == %{"error" => "Could not unfavorite"} end end describe "pinned statuses" do - setup do - user = insert(:user) + setup do: oauth_access(["write:accounts"]) + + setup %{user: user} do {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"}) - [user: user, activity: activity] + %{activity: activity} end clear_config([:instance, :max_pinned_statuses]) do @@ -769,13 +686,11 @@ test "pin status", %{conn: conn, user: user, activity: activity} do assert %{"id" => ^id_str, "pinned" => true} = conn - |> assign(:user, user) |> post("/api/v1/statuses/#{activity.id}/pin") |> json_response(200) assert [%{"id" => ^id_str, "pinned" => true}] = conn - |> assign(:user, user) |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") |> json_response(200) end @@ -783,19 +698,16 @@ test "pin status", %{conn: conn, user: user, activity: activity} do test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"}) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{dm.id}/pin") + conn = post(conn, "/api/v1/statuses/#{dm.id}/pin") assert json_response(conn, 400) == %{"error" => "Could not pin"} end test "unpin status", %{conn: conn, user: user, activity: activity} do {:ok, _} = CommonAPI.pin(activity.id, user) + user = refresh_record(user) id_str = to_string(activity.id) - user = refresh_record(user) assert %{"id" => ^id_str, "pinned" => false} = conn @@ -805,16 +717,12 @@ test "unpin status", %{conn: conn, user: user, activity: activity} do assert [] = conn - |> assign(:user, user) |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") |> json_response(200) end - test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/1/unpin") + test "/unpin: returns 400 error when activity is not exist", %{conn: conn} do + conn = post(conn, "/api/v1/statuses/1/unpin") assert json_response(conn, 400) == %{"error" => "Could not unpin"} end @@ -826,7 +734,6 @@ test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do assert %{"id" => ^id_str_one, "pinned" => true} = conn - |> assign(:user, user) |> post("/api/v1/statuses/#{id_str_one}/pin") |> json_response(200) @@ -844,8 +751,7 @@ test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do setup do Config.put([:rich_media, :enabled], true) - user = insert(:user) - %{user: user} + oauth_access(["read:statuses"]) end test "returns rich-media card", %{conn: conn, user: user} do @@ -887,7 +793,6 @@ test "returns rich-media card", %{conn: conn, user: user} do response_two = conn - |> assign(:user, user) |> get("/api/v1/statuses/#{activity.id}/card") |> json_response(200) @@ -925,72 +830,55 @@ test "replaces missing description with an empty string", %{conn: conn, user: us end test "bookmarks" do - user = insert(:user) - for_user = insert(:user) + %{conn: conn} = oauth_access(["write:bookmarks", "read:bookmarks"]) + author = insert(:user) {:ok, activity1} = - CommonAPI.post(user, %{ + CommonAPI.post(author, %{ "status" => "heweoo?" }) {:ok, activity2} = - CommonAPI.post(user, %{ + CommonAPI.post(author, %{ "status" => "heweoo!" }) - response1 = - build_conn() - |> assign(:user, for_user) - |> post("/api/v1/statuses/#{activity1.id}/bookmark") + response1 = post(conn, "/api/v1/statuses/#{activity1.id}/bookmark") assert json_response(response1, 200)["bookmarked"] == true - response2 = - build_conn() - |> assign(:user, for_user) - |> post("/api/v1/statuses/#{activity2.id}/bookmark") + response2 = post(conn, "/api/v1/statuses/#{activity2.id}/bookmark") assert json_response(response2, 200)["bookmarked"] == true - bookmarks = - build_conn() - |> assign(:user, for_user) - |> get("/api/v1/bookmarks") + bookmarks = get(conn, "/api/v1/bookmarks") assert [json_response(response2, 200), json_response(response1, 200)] == json_response(bookmarks, 200) - response1 = - build_conn() - |> assign(:user, for_user) - |> post("/api/v1/statuses/#{activity1.id}/unbookmark") + response1 = post(conn, "/api/v1/statuses/#{activity1.id}/unbookmark") assert json_response(response1, 200)["bookmarked"] == false - bookmarks = - build_conn() - |> assign(:user, for_user) - |> get("/api/v1/bookmarks") + bookmarks = get(conn, "/api/v1/bookmarks") assert [json_response(response2, 200)] == json_response(bookmarks, 200) end describe "conversation muting" do + setup do: oauth_access(["write:mutes"]) + setup do post_user = insert(:user) - user = insert(:user) - {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"}) - - [user: user, activity: activity] + %{activity: activity} end - test "mute conversation", %{conn: conn, user: user, activity: activity} do + test "mute conversation", %{conn: conn, activity: activity} do id_str = to_string(activity.id) assert %{"id" => ^id_str, "muted" => true} = conn - |> assign(:user, user) |> post("/api/v1/statuses/#{activity.id}/mute") |> json_response(200) end @@ -998,10 +886,7 @@ test "mute conversation", %{conn: conn, user: user, activity: activity} do test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do {:ok, _} = CommonAPI.add_mute(user, activity) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/mute") + conn = post(conn, "/api/v1/statuses/#{activity.id}/mute") assert json_response(conn, 400) == %{"error" => "conversation is already muted"} end @@ -1010,11 +895,10 @@ test "unmute conversation", %{conn: conn, user: user, activity: activity} do {:ok, _} = CommonAPI.add_mute(user, activity) id_str = to_string(activity.id) - user = refresh_record(user) assert %{"id" => ^id_str, "muted" => false} = conn - |> assign(:user, user) + # |> assign(:user, user) |> post("/api/v1/statuses/#{activity.id}/unmute") |> json_response(200) end @@ -1031,6 +915,7 @@ test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{c conn1 = conn |> assign(:user, user2) + |> assign(:token, insert(:oauth_token, user: user2, scopes: ["write:statuses"])) |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id}) assert %{"content" => "xD", "id" => id} = json_response(conn1, 200) @@ -1044,6 +929,7 @@ test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{c conn2 = conn |> assign(:user, user3) + |> assign(:token, insert(:oauth_token, user: user3, scopes: ["write:statuses"])) |> post("/api/v1/statuses/#{activity.id}/reblog") assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} = @@ -1055,6 +941,7 @@ test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{c conn3 = conn |> assign(:user, user3) + |> assign(:token, insert(:oauth_token, user: user3, scopes: ["read:statuses"])) |> get("api/v1/timelines/home") [reblogged_activity] = json_response(conn3, 200) @@ -1066,15 +953,12 @@ test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{c end describe "GET /api/v1/statuses/:id/favourited_by" do - setup do - user = insert(:user) + setup do: oauth_access(["read:accounts"]) + + setup %{user: user} do {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) - conn = - build_conn() - |> assign(:user, user) - - [conn: conn, activity: activity, user: user] + %{activity: activity} end test "returns users who have favorited the status", %{conn: conn, activity: activity} do @@ -1114,20 +998,18 @@ test "does not return users who have favorited the status but are blocked", %{ response = conn - |> assign(:user, user) |> get("/api/v1/statuses/#{activity.id}/favourited_by") |> json_response(:ok) assert Enum.empty?(response) end - test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do + test "does not fail on an unauthenticated request", %{activity: activity} do other_user = insert(:user) {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) response = - conn - |> assign(:user, nil) + build_conn() |> get("/api/v1/statuses/#{activity.id}/favourited_by") |> json_response(:ok) @@ -1135,7 +1017,7 @@ test "does not fail on an unauthenticated request", %{conn: conn, activity: acti assert id == other_user.id end - test "requires authentification for private posts", %{conn: conn, user: user} do + test "requires authentication for private posts", %{user: user} do other_user = insert(:user) {:ok, activity} = @@ -1146,15 +1028,25 @@ test "requires authentification for private posts", %{conn: conn, user: user} do {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) + favourited_by_url = "/api/v1/statuses/#{activity.id}/favourited_by" + + build_conn() + |> get(favourited_by_url) + |> json_response(404) + + conn = + build_conn() + |> assign(:user, other_user) + |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"])) + conn - |> assign(:user, nil) - |> get("/api/v1/statuses/#{activity.id}/favourited_by") + |> assign(:token, nil) + |> get(favourited_by_url) |> json_response(404) response = - build_conn() - |> assign(:user, other_user) - |> get("/api/v1/statuses/#{activity.id}/favourited_by") + conn + |> get(favourited_by_url) |> json_response(200) [%{"id" => id}] = response @@ -1163,15 +1055,12 @@ test "requires authentification for private posts", %{conn: conn, user: user} do end describe "GET /api/v1/statuses/:id/reblogged_by" do - setup do - user = insert(:user) + setup do: oauth_access(["read:accounts"]) + + setup %{user: user} do {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) - conn = - build_conn() - |> assign(:user, user) - - [conn: conn, activity: activity, user: user] + %{activity: activity} end test "returns users who have reblogged the status", %{conn: conn, activity: activity} do @@ -1211,7 +1100,6 @@ test "does not return users who have reblogged the status but are blocked", %{ response = conn - |> assign(:user, user) |> get("/api/v1/statuses/#{activity.id}/reblogged_by") |> json_response(:ok) @@ -1219,7 +1107,7 @@ test "does not return users who have reblogged the status but are blocked", %{ end test "does not return users who have reblogged the status privately", %{ - conn: %{assigns: %{user: user}} = conn, + conn: conn, activity: activity } do other_user = insert(:user) @@ -1228,20 +1116,18 @@ test "does not return users who have reblogged the status privately", %{ response = conn - |> assign(:user, user) |> get("/api/v1/statuses/#{activity.id}/reblogged_by") |> json_response(:ok) assert Enum.empty?(response) end - test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do + test "does not fail on an unauthenticated request", %{activity: activity} do other_user = insert(:user) {:ok, _, _} = CommonAPI.repeat(activity.id, other_user) response = - conn - |> assign(:user, nil) + build_conn() |> get("/api/v1/statuses/#{activity.id}/reblogged_by") |> json_response(:ok) @@ -1249,7 +1135,7 @@ test "does not fail on an unauthenticated request", %{conn: conn, activity: acti assert id == other_user.id end - test "requires authentification for private posts", %{conn: conn, user: user} do + test "requires authentication for private posts", %{user: user} do other_user = insert(:user) {:ok, activity} = @@ -1258,14 +1144,14 @@ test "requires authentification for private posts", %{conn: conn, user: user} do "visibility" => "direct" }) - conn - |> assign(:user, nil) + build_conn() |> get("/api/v1/statuses/#{activity.id}/reblogged_by") |> json_response(404) response = build_conn() |> assign(:user, other_user) + |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"])) |> get("/api/v1/statuses/#{activity.id}/reblogged_by") |> json_response(200) @@ -1284,7 +1170,6 @@ test "context" do response = build_conn() - |> assign(:user, nil) |> get("/api/v1/statuses/#{id3}/context") |> json_response(:ok) @@ -1294,8 +1179,8 @@ test "context" do } = response end - test "returns the favorites of a user", %{conn: conn} do - user = insert(:user) + test "returns the favorites of a user" do + %{user: user, conn: conn} = oauth_access(["read:favourites"]) other_user = insert(:user) {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"}) @@ -1303,10 +1188,7 @@ test "returns the favorites of a user", %{conn: conn} do {:ok, _, _} = CommonAPI.favorite(activity.id, user) - first_conn = - conn - |> assign(:user, user) - |> get("/api/v1/favourites") + first_conn = get(conn, "/api/v1/favourites") assert [status] = json_response(first_conn, 200) assert status["id"] == to_string(activity.id) @@ -1325,18 +1207,12 @@ test "returns the favorites of a user", %{conn: conn} do last_like = status["id"] - second_conn = - conn - |> assign(:user, user) - |> get("/api/v1/favourites?since_id=#{last_like}") + second_conn = get(conn, "/api/v1/favourites?since_id=#{last_like}") assert [second_status] = json_response(second_conn, 200) assert second_status["id"] == to_string(second_activity.id) - third_conn = - conn - |> assign(:user, user) - |> get("/api/v1/favourites?limit=0") + third_conn = get(conn, "/api/v1/favourites?limit=0") assert [] = json_response(third_conn, 200) end diff --git a/test/web/mastodon_api/controllers/suggestion_controller_test.exs b/test/web/mastodon_api/controllers/suggestion_controller_test.exs index 78620a873..c4118a576 100644 --- a/test/web/mastodon_api/controllers/suggestion_controller_test.exs +++ b/test/web/mastodon_api/controllers/suggestion_controller_test.exs @@ -11,8 +11,9 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do import Pleroma.Factory import Tesla.Mock - setup do - user = insert(:user) + setup do: oauth_access(["read"]) + + setup %{user: user} do other_user = insert(:user) host = Config.get([Pleroma.Web.Endpoint, :url, :host]) url500 = "http://test500?#{host}&#{user.nickname}" @@ -32,31 +33,29 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do } end) - [user: user, other_user: other_user] + [other_user: other_user] end clear_config(:suggestions) - test "returns empty result when suggestions disabled", %{conn: conn, user: user} do + test "returns empty result when suggestions disabled", %{conn: conn} do Config.put([:suggestions, :enabled], false) res = conn - |> assign(:user, user) |> get("/api/v1/suggestions") |> json_response(200) assert res == [] end - test "returns error", %{conn: conn, user: user} do + test "returns error", %{conn: conn} do Config.put([:suggestions, :enabled], true) Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}") assert capture_log(fn -> res = conn - |> assign(:user, user) |> get("/api/v1/suggestions") |> json_response(500) @@ -64,13 +63,12 @@ test "returns error", %{conn: conn, user: user} do end) =~ "Could not retrieve suggestions" end - test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do + test "returns suggestions", %{conn: conn, other_user: other_user} do Config.put([:suggestions, :enabled], true) Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}") res = conn - |> assign(:user, user) |> get("/api/v1/suggestions") |> json_response(200) diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs index dc17cc963..bb94d8e5a 100644 --- a/test/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs @@ -20,31 +20,25 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do end describe "home" do - test "the home timeline", %{conn: conn} do - user = insert(:user) + setup do: oauth_access(["read:statuses"]) + + test "the home timeline", %{user: user, conn: conn} do following = insert(:user) {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"}) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/timelines/home") + ret_conn = get(conn, "/api/v1/timelines/home") - assert Enum.empty?(json_response(conn, :ok)) + assert Enum.empty?(json_response(ret_conn, :ok)) - {:ok, user} = User.follow(user, following) + {:ok, _user} = User.follow(user, following) - conn = - build_conn() - |> assign(:user, user) - |> get("/api/v1/timelines/home") + conn = get(conn, "/api/v1/timelines/home") assert [%{"content" => "test"}] = json_response(conn, :ok) end - test "the home timeline when the direct messages are excluded", %{conn: conn} do - user = insert(:user) + test "the home timeline when the direct messages are excluded", %{user: user, conn: conn} do {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"}) {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"}) @@ -54,10 +48,7 @@ test "the home timeline when the direct messages are excluded", %{conn: conn} do {:ok, private_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"}) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/timelines/home", %{"exclude_visibilities" => ["direct"]}) + conn = get(conn, "/api/v1/timelines/home", %{"exclude_visibilities" => ["direct"]}) assert status_ids = json_response(conn, :ok) |> Enum.map(& &1["id"]) assert public_activity.id in status_ids @@ -99,11 +90,7 @@ test "the public timeline when public is set to false", %{conn: conn} do end test "the public timeline includes only public statuses for an authenticated user" do - user = insert(:user) - - conn = - build_conn() - |> assign(:user, user) + %{user: user, conn: conn} = oauth_access(["read:statuses"]) {:ok, _activity} = CommonAPI.post(user, %{"status" => "test"}) {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "private"}) @@ -134,11 +121,13 @@ test "direct timeline", %{conn: conn} do "visibility" => "private" }) - # Only direct should be visible here - res_conn = + conn_user_two = conn |> assign(:user, user_two) - |> get("api/v1/timelines/direct") + |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"])) + + # Only direct should be visible here + res_conn = get(conn_user_two, "api/v1/timelines/direct") [status] = json_response(res_conn, :ok) @@ -149,6 +138,7 @@ test "direct timeline", %{conn: conn} do res_conn = build_conn() |> assign(:user, user_one) + |> assign(:token, insert(:oauth_token, user: user_one, scopes: ["read:statuses"])) |> get("api/v1/timelines/direct") [status] = json_response(res_conn, :ok) @@ -156,10 +146,7 @@ test "direct timeline", %{conn: conn} do assert %{"visibility" => "direct"} = status # Both should be visible here - res_conn = - conn - |> assign(:user, user_two) - |> get("api/v1/timelines/home") + res_conn = get(conn_user_two, "api/v1/timelines/home") [_s1, _s2] = json_response(res_conn, :ok) @@ -172,28 +159,23 @@ test "direct timeline", %{conn: conn} do }) end) - res_conn = - conn - |> assign(:user, user_two) - |> get("api/v1/timelines/direct") + res_conn = get(conn_user_two, "api/v1/timelines/direct") statuses = json_response(res_conn, :ok) assert length(statuses) == 20 res_conn = - conn - |> assign(:user, user_two) - |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]}) + get(conn_user_two, "api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]}) [status] = json_response(res_conn, :ok) assert status["url"] != direct.data["id"] end - test "doesn't include DMs from blocked users", %{conn: conn} do - blocker = insert(:user) + test "doesn't include DMs from blocked users" do + %{user: blocker, conn: conn} = oauth_access(["read:statuses"]) blocked = insert(:user) - user = insert(:user) + other_user = insert(:user) {:ok, _user_relationship} = User.block(blocker, blocked) {:ok, _blocked_direct} = @@ -203,15 +185,12 @@ test "doesn't include DMs from blocked users", %{conn: conn} do }) {:ok, direct} = - CommonAPI.post(user, %{ + CommonAPI.post(other_user, %{ "status" => "Hi @#{blocker.nickname}!", "visibility" => "direct" }) - res_conn = - conn - |> assign(:user, user) - |> get("api/v1/timelines/direct") + res_conn = get(conn, "api/v1/timelines/direct") [status] = json_response(res_conn, :ok) assert status["id"] == direct.id @@ -219,26 +198,26 @@ test "doesn't include DMs from blocked users", %{conn: conn} do end describe "list" do - test "list timeline", %{conn: conn} do - user = insert(:user) + setup do: oauth_access(["read:lists"]) + + test "list timeline", %{user: user, conn: conn} do other_user = insert(:user) {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."}) {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."}) {:ok, list} = Pleroma.List.create("name", user) {:ok, list} = Pleroma.List.follow(list, other_user) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/timelines/list/#{list.id}") + conn = get(conn, "/api/v1/timelines/list/#{list.id}") assert [%{"id" => id}] = json_response(conn, :ok) assert id == to_string(activity_two.id) end - test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do - user = insert(:user) + test "list timeline does not leak non-public statuses for unfollowed users", %{ + user: user, + conn: conn + } do other_user = insert(:user) {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."}) @@ -251,10 +230,7 @@ test "list timeline does not leak non-public statuses for unfollowed users", %{c {:ok, list} = Pleroma.List.create("name", user) {:ok, list} = Pleroma.List.follow(list, other_user) - conn = - conn - |> assign(:user, user) - |> get("/api/v1/timelines/list/#{list.id}") + conn = get(conn, "/api/v1/timelines/list/#{list.id}") assert [%{"id" => id}] = json_response(conn, :ok) @@ -263,6 +239,8 @@ test "list timeline does not leak non-public statuses for unfollowed users", %{c end describe "hashtag" do + setup do: oauth_access(["n/a"]) + @tag capture_log: true test "hashtag timeline", %{conn: conn} do following = insert(:user) diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 42a8779c0..c1f70f9fe 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -5,69 +5,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do use Pleroma.Web.ConnCase - alias Pleroma.Notification - alias Pleroma.Repo - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - import Tesla.Mock - - setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - clear_config([:rich_media, :enabled]) - - test "unimplemented follow_requests, blocks, domain blocks" do - user = insert(:user) - - ["blocks", "domain_blocks", "follow_requests"] - |> Enum.each(fn endpoint -> - conn = - build_conn() - |> assign(:user, user) - |> get("/api/v1/#{endpoint}") - - assert [] = json_response(conn, 200) - end) - end - - describe "link headers" do - test "preserves parameters in link headers", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity1} = - CommonAPI.post(other_user, %{ - "status" => "hi @#{user.nickname}", - "visibility" => "public" - }) - - {:ok, activity2} = - CommonAPI.post(other_user, %{ - "status" => "hi @#{user.nickname}", - "visibility" => "public" - }) - - notification1 = Repo.get_by(Notification, activity_id: activity1.id) - notification2 = Repo.get_by(Notification, activity_id: activity2.id) - - conn = - conn - |> assign(:user, user) - |> get("/api/v1/notifications", %{media_only: true}) - - assert [link_header] = get_resp_header(conn, "link") - assert link_header =~ ~r/media_only=true/ - assert link_header =~ ~r/min_id=#{notification2.id}/ - assert link_header =~ ~r/max_id=#{notification1.id}/ - end - end - - describe "empty_array, stubs for mastodon api" do - test "GET /api/v1/accounts/:id/identity_proofs", %{conn: conn} do - user = insert(:user) + describe "empty_array/2 (stubs)" do + test "GET /api/v1/accounts/:id/identity_proofs" do + %{user: user, conn: conn} = oauth_access(["n/a"]) res = conn @@ -78,12 +18,11 @@ test "GET /api/v1/accounts/:id/identity_proofs", %{conn: conn} do assert res == [] end - test "GET /api/v1/endorsements", %{conn: conn} do - user = insert(:user) + test "GET /api/v1/endorsements" do + %{conn: conn} = oauth_access(["read:accounts"]) res = conn - |> assign(:user, user) |> get("/api/v1/endorsements") |> json_response(200) @@ -91,11 +30,8 @@ test "GET /api/v1/endorsements", %{conn: conn} do end test "GET /api/v1/trends", %{conn: conn} do - user = insert(:user) - res = conn - |> assign(:user, user) |> get("/api/v1/trends") |> json_response(200) diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs index bdd87a79e..17b6ebcbc 100644 --- a/test/web/mastodon_api/views/status_view_test.exs +++ b/test/web/mastodon_api/views/status_view_test.exs @@ -394,6 +394,21 @@ test "a peertube video" do assert length(represented[:media_attachments]) == 1 end + test "a Mobilizon event" do + user = insert(:user) + + {:ok, object} = + Pleroma.Object.Fetcher.fetch_object_from_id( + "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" + ) + + %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"]) + + represented = StatusView.render("show.json", %{for: user, activity: activity}) + + assert represented[:id] == to_string(activity.id) + end + describe "build_tags/1" do test "it returns a a dictionary tags" do object_tags = [ diff --git a/test/web/metadata/twitter_card_test.exs b/test/web/metadata/twitter_card_test.exs index 0814006d2..85a654f52 100644 --- a/test/web/metadata/twitter_card_test.exs +++ b/test/web/metadata/twitter_card_test.exs @@ -26,7 +26,32 @@ test "it renders twitter card for user info" do ] end - test "it does not render attachments if post is nsfw" do + test "it uses summary twittercard if post has no attachment" do + user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") + {:ok, activity} = CommonAPI.post(user, %{"status" => "HI"}) + + note = + insert(:note, %{ + data: %{ + "actor" => user.ap_id, + "tag" => [], + "id" => "https://pleroma.gov/objects/whatever", + "content" => "pleroma in a nutshell" + } + }) + + result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id}) + + assert [ + {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []}, + {:meta, [property: "twitter:description", content: "“pleroma in a nutshell”"], []}, + {:meta, [property: "twitter:image", content: "http://localhost:4001/images/avi.png"], + []}, + {:meta, [property: "twitter:card", content: "summary"], []} + ] == result + end + + test "it renders avatar not attachment if post is nsfw and unfurl_nsfw is disabled" do Pleroma.Config.put([Pleroma.Web.Metadata, :unfurl_nsfw], false) user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") {:ok, activity} = CommonAPI.post(user, %{"status" => "HI"}) @@ -67,7 +92,7 @@ test "it does not render attachments if post is nsfw" do {:meta, [property: "twitter:description", content: "“pleroma in a nutshell”"], []}, {:meta, [property: "twitter:image", content: "http://localhost:4001/images/avi.png"], []}, - {:meta, [property: "twitter:card", content: "summary_large_image"], []} + {:meta, [property: "twitter:card", content: "summary"], []} ] == result end diff --git a/test/web/metadata/utils_test.exs b/test/web/metadata/utils_test.exs new file mode 100644 index 000000000..7547f2932 --- /dev/null +++ b/test/web/metadata/utils_test.exs @@ -0,0 +1,32 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.UtilsTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Web.Metadata.Utils + + describe "scrub_html_and_truncate/1" do + test "it returns text without encode HTML" do + user = insert(:user) + + note = + insert(:note, %{ + data: %{ + "actor" => user.ap_id, + "id" => "https://pleroma.gov/objects/whatever", + "content" => "Pleroma's really cool!" + } + }) + + assert Utils.scrub_html_and_truncate(note) == "Pleroma's really cool!" + end + end + + describe "scrub_html_and_truncate/2" do + test "it returns text without encode HTML" do + assert Utils.scrub_html_and_truncate("Pleroma's really cool!") == "Pleroma's really cool!" + end + end +end diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs index 901f2ae41..9cc534f57 100644 --- a/test/web/oauth/oauth_controller_test.exs +++ b/test/web/oauth/oauth_controller_test.exs @@ -450,7 +450,7 @@ test "properly handles internal calls with `authorization`-wrapped params", %{ test "renders authentication page if user is already authenticated but `force_login` is tru-ish", %{app: app, conn: conn} do - token = insert(:oauth_token, app_id: app.id) + token = insert(:oauth_token, app: app) conn = conn @@ -474,7 +474,7 @@ test "renders authentication page if user is already authenticated but user requ app: app, conn: conn } do - token = insert(:oauth_token, app_id: app.id) + token = insert(:oauth_token, app: app) conn = conn @@ -497,7 +497,7 @@ test "with existing authentication and non-OOB `redirect_uri`, redirects to app app: app, conn: conn } do - token = insert(:oauth_token, app_id: app.id) + token = insert(:oauth_token, app: app) conn = conn @@ -523,7 +523,7 @@ test "with existing authentication and unlisted non-OOB `redirect_uri`, redirect conn: conn } do unlisted_redirect_uri = "http://cross-site-request.com" - token = insert(:oauth_token, app_id: app.id) + token = insert(:oauth_token, app: app) conn = conn @@ -547,7 +547,7 @@ test "with existing authentication and OOB `redirect_uri`, redirects to app with app: app, conn: conn } do - token = insert(:oauth_token, app_id: app.id) + token = insert(:oauth_token, app: app) conn = conn diff --git a/test/web/pleroma_api/controllers/account_controller_test.exs b/test/web/pleroma_api/controllers/account_controller_test.exs index c809f510f..d17026a6b 100644 --- a/test/web/pleroma_api/controllers/account_controller_test.exs +++ b/test/web/pleroma_api/controllers/account_controller_test.exs @@ -33,7 +33,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do test "resend account confirmation email", %{conn: conn, user: user} do conn - |> assign(:user, user) |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}") |> json_response(:no_content) @@ -52,14 +51,12 @@ test "resend account confirmation email", %{conn: conn, user: user} do end describe "PATCH /api/v1/pleroma/accounts/update_avatar" do - test "user avatar can be set", %{conn: conn} do - user = insert(:user) + setup do: oauth_access(["write:accounts"]) + + test "user avatar can be set", %{user: user, conn: conn} do avatar_image = File.read!("test/fixtures/avatar_data_uri") - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image}) + conn = patch(conn, "/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image}) user = refresh_record(user) @@ -78,13 +75,8 @@ test "user avatar can be set", %{conn: conn} do assert %{"url" => _} = json_response(conn, 200) end - test "user avatar can be reset", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""}) + test "user avatar can be reset", %{user: user, conn: conn} do + conn = patch(conn, "/api/v1/pleroma/accounts/update_avatar", %{img: ""}) user = User.get_cached_by_id(user.id) @@ -95,13 +87,10 @@ test "user avatar can be reset", %{conn: conn} do end describe "PATCH /api/v1/pleroma/accounts/update_banner" do - test "can set profile banner", %{conn: conn} do - user = insert(:user) + setup do: oauth_access(["write:accounts"]) - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image}) + test "can set profile banner", %{user: user, conn: conn} do + conn = patch(conn, "/api/v1/pleroma/accounts/update_banner", %{"banner" => @image}) user = refresh_record(user) assert user.banner["type"] == "Image" @@ -109,13 +98,8 @@ test "can set profile banner", %{conn: conn} do assert %{"url" => _} = json_response(conn, 200) end - test "can reset profile banner", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""}) + test "can reset profile banner", %{user: user, conn: conn} do + conn = patch(conn, "/api/v1/pleroma/accounts/update_banner", %{"banner" => ""}) user = refresh_record(user) assert user.banner == %{} @@ -125,26 +109,18 @@ test "can reset profile banner", %{conn: conn} do end describe "PATCH /api/v1/pleroma/accounts/update_background" do - test "background image can be set", %{conn: conn} do - user = insert(:user) + setup do: oauth_access(["write:accounts"]) - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image}) + test "background image can be set", %{user: user, conn: conn} do + conn = patch(conn, "/api/v1/pleroma/accounts/update_background", %{"img" => @image}) user = refresh_record(user) assert user.background["type"] == "Image" assert %{"url" => _} = json_response(conn, 200) end - test "background image can be reset", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""}) + test "background image can be reset", %{user: user, conn: conn} do + conn = patch(conn, "/api/v1/pleroma/accounts/update_background", %{"img" => ""}) user = refresh_record(user) assert user.background == %{} @@ -155,12 +131,12 @@ test "background image can be reset", %{conn: conn} do describe "getting favorites timeline of specified user" do setup do [current_user, user] = insert_pair(:user, hide_favorites: false) - [current_user: current_user, user: user] + %{user: current_user, conn: conn} = oauth_access(["read:favourites"], user: current_user) + [current_user: current_user, user: user, conn: conn] end test "returns list of statuses favorited by specified user", %{ conn: conn, - current_user: current_user, user: user } do [activity | _] = insert_pair(:note_activity) @@ -168,7 +144,6 @@ test "returns list of statuses favorited by specified user", %{ response = conn - |> assign(:user, current_user) |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") |> json_response(:ok) @@ -178,23 +153,18 @@ test "returns list of statuses favorited by specified user", %{ assert like["id"] == activity.id end - test "returns favorites for specified user_id when user is not logged in", %{ - conn: conn, + test "does not return favorites for specified user_id when user is not logged in", %{ user: user } do activity = insert(:note_activity) CommonAPI.favorite(activity.id, user) - response = - conn - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response(:ok) - - assert length(response) == 1 + build_conn() + |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") + |> json_response(403) end test "returns favorited DM only when user is logged in and he is one of recipients", %{ - conn: conn, current_user: current_user, user: user } do @@ -206,25 +176,24 @@ test "returns favorited DM only when user is logged in and he is one of recipien CommonAPI.favorite(direct.id, user) - response = - conn - |> assign(:user, current_user) - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response(:ok) + for u <- [user, current_user] do + response = + build_conn() + |> assign(:user, u) + |> assign(:token, insert(:oauth_token, user: u, scopes: ["read:favourites"])) + |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") + |> json_response(:ok) - assert length(response) == 1 + assert length(response) == 1 + end - anonymous_response = - conn - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response(:ok) - - assert Enum.empty?(anonymous_response) + build_conn() + |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") + |> json_response(403) end test "does not return others' favorited DM when user is not one of recipients", %{ conn: conn, - current_user: current_user, user: user } do user_two = insert(:user) @@ -239,7 +208,6 @@ test "does not return others' favorited DM when user is not one of recipients", response = conn - |> assign(:user, current_user) |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") |> json_response(:ok) @@ -248,7 +216,6 @@ test "does not return others' favorited DM when user is not one of recipients", test "paginates favorites using since_id and max_id", %{ conn: conn, - current_user: current_user, user: user } do activities = insert_list(10, :note_activity) @@ -262,7 +229,6 @@ test "paginates favorites using since_id and max_id", %{ response = conn - |> assign(:user, current_user) |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{ since_id: third_activity.id, max_id: seventh_activity.id @@ -276,7 +242,6 @@ test "paginates favorites using since_id and max_id", %{ test "limits favorites using limit parameter", %{ conn: conn, - current_user: current_user, user: user } do 7 @@ -287,7 +252,6 @@ test "limits favorites using limit parameter", %{ response = conn - |> assign(:user, current_user) |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"}) |> json_response(:ok) @@ -296,12 +260,10 @@ test "limits favorites using limit parameter", %{ test "returns empty response when user does not have any favorited statuses", %{ conn: conn, - current_user: current_user, user: user } do response = conn - |> assign(:user, current_user) |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") |> json_response(:ok) @@ -314,79 +276,61 @@ test "returns 404 error when specified user is not exist", %{conn: conn} do assert json_response(conn, 404) == %{"error" => "Record not found"} end - test "returns 403 error when user has hidden own favorites", %{ - conn: conn, - current_user: current_user - } do + test "returns 403 error when user has hidden own favorites", %{conn: conn} do user = insert(:user, hide_favorites: true) activity = insert(:note_activity) CommonAPI.favorite(activity.id, user) - conn = - conn - |> assign(:user, current_user) - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") + conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites") assert json_response(conn, 403) == %{"error" => "Can't get favorites"} end - test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do + test "hides favorites for new users by default", %{conn: conn} do user = insert(:user) activity = insert(:note_activity) CommonAPI.favorite(activity.id, user) - conn = - conn - |> assign(:user, current_user) - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - assert user.hide_favorites + conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites") + assert json_response(conn, 403) == %{"error" => "Can't get favorites"} end end describe "subscribing / unsubscribing" do - test "subscribing / unsubscribing to a user", %{conn: conn} do - user = insert(:user) + test "subscribing / unsubscribing to a user" do + %{user: user, conn: conn} = oauth_access(["follow"]) subscription_target = insert(:user) - conn = + ret_conn = conn |> assign(:user, user) |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe") - assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200) + assert %{"id" => _id, "subscribing" => true} = json_response(ret_conn, 200) - conn = - build_conn() - |> assign(:user, user) - |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe") + conn = post(conn, "/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe") assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200) end end describe "subscribing" do - test "returns 404 when subscription_target not found", %{conn: conn} do - user = insert(:user) + test "returns 404 when subscription_target not found" do + %{conn: conn} = oauth_access(["write:follows"]) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/pleroma/accounts/target_id/subscribe") + conn = post(conn, "/api/v1/pleroma/accounts/target_id/subscribe") assert %{"error" => "Record not found"} = json_response(conn, 404) end end describe "unsubscribing" do - test "returns 404 when subscription_target not found", %{conn: conn} do - user = insert(:user) + test "returns 404 when subscription_target not found" do + %{conn: conn} = oauth_access(["follow"]) - conn = - conn - |> assign(:user, user) - |> post("/api/v1/pleroma/accounts/target_id/unsubscribe") + conn = post(conn, "/api/v1/pleroma/accounts/target_id/unsubscribe") assert %{"error" => "Record not found"} = json_response(conn, 404) end diff --git a/test/web/pleroma_api/controllers/emoji_api_controller_test.exs b/test/web/pleroma_api/controllers/emoji_api_controller_test.exs index 3d3becefd..e1b484dae 100644 --- a/test/web/pleroma_api/controllers/emoji_api_controller_test.exs +++ b/test/web/pleroma_api/controllers/emoji_api_controller_test.exs @@ -39,9 +39,12 @@ test "shared & non-shared pack information in list_packs is ok" do test "listing remote packs" do admin = insert(:user, is_admin: true) - conn = build_conn() |> assign(:user, admin) + %{conn: conn} = oauth_access(["admin:write"], user: admin) - resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200) + resp = + build_conn() + |> get(emoji_api_path(conn, :list_packs)) + |> json_response(200) mock(fn %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> @@ -123,7 +126,10 @@ test "downloading shared & unshared packs from another instance via download_fro admin = insert(:user, is_admin: true) - conn = build_conn() |> assign(:user, admin) + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, insert(:oauth_admin_token, user: admin, scopes: ["admin:write"])) assert (conn |> put_req_header("content-type", "application/json") @@ -168,8 +174,6 @@ test "downloading shared & unshared packs from another instance via download_fro # non-shared, downloaded from the fallback URL - conn = build_conn() |> assign(:user, admin) - assert conn |> put_req_header("content-type", "application/json") |> post( @@ -205,8 +209,12 @@ test "downloading shared & unshared packs from another instance via download_fro File.write!(pack_file, original_content) end) + admin = insert(:user, is_admin: true) + %{conn: conn} = oauth_access(["admin:write"], user: admin) + {:ok, - admin: insert(:user, is_admin: true), + admin: admin, + conn: conn, pack_file: pack_file, new_data: %{ "license" => "Test license changed", @@ -217,10 +225,9 @@ test "downloading shared & unshared packs from another instance via download_fro end test "for a pack without a fallback source", ctx do - conn = build_conn() + conn = ctx[:conn] assert conn - |> assign(:user, ctx[:admin]) |> post( emoji_api_path(conn, :update_metadata, "test_pack"), %{ @@ -250,10 +257,9 @@ test "for a pack with a fallback source", ctx do "74409E2674DAA06C072729C6C8426C4CB3B7E0B85ED77792DB7A436E11D76DAF" ) - conn = build_conn() + conn = ctx[:conn] assert conn - |> assign(:user, ctx[:admin]) |> post( emoji_api_path(conn, :update_metadata, "test_pack"), %{ @@ -277,10 +283,9 @@ test "when the fallback source doesn't have all the files", ctx do new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack") - conn = build_conn() + conn = ctx[:conn] assert (conn - |> assign(:user, ctx[:admin]) |> post( emoji_api_path(conn, :update_metadata, "test_pack"), %{ @@ -304,8 +309,7 @@ test "updating pack files" do end) admin = insert(:user, is_admin: true) - - conn = build_conn() + %{conn: conn} = oauth_access(["admin:write"], user: admin) same_name = %{ "action" => "add", @@ -319,8 +323,6 @@ test "updating pack files" do different_name = %{same_name | "shortcode" => "blank_2"} - conn = conn |> assign(:user, admin) - assert (conn |> post(emoji_api_path(conn, :update_file, "test_pack"), same_name) |> json_response(:conflict))["error"] =~ "already exists" @@ -392,8 +394,7 @@ test "creating and deleting a pack" do end) admin = insert(:user, is_admin: true) - - conn = build_conn() |> assign(:user, admin) + %{conn: conn} = oauth_access(["admin:write"], user: admin) assert conn |> put_req_header("content-type", "application/json") @@ -432,9 +433,9 @@ test "filesystem import" do refute Map.has_key?(resp, "test_pack_for_import") admin = insert(:user, is_admin: true) + %{conn: conn} = oauth_access(["admin:write"], user: admin) assert conn - |> assign(:user, admin) |> post(emoji_api_path(conn, :import_from_fs)) |> json_response(200) == ["test_pack_for_import"] @@ -449,11 +450,10 @@ test "filesystem import" do File.write!("#{@emoji_dir_path}/test_pack_for_import/emoji.txt", emoji_txt_content) assert conn - |> assign(:user, admin) |> post(emoji_api_path(conn, :import_from_fs)) |> json_response(200) == ["test_pack_for_import"] - resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200) + resp = build_conn() |> get(emoji_api_path(conn, :list_packs)) |> json_response(200) assert resp["test_pack_for_import"]["files"] == %{ "blank" => "blank.png", diff --git a/test/web/pleroma_api/controllers/mascot_controller_test.exs b/test/web/pleroma_api/controllers/mascot_controller_test.exs index ae9539b04..40c33e609 100644 --- a/test/web/pleroma_api/controllers/mascot_controller_test.exs +++ b/test/web/pleroma_api/controllers/mascot_controller_test.exs @@ -7,10 +7,8 @@ defmodule Pleroma.Web.PleromaAPI.MascotControllerTest do alias Pleroma.User - import Pleroma.Factory - - test "mascot upload", %{conn: conn} do - user = insert(:user) + test "mascot upload" do + %{conn: conn} = oauth_access(["write:accounts"]) non_image_file = %Plug.Upload{ content_type: "audio/mpeg", @@ -18,12 +16,9 @@ test "mascot upload", %{conn: conn} do filename: "sound.mp3" } - conn = - conn - |> assign(:user, user) - |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file}) + ret_conn = put(conn, "/api/v1/pleroma/mascot", %{"file" => non_image_file}) - assert json_response(conn, 415) + assert json_response(ret_conn, 415) file = %Plug.Upload{ content_type: "image/jpg", @@ -31,23 +26,18 @@ test "mascot upload", %{conn: conn} do filename: "an_image.jpg" } - conn = - build_conn() - |> assign(:user, user) - |> put("/api/v1/pleroma/mascot", %{"file" => file}) + conn = put(conn, "/api/v1/pleroma/mascot", %{"file" => file}) assert %{"id" => _, "type" => image} = json_response(conn, 200) end - test "mascot retrieving", %{conn: conn} do - user = insert(:user) - # When user hasn't set a mascot, we should just get pleroma tan back - conn = - conn - |> assign(:user, user) - |> get("/api/v1/pleroma/mascot") + test "mascot retrieving" do + %{user: user, conn: conn} = oauth_access(["read:accounts", "write:accounts"]) - assert %{"url" => url} = json_response(conn, 200) + # When user hasn't set a mascot, we should just get pleroma tan back + ret_conn = get(conn, "/api/v1/pleroma/mascot") + + assert %{"url" => url} = json_response(ret_conn, 200) assert url =~ "pleroma-fox-tan-smol" # When a user sets their mascot, we should get that back @@ -57,17 +47,14 @@ test "mascot retrieving", %{conn: conn} do filename: "an_image.jpg" } - conn = - build_conn() - |> assign(:user, user) - |> put("/api/v1/pleroma/mascot", %{"file" => file}) + ret_conn = put(conn, "/api/v1/pleroma/mascot", %{"file" => file}) - assert json_response(conn, 200) + assert json_response(ret_conn, 200) user = User.get_cached_by_id(user.id) conn = - build_conn() + conn |> assign(:user, user) |> get("/api/v1/pleroma/mascot") diff --git a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs index b1b59beed..3f7ef13bc 100644 --- a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs +++ b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs @@ -23,6 +23,7 @@ test "POST /api/v1/pleroma/statuses/:id/react_with_emoji", %{conn: conn} do result = conn |> assign(:user, other_user) + |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"])) |> post("/api/v1/pleroma/statuses/#{activity.id}/react_with_emoji", %{"emoji" => "☕"}) assert %{"id" => id} = json_response(result, 200) @@ -39,6 +40,7 @@ test "POST /api/v1/pleroma/statuses/:id/unreact_with_emoji", %{conn: conn} do result = conn |> assign(:user, other_user) + |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"])) |> post("/api/v1/pleroma/statuses/#{activity.id}/unreact_with_emoji", %{"emoji" => "☕"}) assert %{"id" => id} = json_response(result, 200) @@ -55,6 +57,11 @@ test "GET /api/v1/pleroma/statuses/:id/emoji_reactions_by", %{conn: conn} do {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"}) + conn = + conn + |> assign(:user, user) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["read:statuses"])) + result = conn |> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by") @@ -73,9 +80,9 @@ test "GET /api/v1/pleroma/statuses/:id/emoji_reactions_by", %{conn: conn} do assert represented_user["id"] == other_user.id end - test "/api/v1/pleroma/conversations/:id", %{conn: conn} do + test "/api/v1/pleroma/conversations/:id" do user = insert(:user) - other_user = insert(:user) + %{user: other_user, conn: conn} = oauth_access(["read:statuses"]) {:ok, _activity} = CommonAPI.post(user, %{"status" => "Hi @#{other_user.nickname}!", "visibility" => "direct"}) @@ -84,16 +91,15 @@ test "/api/v1/pleroma/conversations/:id", %{conn: conn} do result = conn - |> assign(:user, other_user) |> get("/api/v1/pleroma/conversations/#{participation.id}") |> json_response(200) assert result["id"] == participation.id |> to_string() end - test "/api/v1/pleroma/conversations/:id/statuses", %{conn: conn} do + test "/api/v1/pleroma/conversations/:id/statuses" do user = insert(:user) - other_user = insert(:user) + %{user: other_user, conn: conn} = oauth_access(["read:statuses"]) third_user = insert(:user) {:ok, _activity} = @@ -113,7 +119,6 @@ test "/api/v1/pleroma/conversations/:id/statuses", %{conn: conn} do result = conn - |> assign(:user, other_user) |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses") |> json_response(200) @@ -124,8 +129,8 @@ test "/api/v1/pleroma/conversations/:id/statuses", %{conn: conn} do assert [%{"id" => ^id_one}, %{"id" => ^id_two}] = result end - test "PATCH /api/v1/pleroma/conversations/:id", %{conn: conn} do - user = insert(:user) + test "PATCH /api/v1/pleroma/conversations/:id" do + %{user: user, conn: conn} = oauth_access(["write:conversations"]) other_user = insert(:user) {:ok, _activity} = CommonAPI.post(user, %{"status" => "Hi", "visibility" => "direct"}) @@ -140,7 +145,6 @@ test "PATCH /api/v1/pleroma/conversations/:id", %{conn: conn} do result = conn - |> assign(:user, user) |> patch("/api/v1/pleroma/conversations/#{participation.id}", %{ "recipients" => [user.id, other_user.id] }) @@ -155,9 +159,9 @@ test "PATCH /api/v1/pleroma/conversations/:id", %{conn: conn} do assert other_user in participation.recipients end - test "POST /api/v1/pleroma/conversations/read", %{conn: conn} do + test "POST /api/v1/pleroma/conversations/read" do user = insert(:user) - other_user = insert(:user) + %{user: other_user, conn: conn} = oauth_access(["write:notifications"]) {:ok, _activity} = CommonAPI.post(user, %{"status" => "Hi @#{other_user.nickname}", "visibility" => "direct"}) @@ -172,7 +176,6 @@ test "POST /api/v1/pleroma/conversations/read", %{conn: conn} do [%{"unread" => false}, %{"unread" => false}] = conn - |> assign(:user, other_user) |> post("/api/v1/pleroma/conversations/read", %{}) |> json_response(200) @@ -183,8 +186,9 @@ test "POST /api/v1/pleroma/conversations/read", %{conn: conn} do end describe "POST /api/v1/pleroma/notifications/read" do - test "it marks a single notification as read", %{conn: conn} do - user1 = insert(:user) + setup do: oauth_access(["write:notifications"]) + + test "it marks a single notification as read", %{user: user1, conn: conn} do user2 = insert(:user) {:ok, activity1} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"}) {:ok, activity2} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"}) @@ -193,7 +197,6 @@ test "it marks a single notification as read", %{conn: conn} do response = conn - |> assign(:user, user1) |> post("/api/v1/pleroma/notifications/read", %{"id" => "#{notification1.id}"}) |> json_response(:ok) @@ -202,8 +205,7 @@ test "it marks a single notification as read", %{conn: conn} do refute Repo.get(Notification, notification2.id).seen end - test "it marks multiple notifications as read", %{conn: conn} do - user1 = insert(:user) + test "it marks multiple notifications as read", %{user: user1, conn: conn} do user2 = insert(:user) {:ok, _activity1} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"}) {:ok, _activity2} = CommonAPI.post(user2, %{"status" => "hi @#{user1.nickname}"}) @@ -213,7 +215,6 @@ test "it marks multiple notifications as read", %{conn: conn} do [response1, response2] = conn - |> assign(:user, user1) |> post("/api/v1/pleroma/notifications/read", %{"max_id" => "#{notification2.id}"}) |> json_response(:ok) @@ -225,11 +226,8 @@ test "it marks multiple notifications as read", %{conn: conn} do end test "it returns error when notification not found", %{conn: conn} do - user1 = insert(:user) - response = conn - |> assign(:user, user1) |> post("/api/v1/pleroma/notifications/read", %{"id" => "22222222222222"}) |> json_response(:bad_request) diff --git a/test/web/pleroma_api/controllers/scrobble_controller_test.exs b/test/web/pleroma_api/controllers/scrobble_controller_test.exs index 881f8012c..2242610f1 100644 --- a/test/web/pleroma_api/controllers/scrobble_controller_test.exs +++ b/test/web/pleroma_api/controllers/scrobble_controller_test.exs @@ -6,16 +6,13 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do use Pleroma.Web.ConnCase alias Pleroma.Web.CommonAPI - import Pleroma.Factory describe "POST /api/v1/pleroma/scrobble" do - test "works correctly", %{conn: conn} do - user = insert(:user) + test "works correctly" do + %{conn: conn} = oauth_access(["write"]) conn = - conn - |> assign(:user, user) - |> post("/api/v1/pleroma/scrobble", %{ + post(conn, "/api/v1/pleroma/scrobble", %{ "title" => "lain radio episode 1", "artist" => "lain", "album" => "lain radio", @@ -27,8 +24,8 @@ test "works correctly", %{conn: conn} do end describe "GET /api/v1/pleroma/accounts/:id/scrobbles" do - test "works correctly", %{conn: conn} do - user = insert(:user) + test "works correctly" do + %{user: user, conn: conn} = oauth_access(["read"]) {:ok, _activity} = CommonAPI.listen(user, %{ @@ -51,9 +48,7 @@ test "works correctly", %{conn: conn} do "album" => "lain radio" }) - conn = - conn - |> get("/api/v1/pleroma/accounts/#{user.id}/scrobbles") + conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/scrobbles") result = json_response(conn, 200) diff --git a/test/web/twitter_api/remote_follow_controller_test.exs b/test/web/twitter_api/remote_follow_controller_test.exs new file mode 100644 index 000000000..444949375 --- /dev/null +++ b/test/web/twitter_api/remote_follow_controller_test.exs @@ -0,0 +1,235 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.User + alias Pleroma.Web.CommonAPI + import ExUnit.CaptureLog + import Pleroma.Factory + + setup do + Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + clear_config([:instance]) + clear_config([:frontend_configurations, :pleroma_fe]) + clear_config([:user, :deny_follow_blocked]) + + describe "GET /ostatus_subscribe - remote_follow/2" do + test "adds status to pleroma instance if the `acct` is a status", %{conn: conn} do + assert conn + |> get( + remote_follow_path(conn, :follow, %{ + acct: "https://mastodon.social/users/emelie/statuses/101849165031453009" + }) + ) + |> redirected_to() =~ "/notice/" + end + + test "show follow account page if the `acct` is a account link", %{conn: conn} do + response = + conn + |> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"})) + |> html_response(200) + + assert response =~ "Log in to follow" + end + + test "show follow page if the `acct` is a account link", %{conn: conn} do + user = insert(:user) + + response = + conn + |> assign(:user, user) + |> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"})) + |> html_response(200) + + assert response =~ "Remote follow" + end + + test "show follow page with error when user cannot fecth by `acct` link", %{conn: conn} do + user = insert(:user) + + assert capture_log(fn -> + response = + conn + |> assign(:user, user) + |> get( + remote_follow_path(conn, :follow, %{ + acct: "https://mastodon.social/users/not_found" + }) + ) + |> html_response(200) + + assert response =~ "Error fetching user" + end) =~ "Object has been deleted" + end + end + + describe "POST /ostatus_subscribe - do_follow/2 with assigned user " do + test "required `follow | write:follows` scope", %{conn: conn} do + user = insert(:user) + user2 = insert(:user) + read_token = insert(:oauth_token, user: user, scopes: ["read"]) + + assert capture_log(fn -> + response = + conn + |> assign(:user, user) + |> assign(:token, read_token) + |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) + |> response(200) + + assert response =~ "Error following account" + end) =~ "Insufficient permissions: follow | write:follows." + end + + test "follows user", %{conn: conn} do + user = insert(:user) + user2 = insert(:user) + + response = + conn + |> assign(:user, user) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"])) + |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) + |> response(200) + + assert response =~ "Account followed!" + assert user2.follower_address in User.following(user) + end + + test "returns error when user is deactivated", %{conn: conn} do + user = insert(:user, deactivated: true) + user2 = insert(:user) + + response = + conn + |> assign(:user, user) + |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) + |> response(200) + + assert response =~ "Error following account" + end + + test "returns error when user is blocked", %{conn: conn} do + Pleroma.Config.put([:user, :deny_follow_blocked], true) + user = insert(:user) + user2 = insert(:user) + + {:ok, _user_block} = Pleroma.User.block(user2, user) + + response = + conn + |> assign(:user, user) + |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) + |> response(200) + + assert response =~ "Error following account" + end + + test "returns error when followee not found", %{conn: conn} do + user = insert(:user) + + response = + conn + |> assign(:user, user) + |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => "jimm"}}) + |> response(200) + + assert response =~ "Error following account" + end + + test "returns success result when user already in followers", %{conn: conn} do + user = insert(:user) + user2 = insert(:user) + {:ok, _, _, _} = CommonAPI.follow(user, user2) + + response = + conn + |> assign(:user, refresh_record(user)) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"])) + |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) + |> response(200) + + assert response =~ "Account followed!" + end + end + + describe "POST /ostatus_subscribe - follow/2 without assigned user " do + test "follows", %{conn: conn} do + user = insert(:user) + user2 = insert(:user) + + response = + conn + |> post(remote_follow_path(conn, :do_follow), %{ + "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id} + }) + |> response(200) + + assert response =~ "Account followed!" + assert user2.follower_address in User.following(user) + end + + test "returns error when followee not found", %{conn: conn} do + user = insert(:user) + + response = + conn + |> post(remote_follow_path(conn, :do_follow), %{ + "authorization" => %{"name" => user.nickname, "password" => "test", "id" => "jimm"} + }) + |> response(200) + + assert response =~ "Error following account" + end + + test "returns error when login invalid", %{conn: conn} do + user = insert(:user) + + response = + conn + |> post(remote_follow_path(conn, :do_follow), %{ + "authorization" => %{"name" => "jimm", "password" => "test", "id" => user.id} + }) + |> response(200) + + assert response =~ "Wrong username or password" + end + + test "returns error when password invalid", %{conn: conn} do + user = insert(:user) + user2 = insert(:user) + + response = + conn + |> post(remote_follow_path(conn, :do_follow), %{ + "authorization" => %{"name" => user.nickname, "password" => "42", "id" => user2.id} + }) + |> response(200) + + assert response =~ "Wrong username or password" + end + + test "returns error when user is blocked", %{conn: conn} do + Pleroma.Config.put([:user, :deny_follow_blocked], true) + user = insert(:user) + user2 = insert(:user) + {:ok, _user_block} = Pleroma.User.block(user2, user) + + response = + conn + |> post(remote_follow_path(conn, :do_follow), %{ + "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id} + }) + |> response(200) + + assert response =~ "Error following account" + end + end +end diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index 43299e147..5d60c0d51 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -6,11 +6,9 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do use Pleroma.Web.ConnCase use Oban.Testing, repo: Pleroma.Repo - alias Pleroma.Repo alias Pleroma.Tests.ObanHelpers alias Pleroma.User - alias Pleroma.Web.CommonAPI - import ExUnit.CaptureLog + import Pleroma.Factory import Mock @@ -24,21 +22,20 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do clear_config([:user, :deny_follow_blocked]) describe "POST /api/pleroma/follow_import" do + setup do: oauth_access(["follow"]) + test "it returns HTTP 200", %{conn: conn} do - user1 = insert(:user) user2 = insert(:user) response = conn - |> assign(:user, user1) |> post("/api/pleroma/follow_import", %{"list" => "#{user2.ap_id}"}) |> json_response(:ok) assert response == "job started" end - test "it imports follow lists from file", %{conn: conn} do - user1 = insert(:user) + test "it imports follow lists from file", %{user: user1, conn: conn} do user2 = insert(:user) with_mocks([ @@ -49,7 +46,6 @@ test "it imports follow lists from file", %{conn: conn} do ]) do response = conn - |> assign(:user, user1) |> post("/api/pleroma/follow_import", %{"list" => %Plug.Upload{path: "follow_list.txt"}}) |> json_response(:ok) @@ -67,12 +63,10 @@ test "it imports follow lists from file", %{conn: conn} do end test "it imports new-style mastodon follow lists", %{conn: conn} do - user1 = insert(:user) user2 = insert(:user) response = conn - |> assign(:user, user1) |> post("/api/pleroma/follow_import", %{ "list" => "Account address,Show boosts\n#{user2.ap_id},true" }) @@ -81,7 +75,7 @@ test "it imports new-style mastodon follow lists", %{conn: conn} do assert response == "job started" end - test "requires 'follow' or 'write:follows' permissions", %{conn: conn} do + test "requires 'follow' or 'write:follows' permissions" do token1 = insert(:oauth_token, scopes: ["read", "write"]) token2 = insert(:oauth_token, scopes: ["follow"]) token3 = insert(:oauth_token, scopes: ["something"]) @@ -89,7 +83,7 @@ test "requires 'follow' or 'write:follows' permissions", %{conn: conn} do for token <- [token1, token2, token3] do conn = - conn + build_conn() |> put_req_header("authorization", "Bearer #{token.token}") |> post("/api/pleroma/follow_import", %{"list" => "#{another_user.ap_id}"}) @@ -104,21 +98,21 @@ test "requires 'follow' or 'write:follows' permissions", %{conn: conn} do end describe "POST /api/pleroma/blocks_import" do + # Note: "follow" or "write:blocks" permission is required + setup do: oauth_access(["write:blocks"]) + test "it returns HTTP 200", %{conn: conn} do - user1 = insert(:user) user2 = insert(:user) response = conn - |> assign(:user, user1) |> post("/api/pleroma/blocks_import", %{"list" => "#{user2.ap_id}"}) |> json_response(:ok) assert response == "job started" end - test "it imports blocks users from file", %{conn: conn} do - user1 = insert(:user) + test "it imports blocks users from file", %{user: user1, conn: conn} do user2 = insert(:user) user3 = insert(:user) @@ -127,7 +121,6 @@ test "it imports blocks users from file", %{conn: conn} do ]) do response = conn - |> assign(:user, user1) |> post("/api/pleroma/blocks_import", %{"list" => %Plug.Upload{path: "blocks_list.txt"}}) |> json_response(:ok) @@ -146,18 +139,17 @@ test "it imports blocks users from file", %{conn: conn} do end describe "PUT /api/pleroma/notification_settings" do - test "it updates notification settings", %{conn: conn} do - user = insert(:user) + setup do: oauth_access(["write:accounts"]) + test "it updates notification settings", %{user: user, conn: conn} do conn - |> assign(:user, user) |> put("/api/pleroma/notification_settings", %{ "followers" => false, "bar" => 1 }) |> json_response(:ok) - user = Repo.get(User, user.id) + user = refresh_record(user) assert %Pleroma.User.NotificationSetting{ followers: false, @@ -168,11 +160,8 @@ test "it updates notification settings", %{conn: conn} do } == user.notification_settings end - test "it update notificatin privacy option", %{conn: conn} do - user = insert(:user) - + test "it updates notification privacy option", %{user: user, conn: conn} do conn - |> assign(:user, user) |> put("/api/pleroma/notification_settings", %{"privacy_option" => "1"}) |> json_response(:ok) @@ -328,196 +317,6 @@ test "returns json with custom emoji with tags", %{conn: conn} do end end - describe "GET /ostatus_subscribe - remote_follow/2" do - test "adds status to pleroma instance if the `acct` is a status", %{conn: conn} do - conn = - get( - conn, - "/ostatus_subscribe?acct=https://mastodon.social/users/emelie/statuses/101849165031453009" - ) - - assert redirected_to(conn) =~ "/notice/" - end - - test "show follow account page if the `acct` is a account link", %{conn: conn} do - response = - get( - conn, - "/ostatus_subscribe?acct=https://mastodon.social/users/emelie" - ) - - assert html_response(response, 200) =~ "Log in to follow" - end - - test "show follow page if the `acct` is a account link", %{conn: conn} do - user = insert(:user) - - response = - conn - |> assign(:user, user) - |> get("/ostatus_subscribe?acct=https://mastodon.social/users/emelie") - - assert html_response(response, 200) =~ "Remote follow" - end - - test "show follow page with error when user cannot fecth by `acct` link", %{conn: conn} do - user = insert(:user) - - assert capture_log(fn -> - response = - conn - |> assign(:user, user) - |> get("/ostatus_subscribe?acct=https://mastodon.social/users/not_found") - - assert html_response(response, 200) =~ "Error fetching user" - end) =~ "Object has been deleted" - end - end - - describe "POST /ostatus_subscribe - do_remote_follow/2 with assigned user " do - test "follows user", %{conn: conn} do - user = insert(:user) - user2 = insert(:user) - - response = - conn - |> assign(:user, user) - |> post("/ostatus_subscribe", %{"user" => %{"id" => user2.id}}) - |> response(200) - - assert response =~ "Account followed!" - assert user2.follower_address in User.following(user) - end - - test "returns error when user is deactivated", %{conn: conn} do - user = insert(:user, deactivated: true) - user2 = insert(:user) - - response = - conn - |> assign(:user, user) - |> post("/ostatus_subscribe", %{"user" => %{"id" => user2.id}}) - |> response(200) - - assert response =~ "Error following account" - end - - test "returns error when user is blocked", %{conn: conn} do - Pleroma.Config.put([:user, :deny_follow_blocked], true) - user = insert(:user) - user2 = insert(:user) - - {:ok, _user_block} = Pleroma.User.block(user2, user) - - response = - conn - |> assign(:user, user) - |> post("/ostatus_subscribe", %{"user" => %{"id" => user2.id}}) - |> response(200) - - assert response =~ "Error following account" - end - - test "returns error when followee not found", %{conn: conn} do - user = insert(:user) - - response = - conn - |> assign(:user, user) - |> post("/ostatus_subscribe", %{"user" => %{"id" => "jimm"}}) - |> response(200) - - assert response =~ "Error following account" - end - - test "returns success result when user already in followers", %{conn: conn} do - user = insert(:user) - user2 = insert(:user) - {:ok, _, _, _} = CommonAPI.follow(user, user2) - - response = - conn - |> assign(:user, refresh_record(user)) - |> post("/ostatus_subscribe", %{"user" => %{"id" => user2.id}}) - |> response(200) - - assert response =~ "Account followed!" - end - end - - describe "POST /ostatus_subscribe - do_remote_follow/2 without assigned user " do - test "follows", %{conn: conn} do - user = insert(:user) - user2 = insert(:user) - - response = - conn - |> post("/ostatus_subscribe", %{ - "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id} - }) - |> response(200) - - assert response =~ "Account followed!" - assert user2.follower_address in User.following(user) - end - - test "returns error when followee not found", %{conn: conn} do - user = insert(:user) - - response = - conn - |> post("/ostatus_subscribe", %{ - "authorization" => %{"name" => user.nickname, "password" => "test", "id" => "jimm"} - }) - |> response(200) - - assert response =~ "Error following account" - end - - test "returns error when login invalid", %{conn: conn} do - user = insert(:user) - - response = - conn - |> post("/ostatus_subscribe", %{ - "authorization" => %{"name" => "jimm", "password" => "test", "id" => user.id} - }) - |> response(200) - - assert response =~ "Wrong username or password" - end - - test "returns error when password invalid", %{conn: conn} do - user = insert(:user) - user2 = insert(:user) - - response = - conn - |> post("/ostatus_subscribe", %{ - "authorization" => %{"name" => user.nickname, "password" => "42", "id" => user2.id} - }) - |> response(200) - - assert response =~ "Wrong username or password" - end - - test "returns error when user is blocked", %{conn: conn} do - Pleroma.Config.put([:user, :deny_follow_blocked], true) - user = insert(:user) - user2 = insert(:user) - {:ok, _user_block} = Pleroma.User.block(user2, user) - - response = - conn - |> post("/ostatus_subscribe", %{ - "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id} - }) - |> response(200) - - assert response =~ "Error following account" - end - end - describe "GET /api/pleroma/healthcheck" do clear_config([:instance, :healthcheck]) @@ -552,7 +351,7 @@ test "returns 200 when healthcheck enabled and all ok", %{conn: conn} do end end - test "returns 503 when healthcheck enabled and health is false", %{conn: conn} do + test "returns 503 when healthcheck enabled and health is false", %{conn: conn} do Pleroma.Config.put([:instance, :healthcheck], true) with_mock Pleroma.Healthcheck, @@ -574,12 +373,11 @@ test "returns 503 when healthcheck enabled and health is false", %{conn: conn} end describe "POST /api/pleroma/disable_account" do - test "it returns HTTP 200", %{conn: conn} do - user = insert(:user) + setup do: oauth_access(["write:accounts"]) + test "with valid permissions and password, it disables the account", %{conn: conn, user: user} do response = conn - |> assign(:user, user) |> post("/api/pleroma/disable_account", %{"password" => "test"}) |> json_response(:ok) @@ -591,12 +389,11 @@ test "it returns HTTP 200", %{conn: conn} do assert user.deactivated == true end - test "it returns returns when password invalid", %{conn: conn} do + test "with valid permissions and invalid password, it returns an error", %{conn: conn} do user = insert(:user) response = conn - |> assign(:user, user) |> post("/api/pleroma/disable_account", %{"password" => "test1"}) |> json_response(:ok) @@ -666,7 +463,7 @@ test "it redirect to webfinger url", %{conn: conn} do "https://social.heldscal.la/main/ostatussub?profile=#{user.ap_id}" end - test "it renders form with error when use not found", %{conn: conn} do + test "it renders form with error when user not found", %{conn: conn} do user2 = insert(:user, ap_id: "shp@social.heldscal.la") response = @@ -691,29 +488,21 @@ test "it returns new captcha", %{conn: conn} do end end - defp with_credentials(conn, username, password) do - header_content = "Basic " <> Base.encode64("#{username}:#{password}") - put_req_header(conn, "authorization", header_content) - end - - defp valid_user(_context) do - user = insert(:user) - [user: user] - end - describe "POST /api/pleroma/change_email" do - setup [:valid_user] + setup do: oauth_access(["write:accounts"]) - test "without credentials", %{conn: conn} do - conn = post(conn, "/api/pleroma/change_email") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials and invalid password", %{conn: conn, user: current_user} do + test "without permissions", %{conn: conn} do conn = conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/change_email", %{ + |> assign(:token, nil) + |> post("/api/pleroma/change_email") + + assert json_response(conn, 403) == %{"error" => "Insufficient permissions: write:accounts."} + end + + test "with proper permissions and invalid password", %{conn: conn} do + conn = + post(conn, "/api/pleroma/change_email", %{ "password" => "hi", "email" => "test@test.com" }) @@ -721,14 +510,11 @@ test "with credentials and invalid password", %{conn: conn, user: current_user} assert json_response(conn, 200) == %{"error" => "Invalid password."} end - test "with credentials, valid password and invalid email", %{ - conn: conn, - user: current_user + test "with proper permissions, valid password and invalid email", %{ + conn: conn } do conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/change_email", %{ + post(conn, "/api/pleroma/change_email", %{ "password" => "test", "email" => "foobar" }) @@ -736,28 +522,22 @@ test "with credentials, valid password and invalid email", %{ assert json_response(conn, 200) == %{"error" => "Email has invalid format."} end - test "with credentials, valid password and no email", %{ - conn: conn, - user: current_user + test "with proper permissions, valid password and no email", %{ + conn: conn } do conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/change_email", %{ + post(conn, "/api/pleroma/change_email", %{ "password" => "test" }) assert json_response(conn, 200) == %{"error" => "Email can't be blank."} end - test "with credentials, valid password and blank email", %{ - conn: conn, - user: current_user + test "with proper permissions, valid password and blank email", %{ + conn: conn } do conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/change_email", %{ + post(conn, "/api/pleroma/change_email", %{ "password" => "test", "email" => "" }) @@ -765,16 +545,13 @@ test "with credentials, valid password and blank email", %{ assert json_response(conn, 200) == %{"error" => "Email can't be blank."} end - test "with credentials, valid password and non unique email", %{ - conn: conn, - user: current_user + test "with proper permissions, valid password and non unique email", %{ + conn: conn } do user = insert(:user) conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/change_email", %{ + post(conn, "/api/pleroma/change_email", %{ "password" => "test", "email" => user.email }) @@ -782,14 +559,11 @@ test "with credentials, valid password and non unique email", %{ assert json_response(conn, 200) == %{"error" => "Email has already been taken."} end - test "with credentials, valid password and valid email", %{ - conn: conn, - user: current_user + test "with proper permissions, valid password and valid email", %{ + conn: conn } do conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/change_email", %{ + post(conn, "/api/pleroma/change_email", %{ "password" => "test", "email" => "cofe@foobar.com" }) @@ -799,18 +573,20 @@ test "with credentials, valid password and valid email", %{ end describe "POST /api/pleroma/change_password" do - setup [:valid_user] + setup do: oauth_access(["write:accounts"]) - test "without credentials", %{conn: conn} do - conn = post(conn, "/api/pleroma/change_password") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials and invalid password", %{conn: conn, user: current_user} do + test "without permissions", %{conn: conn} do conn = conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/change_password", %{ + |> assign(:token, nil) + |> post("/api/pleroma/change_password") + + assert json_response(conn, 403) == %{"error" => "Insufficient permissions: write:accounts."} + end + + test "with proper permissions and invalid password", %{conn: conn} do + conn = + post(conn, "/api/pleroma/change_password", %{ "password" => "hi", "new_password" => "newpass", "new_password_confirmation" => "newpass" @@ -819,14 +595,12 @@ test "with credentials and invalid password", %{conn: conn, user: current_user} assert json_response(conn, 200) == %{"error" => "Invalid password."} end - test "with credentials, valid password and new password and confirmation not matching", %{ - conn: conn, - user: current_user - } do + test "with proper permissions, valid password and new password and confirmation not matching", + %{ + conn: conn + } do conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/change_password", %{ + post(conn, "/api/pleroma/change_password", %{ "password" => "test", "new_password" => "newpass", "new_password_confirmation" => "notnewpass" @@ -837,14 +611,11 @@ test "with credentials, valid password and new password and confirmation not mat } end - test "with credentials, valid password and invalid new password", %{ - conn: conn, - user: current_user + test "with proper permissions, valid password and invalid new password", %{ + conn: conn } do conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/change_password", %{ + post(conn, "/api/pleroma/change_password", %{ "password" => "test", "new_password" => "", "new_password_confirmation" => "" @@ -855,47 +626,46 @@ test "with credentials, valid password and invalid new password", %{ } end - test "with credentials, valid password and matching new password and confirmation", %{ + test "with proper permissions, valid password and matching new password and confirmation", %{ conn: conn, - user: current_user + user: user } do conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/change_password", %{ + post(conn, "/api/pleroma/change_password", %{ "password" => "test", "new_password" => "newpass", "new_password_confirmation" => "newpass" }) assert json_response(conn, 200) == %{"status" => "success"} - fetched_user = User.get_cached_by_id(current_user.id) + fetched_user = User.get_cached_by_id(user.id) assert Comeonin.Pbkdf2.checkpw("newpass", fetched_user.password_hash) == true end end describe "POST /api/pleroma/delete_account" do - setup [:valid_user] + setup do: oauth_access(["write:accounts"]) - test "without credentials", %{conn: conn} do - conn = post(conn, "/api/pleroma/delete_account") - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials and invalid password", %{conn: conn, user: current_user} do + test "without permissions", %{conn: conn} do conn = conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/delete_account", %{"password" => "hi"}) + |> assign(:token, nil) + |> post("/api/pleroma/delete_account") - assert json_response(conn, 200) == %{"error" => "Invalid password."} + assert json_response(conn, 403) == + %{"error" => "Insufficient permissions: write:accounts."} end - test "with credentials and valid password", %{conn: conn, user: current_user} do - conn = - conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/pleroma/delete_account", %{"password" => "test"}) + test "with proper permissions and wrong or missing password", %{conn: conn} do + for params <- [%{"password" => "hi"}, %{}] do + ret_conn = post(conn, "/api/pleroma/delete_account", params) + + assert json_response(ret_conn, 200) == %{"error" => "Invalid password."} + end + end + + test "with proper permissions and valid password", %{conn: conn} do + conn = post(conn, "/api/pleroma/delete_account", %{"password" => "test"}) assert json_response(conn, 200) == %{"status" => "success"} end