Resolve merge conflicts

This commit is contained in:
rinpatch 2019-06-01 16:29:58 +03:00
commit 65db5e9f52
99 changed files with 2461 additions and 650 deletions

View file

@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [unreleased] ## [unreleased]
### Added ### Added
- Optional SSH access mode. (Needs `erlang-ssh` package on some distributions).
- [MongooseIM](https://github.com/esl/MongooseIM) http authentication support.
- LDAP authentication - LDAP authentication
- External OAuth provider authentication - External OAuth provider authentication
- A [job queue](https://git.pleroma.social/pleroma/pleroma_job_queue) for federation, emails, web push, etc. - A [job queue](https://git.pleroma.social/pleroma/pleroma_job_queue) for federation, emails, web push, etc.
@ -23,6 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Configuration: `report_uri` option - Configuration: `report_uri` option
- Pleroma API: User subscriptions - Pleroma API: User subscriptions
- Pleroma API: Healthcheck endpoint - Pleroma API: Healthcheck endpoint
- Pleroma API: `/api/v1/pleroma/mascot` per-user frontend mascot configuration endpoints
- Admin API: Endpoints for listing/revoking invite tokens - Admin API: Endpoints for listing/revoking invite tokens
- Admin API: Endpoints for making users follow/unfollow each other - Admin API: Endpoints for making users follow/unfollow each other
- Admin API: added filters (role, tags, email, name) for users endpoint - Admin API: added filters (role, tags, email, name) for users endpoint
@ -38,6 +41,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Metadata: RelMe provider - Metadata: RelMe provider
- OAuth: added support for refresh tokens - OAuth: added support for refresh tokens
- Emoji packs and emoji pack manager - Emoji packs and emoji pack manager
- Object pruning (`mix pleroma.database prune_objects`)
- OAuth: added job to clean expired access tokens
- MRF: Support for rejecting reports from specific instances (`mrf_simple`)
- MRF: Support for stripping avatars and banner images from specific instances (`mrf_simple`)
### Changed ### Changed
- **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer - **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer
@ -72,6 +79,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Don't ship finmoji by default, they can be installed as an emoji pack - Don't ship finmoji by default, they can be installed as an emoji pack
- Hide deactivated users and their statuses - Hide deactivated users and their statuses
- Posts which are marked sensitive or tagged nsfw no longer have link previews. - Posts which are marked sensitive or tagged nsfw no longer have link previews.
- HTTP connection timeout is now set to 10 seconds.
- Respond with a 404 Not implemented JSON error message when requested API is not implemented
### Fixed ### Fixed
- Added an FTS index on objects. Running `vacuum analyze` and setting a larger `work_mem` is recommended. - Added an FTS index on objects. Running `vacuum analyze` and setting a larger `work_mem` is recommended.
@ -103,11 +112,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Mastodon API: Correct `reblogged`, `favourited`, and `bookmarked` values in the reblog status JSON - Mastodon API: Correct `reblogged`, `favourited`, and `bookmarked` values in the reblog status JSON
- Mastodon API: Exposing default scope of the user to anyone - Mastodon API: Exposing default scope of the user to anyone
- Mastodon API: Make `irreversible` field default to `false` [`POST /api/v1/filters`] - Mastodon API: Make `irreversible` field default to `false` [`POST /api/v1/filters`]
- Mastodon API: Replace missing non-nullable Card attributes with empty strings
- User-Agent is now sent correctly for all HTTP requests. - User-Agent is now sent correctly for all HTTP requests.
- MRF: Simple policy now properly delists imported or relayed statuses
## Removed ## Removed
- Configuration: `config :pleroma, :fe` in favor of the more flexible `config :pleroma, :frontend_configurations` - Configuration: `config :pleroma, :fe` in favor of the more flexible `config :pleroma, :frontend_configurations`
## [0.9.99999] - 2019-05-31
### Security
- Mastodon API: Fix lists leaking private posts
## [0.9.9999] - 2019-04-05 ## [0.9.9999] - 2019-04-05
### Security ### Security
- Mastodon API: Fix content warnings skipping HTML sanitization - Mastodon API: Fix content warnings skipping HTML sanitization

View file

@ -184,9 +184,6 @@
"application/ld+json" => ["activity+json"] "application/ld+json" => ["activity+json"]
} }
config :pleroma, :websub, Pleroma.Web.Websub
config :pleroma, :ostatus, Pleroma.Web.OStatus
config :pleroma, :httpoison, Pleroma.HTTP
config :tesla, adapter: Tesla.Adapter.Hackney config :tesla, adapter: Tesla.Adapter.Hackney
# Configures http settings, upstream proxy etc. # Configures http settings, upstream proxy etc.
@ -245,7 +242,8 @@
welcome_message: nil, welcome_message: nil,
max_report_comment_size: 1000, max_report_comment_size: 1000,
safe_dm_mentions: false, safe_dm_mentions: false,
healthcheck: false healthcheck: false,
remote_post_retention_days: 90
config :pleroma, :app_account_creation, enabled: true, max_requests: 25, interval: 1800 config :pleroma, :app_account_creation, enabled: true, max_requests: 25, interval: 1800
@ -282,6 +280,19 @@
showInstanceSpecificPanel: true showInstanceSpecificPanel: true
} }
config :pleroma, :assets,
mascots: [
pleroma_fox_tan: %{
url: "/images/pleroma-fox-tan-smol.png",
mime_type: "image/png"
},
pleroma_fox_tan_shy: %{
url: "/images/pleroma-fox-tan-shy.png",
mime_type: "image/png"
}
],
default_mascot: :pleroma_fox_tan
config :pleroma, :activitypub, config :pleroma, :activitypub,
accept_blocks: true, accept_blocks: true,
unfollow_blocked: true, unfollow_blocked: true,
@ -304,8 +315,11 @@
media_removal: [], media_removal: [],
media_nsfw: [], media_nsfw: [],
federated_timeline_removal: [], federated_timeline_removal: [],
report_removal: [],
reject: [], reject: [],
accept: [] accept: [],
avatar_removal: [],
banner_removal: []
config :pleroma, :mrf_keyword, config :pleroma, :mrf_keyword,
reject: [], reject: [],
@ -376,6 +390,7 @@
"activities", "activities",
"api", "api",
"auth", "auth",
"check_password",
"dev", "dev",
"friend-requests", "friend-requests",
"inbox", "inbox",
@ -396,6 +411,7 @@
"status", "status",
"tag", "tag",
"user-search", "user-search",
"user_exists",
"users", "users",
"web" "web"
] ]
@ -470,7 +486,9 @@
config :pleroma, :oauth2, config :pleroma, :oauth2,
token_expires_in: 600, token_expires_in: 600,
issue_new_refresh_token: true issue_new_refresh_token: true,
clean_expired_tokens: false,
clean_expired_tokens_interval: 86_400_000
config :pleroma, :database, rum_enabled: false config :pleroma, :database, rum_enabled: false

View file

@ -39,8 +39,6 @@
# Reduce hash rounds for testing # Reduce hash rounds for testing
config :pbkdf2_elixir, rounds: 1 config :pbkdf2_elixir, rounds: 1
config :pleroma, :websub, Pleroma.Web.WebsubMock
config :pleroma, :ostatus, Pleroma.Web.OStatusMock
config :tesla, adapter: Tesla.Mock config :tesla, adapter: Tesla.Mock
config :pleroma, :rich_media, enabled: false config :pleroma, :rich_media, enabled: false

View file

@ -252,6 +252,45 @@ See [Admin-API](Admin-API.md)
] ]
``` ```
## `/api/v1/pleroma/mascot`
### Gets user mascot image
* Method `GET`
* Authentication: required
* Response: JSON. Returns a mastodon media attachment entity.
* Example response:
```json
{
"id": "abcdefg",
"url": "https://pleroma.example.org/media/abcdefg.png",
"type": "image",
"pleroma": {
"mime_type": "image/png"
}
}
```
### Updates user mascot image
* Method `PUT`
* Authentication: required
* Params:
* `image`: Multipart image
* Response: JSON. Returns a mastodon media attachment entity
when successful, otherwise returns HTTP 415 `{"error": "error_msg"}`
* Example response:
```json
{
"id": "abcdefg",
"url": "https://pleroma.example.org/media/abcdefg.png",
"type": "image",
"pleroma": {
"mime_type": "image/png"
}
}
```
* Note: Behaves exactly the same as `POST /api/v1/upload`.
Can only accept images - any attempt to upload non-image files will be met with `HTTP 415 Unsupported Media Type`.
## `/api/pleroma/notification_settings` ## `/api/pleroma/notification_settings`
### Updates user notification settings ### Updates user notification settings
* Method `PUT` * Method `PUT`

View file

@ -109,6 +109,7 @@ config :pleroma, Pleroma.Emails.Mailer,
* `max_report_comment_size`: The maximum size of the report comment (Default: `1000`) * `max_report_comment_size`: The maximum size of the report comment (Default: `1000`)
* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). (Default: `false`) * `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). (Default: `false`)
* `healthcheck`: if set to true, system data will be shown on ``/api/pleroma/healthcheck``. * `healthcheck`: if set to true, system data will be shown on ``/api/pleroma/healthcheck``.
* `remote_post_retention_days`: the default amount of days to retain remote posts when pruning the database
## :app_account_creation ## :app_account_creation
REST API for creating an account settings REST API for creating an account settings
@ -208,12 +209,25 @@ This section is used to configure Pleroma-FE, unless ``:managed_config`` in ``:i
* `hide_post_stats`: Hide notices statistics(repeats, favorites, …) * `hide_post_stats`: Hide notices statistics(repeats, favorites, …)
* `hide_user_stats`: Hide profile statistics(posts, posts per day, followers, followings, …) * `hide_user_stats`: Hide profile statistics(posts, posts per day, followers, followings, …)
## :assets
This section configures assets to be used with various frontends. Currently the only option
relates to mascots on the mastodon frontend
* `mascots`: KeywordList of mascots, each element __MUST__ contain both a `url` and a
`mime_type` key.
* `default_mascot`: An element from `mascots` - This will be used as the default mascot
on MastoFE (default: `:pleroma_fox_tan`)
## :mrf_simple ## :mrf_simple
* `media_removal`: List of instances to remove medias from * `media_removal`: List of instances to remove medias from
* `media_nsfw`: List of instances to put medias as NSFW(sensitive) from * `media_nsfw`: List of instances to put medias as NSFW(sensitive) from
* `federated_timeline_removal`: List of instances to remove from Federated (aka The Whole Known Network) Timeline * `federated_timeline_removal`: List of instances to remove from Federated (aka The Whole Known Network) Timeline
* `reject`: List of instances to reject any activities from * `reject`: List of instances to reject any activities from
* `accept`: List of instances to accept any activities from * `accept`: List of instances to accept any activities from
* `report_removal`: List of instances to reject reports from
* `avatar_removal`: List of instances to strip avatars from
* `banner_removal`: List of instances to strip banners from
## :mrf_rejectnonpublic ## :mrf_rejectnonpublic
* `allow_followersonly`: whether to allow followers-only posts * `allow_followersonly`: whether to allow followers-only posts
@ -472,7 +486,7 @@ config :esshd,
password_authenticator: "Pleroma.BBS.Authenticator" password_authenticator: "Pleroma.BBS.Authenticator"
``` ```
Feel free to adjust the priv_dir and port number. Then you will have to create the key for the keys (in the example `priv/ssh_keys`) and create the host keys with `ssh-keygen -N "" -b 2048 -t rsa -f ssh_host_rsa_key`. After restarting, you should be able to connect to your Pleroma instance with `ssh username@server -p $PORT` Feel free to adjust the priv_dir and port number. Then you will have to create the key for the keys (in the example `priv/ssh_keys`) and create the host keys with `ssh-keygen -m PEM -N "" -b 2048 -t rsa -f ssh_host_rsa_key`. After restarting, you should be able to connect to your Pleroma instance with `ssh username@server -p $PORT`
## :auth ## :auth
@ -544,6 +558,8 @@ Configure OAuth 2 provider capabilities:
* `token_expires_in` - The lifetime in seconds of the access token. * `token_expires_in` - The lifetime in seconds of the access token.
* `issue_new_refresh_token` - Keeps old refresh token or generate new refresh token when to obtain an access token. * `issue_new_refresh_token` - Keeps old refresh token or generate new refresh token when to obtain an access token.
* `clean_expired_tokens` - Enable a background job to clean expired oauth tokens. Defaults to `false`.
* `clean_expired_tokens_interval` - Interval to run the job to clean expired tokens. Defaults to `86_400_000` (24 hours).
## :emoji ## :emoji
* `shortcode_globs`: Location of custom emoji files. `*` can be used as a wildcard. Example `["/emoji/custom/**/*.png"]` * `shortcode_globs`: Location of custom emoji files. `*` can be used as a wildcard. Example `["/emoji/custom/**/*.png"]`

View file

@ -0,0 +1,10 @@
# Configuring MongooseIM (XMPP Server) to use Pleroma for authentication
If you want to give your Pleroma users an XMPP (chat) account, you can configure [MongooseIM](https://github.com/esl/MongooseIM) to use your Pleroma server for user authentication, automatically giving every local user an XMPP account.
In general, you just have to follow the configuration described at [https://mongooseim.readthedocs.io/en/latest/authentication-backends/HTTP-authentication-module/](https://mongooseim.readthedocs.io/en/latest/authentication-backends/HTTP-authentication-module/) and do these changes to your mongooseim.cfg.
1. Set the auth_method to `{auth_method, http}`.
2. Add the http auth pool like this: `{http, global, auth, [{workers, 50}], [{server, "https://yourpleromainstance.com"}]}`
Restart your MongooseIM server, your users should now be able to connect with their Pleroma credentials.

View file

@ -5,6 +5,7 @@ Possible uses include:
* marking incoming messages with media from a given account or instance as sensitive * marking incoming messages with media from a given account or instance as sensitive
* rejecting messages from a specific instance * rejecting messages from a specific instance
* rejecting reports (flags) from a specific instance
* removing/unlisting messages from the public timelines * removing/unlisting messages from the public timelines
* removing media from messages * removing media from messages
* sending only public messages to a specific instance * sending only public messages to a specific instance
@ -41,12 +42,13 @@ Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_si
* `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media. * `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
* `reject`: Servers in this group will have their messages rejected. * `reject`: Servers in this group will have their messages rejected.
* `federated_timeline_removal`: Servers in this group will have their messages unlisted from the public timelines by flipping the `to` and `cc` fields. * `federated_timeline_removal`: Servers in this group will have their messages unlisted from the public timelines by flipping the `to` and `cc` fields.
* `report_removal`: Servers in this group will have their reports (flags) rejected.
Servers should be configured as lists. Servers should be configured as lists.
### Example ### Example
This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com` and remove messages from `spam.university` from the federated timeline: This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`, remove messages from `spam.university` from the federated timeline and block reports (flags) from `whiny.whiner`:
``` ```
config :pleroma, :instance, config :pleroma, :instance,
@ -56,7 +58,8 @@ config :pleroma, :mrf_simple,
media_removal: ["illegalporn.biz"], media_removal: ["illegalporn.biz"],
media_nsfw: ["porn.biz", "porn.business"], media_nsfw: ["porn.biz", "porn.business"],
reject: ["spam.com"], reject: ["spam.com"],
federated_timeline_removal: ["spam.university"] federated_timeline_removal: ["spam.university"],
report_removal: ["whiny.whiner"]
``` ```

View file

@ -87,7 +87,7 @@ sudo adduser -S -s /bin/false -h /opt/pleroma -H pleroma
```shell ```shell
sudo mkdir -p /opt/pleroma sudo mkdir -p /opt/pleroma
sudo chown -R pleroma:pleroma /opt/pleroma sudo chown -R pleroma:pleroma /opt/pleroma
sudo -Hu pleroma git clone https://git.pleroma.social/pleroma/pleroma /opt/pleroma sudo -Hu pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma /opt/pleroma
``` ```
* Change to the new directory: * Change to the new directory:

View file

@ -66,7 +66,7 @@ sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
```shell ```shell
sudo mkdir -p /opt/pleroma sudo mkdir -p /opt/pleroma
sudo chown -R pleroma:pleroma /opt/pleroma sudo chown -R pleroma:pleroma /opt/pleroma
sudo -Hu pleroma git clone https://git.pleroma.social/pleroma/pleroma /opt/pleroma sudo -Hu pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma /opt/pleroma
``` ```
* Change to the new directory: * Change to the new directory:

View file

@ -143,7 +143,7 @@ sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
```shell ```shell
sudo mkdir -p /opt/pleroma sudo mkdir -p /opt/pleroma
sudo chown -R pleroma:pleroma /opt/pleroma sudo chown -R pleroma:pleroma /opt/pleroma
sudo -Hu pleroma git clone https://git.pleroma.social/pleroma/pleroma /opt/pleroma sudo -Hu pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma /opt/pleroma
``` ```
* Change to the new directory: * Change to the new directory:

View file

@ -68,7 +68,7 @@ sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
```shell ```shell
sudo mkdir -p /opt/pleroma sudo mkdir -p /opt/pleroma
sudo chown -R pleroma:pleroma /opt/pleroma sudo chown -R pleroma:pleroma /opt/pleroma
sudo -Hu pleroma git clone https://git.pleroma.social/pleroma/pleroma /opt/pleroma sudo -Hu pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma /opt/pleroma
``` ```
* Change to the new directory: * Change to the new directory:

View file

@ -69,7 +69,7 @@ cd ~
* Gitリポジトリをクローンします。 * Gitリポジトリをクローンします。
``` ```
git clone https://git.pleroma.social/pleroma/pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma
``` ```
* 新しいディレクトリに移動します。 * 新しいディレクトリに移動します。

View file

@ -106,7 +106,7 @@ It is highly recommended you use your own fork for the `https://path/to/repo` pa
```shell ```shell
pleroma$ cd ~ pleroma$ cd ~
pleroma$ git clone https://path/to/repo pleroma$ git clone -b master https://path/to/repo
``` ```
* Change to the new directory: * Change to the new directory:

View file

@ -58,7 +58,7 @@ Clone the repository:
``` ```
$ cd /home/pleroma $ cd /home/pleroma
$ git clone https://git.pleroma.social/pleroma/pleroma.git $ git clone -b master https://git.pleroma.social/pleroma/pleroma.git
``` ```
Configure Pleroma. Note that you need a domain name at this point: Configure Pleroma. Note that you need a domain name at this point:

View file

@ -29,7 +29,7 @@ This creates a "pleroma" login class and sets higher values than default for dat
Create the \_pleroma user, assign it the pleroma login class and create its home directory (/home/\_pleroma/): `useradd -m -L pleroma _pleroma` Create the \_pleroma user, assign it the pleroma login class and create its home directory (/home/\_pleroma/): `useradd -m -L pleroma _pleroma`
#### Clone pleroma's directory #### Clone pleroma's directory
Enter a shell as the \_pleroma user. As root, run `su _pleroma -;cd`. Then clone the repository with `git clone https://git.pleroma.social/pleroma/pleroma.git`. Pleroma is now installed in /home/\_pleroma/pleroma/, it will be configured and started at the end of this guide. Enter a shell as the \_pleroma user. As root, run `su _pleroma -;cd`. Then clone the repository with `git clone -b master https://git.pleroma.social/pleroma/pleroma.git`. Pleroma is now installed in /home/\_pleroma/pleroma/, it will be configured and started at the end of this guide.
#### Postgresql #### Postgresql
Start a shell as the \_postgresql user (as root run `su _postgresql -` then run the `initdb` command to initialize postgresql: Start a shell as the \_postgresql user (as root run `su _postgresql -` then run the `initdb` command to initialize postgresql:

View file

@ -44,7 +44,7 @@ Vaihda pleroma-käyttäjään ja mene kotihakemistoosi:
Lataa pleroman lähdekoodi: Lataa pleroman lähdekoodi:
`$ git clone https://git.pleroma.social/pleroma/pleroma.git` `$ git clone -b master https://git.pleroma.social/pleroma/pleroma.git`
`$ cd pleroma` `$ cd pleroma`

View file

@ -10,7 +10,9 @@ example.tld {
gzip gzip
proxy / localhost:4000 { # this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
# and `localhost.` resolves to [::0] on some systems: see issue #930
proxy / 127.0.0.1:4000 {
websocket websocket
transparent transparent
} }

View file

@ -58,8 +58,10 @@ CustomLog ${APACHE_LOG_DIR}/access.log combined
RewriteRule /(.*) ws://localhost:4000/$1 [P,L] RewriteRule /(.*) ws://localhost:4000/$1 [P,L]
ProxyRequests off ProxyRequests off
ProxyPass / http://localhost:4000/ # this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
ProxyPassReverse / http://localhost:4000/ # and `localhost.` resolves to [::0] on some systems: see issue #930
ProxyPass / http://127.0.0.1:4000/
ProxyPassReverse / http://127.0.0.1:4000/
RequestHeader set Host ${servername} RequestHeader set Host ${servername}
ProxyPreserveHost On ProxyPreserveHost On

View file

@ -0,0 +1,932 @@
%%%
%%% ejabberd configuration file
%%%
%%%'
%%% The parameters used in this configuration file are explained in more detail
%%% in the ejabberd Installation and Operation Guide.
%%% Please consult the Guide in case of doubts, it is included with
%%% your copy of ejabberd, and is also available online at
%%% http://www.process-one.net/en/ejabberd/docs/
%%% This configuration file contains Erlang terms.
%%% In case you want to understand the syntax, here are the concepts:
%%%
%%% - The character to comment a line is %
%%%
%%% - Each term ends in a dot, for example:
%%% override_global.
%%%
%%% - A tuple has a fixed definition, its elements are
%%% enclosed in {}, and separated with commas:
%%% {loglevel, 4}.
%%%
%%% - A list can have as many elements as you want,
%%% and is enclosed in [], for example:
%%% [http_poll, web_admin, tls]
%%%
%%% Pay attention that list elements are delimited with commas,
%%% but no comma is allowed after the last list element. This will
%%% give a syntax error unlike in more lenient languages (e.g. Python).
%%%
%%% - A keyword of ejabberd is a word in lowercase.
%%% Strings are enclosed in "" and can contain spaces, dots, ...
%%% {language, "en"}.
%%% {ldap_rootdn, "dc=example,dc=com"}.
%%%
%%% - This term includes a tuple, a keyword, a list, and two strings:
%%% {hosts, ["jabber.example.net", "im.example.com"]}.
%%%
%%% - This config is preprocessed during release generation by a tool which
%%% interprets double curly braces as substitution markers, so avoid this
%%% syntax in this file (though it's valid Erlang).
%%%
%%% So this is OK (though arguably looks quite ugly):
%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }.
%%%
%%% And I can't give an example of what's not OK exactly because
%%% of this rule.
%%%
%%%. =======================
%%%' OVERRIDE STORED OPTIONS
%%
%% Override the old values stored in the database.
%%
%%
%% Override global options (shared by all ejabberd nodes in a cluster).
%%
%%override_global.
%%
%% Override local options (specific for this particular ejabberd node).
%%
%%override_local.
%%
%% Remove the Access Control Lists before new ones are added.
%%
%%override_acls.
%%%. =========
%%%' DEBUGGING
%%
%% loglevel: Verbosity of log files generated by ejabberd.
%% 0: No ejabberd log at all (not recommended)
%% 1: Critical
%% 2: Error
%% 3: Warning
%% 4: Info
%% 5: Debug
%%
{loglevel, 3}.
%%%. ================
%%%' SERVED HOSTNAMES
%%
%% hosts: Domains served by ejabberd.
%% You can define one or several, for example:
%% {hosts, ["example.net", "example.com", "example.org"]}.
%%
{hosts, ["pleroma.soykaf.com"] }.
%%
%% route_subdomains: Delegate subdomains to other XMPP servers.
%% For example, if this ejabberd serves example.org and you want
%% to allow communication with an XMPP server called im.example.org.
%%
%%{route_subdomains, s2s}.
%%%. ===============
%%%' LISTENING PORTS
%%
%% listen: The ports ejabberd will listen on, which service each is handled
%% by and what options to start it with.
%%
{listen,
[
%% BOSH and WS endpoints over HTTP
{ 5280, ejabberd_cowboy, [
{num_acceptors, 10},
{transport_options, [{max_connections, 1024}]},
{modules, [
{"_", "/http-bind", mod_bosh},
{"_", "/ws-xmpp", mod_websockets, [{ejabberd_service, [
{access, all},
{shaper_rule, fast},
{ip, {127, 0, 0, 1}},
{password, "secret"}]}
%% Uncomment to enable connection dropping or/and server-side pings
%{timeout, 600000}, {ping_rate, 2000}
]}
%% Uncomment to serve static files
%{"_", "/static/[...]", cowboy_static,
% {dir, "/var/www", [{mimetypes, cow_mimetypes, all}]}
%},
%% Example usage of mod_revproxy
%% {"_", "/[...]", mod_revproxy, [{timeout, 5000},
%% % time limit for upstream to respond
%% {body_length, 8000000},
%% % maximum body size (may be infinity)
%% {custom_headers, [{<<"header">>,<<"value">>}]}
%% % list of extra headers that are send to upstream
%% ]}
%% Example usage of mod_cowboy
%% {"_", "/[...]", mod_cowboy, [{http, mod_revproxy,
%% [{timeout, 5000},
%% % time limit for upstream to respond
%% {body_length, 8000000},
%% % maximum body size (may be infinity)
%% {custom_headers, [{<<"header">>,<<"value">>}]}
%% % list of extra headers that are send to upstream
%% ]},
%% {ws, xmpp, mod_websockets}
%% ]}
]}
]},
%% BOSH and WS endpoints over HTTPS
{ 5285, ejabberd_cowboy, [
{num_acceptors, 10},
{transport_options, [{max_connections, 1024}]},
{ssl, [{certfile, "priv/ssl/fullchain.pem"}, {keyfile, "priv/ssl/privkey.pem"}, {password, ""}]},
{modules, [
{"_", "/http-bind", mod_bosh},
{"_", "/ws-xmpp", mod_websockets, [
%% Uncomment to enable connection dropping or/and server-side pings
%{timeout, 600000}, {ping_rate, 60000}
]}
%% Uncomment to serve static files
%{"_", "/static/[...]", cowboy_static,
% {dir, "/var/www", [{mimetypes, cow_mimetypes, all}]}
%},
]}
]},
%% MongooseIM HTTP API it's important to start it on localhost
%% or some private interface only (not accessible from the outside)
%% At least start it on different port which will be hidden behind firewall
{ {8088, "127.0.0.1"} , ejabberd_cowboy, [
{num_acceptors, 10},
{transport_options, [{max_connections, 1024}]},
{modules, [
{"localhost", "/api", mongoose_api_admin, []}
]}
]},
{ 8089 , ejabberd_cowboy, [
{num_acceptors, 10},
{transport_options, [{max_connections, 1024}]},
{protocol_options, [{compress, true}]},
{ssl, [{certfile, "priv/ssl/fullchain.pem"}, {keyfile, "priv/ssl/privkey.pem"}, {password, ""}]},
{modules, [
{"_", "/api/sse", lasse_handler, [mongoose_client_api_sse]},
{"_", "/api/messages/[:with]", mongoose_client_api_messages, []},
{"_", "/api/contacts/[:jid]", mongoose_client_api_contacts, []},
{"_", "/api/rooms/[:id]", mongoose_client_api_rooms, []},
{"_", "/api/rooms/[:id]/config", mongoose_client_api_rooms_config, []},
{"_", "/api/rooms/:id/users/[:user]", mongoose_client_api_rooms_users, []},
{"_", "/api/rooms/[:id]/messages", mongoose_client_api_rooms_messages, []}
]}
]},
%% Following HTTP API is deprected, the new one abouve should be used instead
{ {5288, "127.0.0.1"} , ejabberd_cowboy, [
{num_acceptors, 10},
{transport_options, [{max_connections, 1024}]},
{modules, [
{"localhost", "/api", mongoose_api, [{handlers, [mongoose_api_metrics,
mongoose_api_users]}]}
]}
]},
{ 5222, ejabberd_c2s, [
%%
%% If TLS is compiled in and you installed a SSL
%% certificate, specify the full path to the
%% file and uncomment this line:
%%
{certfile, "priv/ssl/both.pem"}, starttls,
%%{zlib, 10000},
%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS
%% {ciphers, "DEFAULT:!EXPORT:!LOW:!SSLv2"},
{access, c2s},
{shaper, c2s_shaper},
{max_stanza_size, 65536},
{protocol_options, ["no_sslv3"]}
]},
%%
%% To enable the old SSL connection method on port 5223:
%%
%%{5223, ejabberd_c2s, [
%% {access, c2s},
%% {shaper, c2s_shaper},
%% {certfile, "/path/to/ssl.pem"}, tls,
%% {max_stanza_size, 65536}
%% ]},
{ 5269, ejabberd_s2s_in, [
{shaper, s2s_shaper},
{max_stanza_size, 131072},
{protocol_options, ["no_sslv3"]}
]}
%%
%% ejabberd_service: Interact with external components (transports, ...)
%%
,{8888, ejabberd_service, [
{access, all},
{shaper_rule, fast},
{ip, {127, 0, 0, 1}},
{password, "secret"}
]}
%%
%% ejabberd_stun: Handles STUN Binding requests
%%
%%{ {3478, udp}, ejabberd_stun, []}
]}.
%%
%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections.
%% Allowed values are: false optional required required_trusted
%% You must specify a certificate file.
%%
{s2s_use_starttls, optional}.
%%
%% s2s_certfile: Specify a certificate file.
%%
{s2s_certfile, "priv/ssl/both.pem"}.
%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS
%% {s2s_ciphers, "DEFAULT:!EXPORT:!LOW:!SSLv2"}.
%%
%% domain_certfile: Specify a different certificate for each served hostname.
%%
%%{domain_certfile, "example.org", "/path/to/example_org.pem"}.
%%{domain_certfile, "example.com", "/path/to/example_com.pem"}.
%%
%% S2S whitelist or blacklist
%%
%% Default s2s policy for undefined hosts.
%%
{s2s_default_policy, deny }.
%%
%% Allow or deny communication with specific servers.
%%
%%{ {s2s_host, "goodhost.org"}, allow}.
%%{ {s2s_host, "badhost.org"}, deny}.
{outgoing_s2s_port, 5269 }.
%%
%% IP addresses predefined for specific hosts to skip DNS lookups.
%% Ports defined here take precedence over outgoing_s2s_port.
%% Examples:
%%
%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }.
%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }.
%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }.
%%
%% Outgoing S2S options
%%
%% Preferred address families (which to try first) and connect timeout
%% in milliseconds.
%%
%%{outgoing_s2s_options, [ipv4, ipv6], 10000}.
%%
%%%. ==============
%%%' SESSION BACKEND
%%{sm_backend, {mnesia, []}}.
%% Requires {redis, global, default, ..., ...} outgoing pool
%%{sm_backend, {redis, []}}.
{sm_backend, {mnesia, []} }.
%%%. ==============
%%%' AUTHENTICATION
%% Advertised SASL mechanisms
{sasl_mechanisms, [cyrsasl_plain]}.
%%
%% auth_method: Method used to authenticate the users.
%% The default method is the internal.
%% If you want to use a different method,
%% comment this line and enable the correct ones.
%%
%% {auth_method, internal }.
{auth_method, http }.
{auth_opts, [
{http, global, auth, [{workers, 50}], [{server, "https://pleroma.soykaf.com"}]},
{password_format, plain} % default
%% {password_format, scram}
%% {scram_iterations, 4096} % default
%%
%% For auth_http:
%% {basic_auth, "user:password"}
%% {path_prefix, "/"} % default
%% auth_http requires {http, Host | global, auth, ..., ...} outgoing pool.
%%
%% For auth_external
%%{extauth_program, "/path/to/authentication/script"}.
%%
%% For auth_jwt
%% {jwt_secret_source, "/path/to/file"},
%% {jwt_algorithm, "RS256"},
%% {jwt_username_key, user}
%% For cyrsasl_external
%% {authenticate_with_cn, false}
{cyrsasl_external, standard}
]}.
%%
%% Authentication using external script
%% Make sure the script is executable by ejabberd.
%%
%%{auth_method, external}.
%%
%% Authentication using RDBMS
%% Remember to setup a database in the next section.
%%
%%{auth_method, rdbms}.
%%
%% Authentication using LDAP
%%
%%{auth_method, ldap}.
%%
%% List of LDAP servers:
%%{ldap_servers, ["localhost"]}.
%%
%% Encryption of connection to LDAP servers:
%%{ldap_encrypt, none}.
%%{ldap_encrypt, tls}.
%%
%% Port to connect to on LDAP servers:
%%{ldap_port, 389}.
%%{ldap_port, 636}.
%%
%% LDAP manager:
%%{ldap_rootdn, "dc=example,dc=com"}.
%%
%% Password of LDAP manager:
%%{ldap_password, "******"}.
%%
%% Search base of LDAP directory:
%%{ldap_base, "dc=example,dc=com"}.
%%
%% LDAP attribute that holds user ID:
%%{ldap_uids, [{"mail", "%u@mail.example.org"}]}.
%%
%% LDAP filter:
%%{ldap_filter, "(objectClass=shadowAccount)"}.
%%
%% Anonymous login support:
%% auth_method: anonymous
%% anonymous_protocol: sasl_anon | login_anon | both
%% allow_multiple_connections: true | false
%%
%%{host_config, "public.example.org", [{auth_method, anonymous},
%% {allow_multiple_connections, false},
%% {anonymous_protocol, sasl_anon}]}.
%%
%% To use both anonymous and internal authentication:
%%
%%{host_config, "public.example.org", [{auth_method, [internal, anonymous]}]}.
%%%. ==============
%%%' OUTGOING CONNECTIONS (e.g. DB)
%% Here you may configure all outgoing connections used by MongooseIM,
%% e.g. to RDBMS (such as MySQL), Riak or external HTTP components.
%% Default MongooseIM configuration uses only Mnesia (non-Mnesia extensions are disabled),
%% so no options here are uncommented out of the box.
%% This section includes configuration examples; for comprehensive guide
%% please consult MongooseIM documentation, page "Outgoing connections":
%% - doc/advanced-configuration/outgoing-connections.md
%% - https://mongooseim.readthedocs.io/en/latest/advanced-configuration/outgoing-connections/
{outgoing_pools, [
% {riak, global, default, [{workers, 5}], [{address, "127.0.0.1"}, {port, 8087}]},
% {elastic, global, default, [], [{host, "elastic.host.com"}, {port, 9042}]},
{http, global, auth, [{workers, 50}], [{server, "https://pleroma.soykaf.com"}]}
% {cassandra, global, default, [{workers, 100}], [{servers, [{"server1", 9042}]}, {keyspace, "big_mongooseim"}]},
% {rdbms, global, default, [{workers, 10}], [{server, {mysql, "server", 3306, "database", "username", "password"}}]}
]}.
%% More examples that may be added to outgoing_pools list:
%%
%% == MySQL ==
%% {rdbms, global, default, [{workers, 10}],
%% [{server, {mysql, "server", 3306, "database", "username", "password"}},
%% {keepalive_interval, 10}]},
%% keepalive_interval is optional
%% == PostgreSQL ==
%% {rdbms, global, default, [{workers, 10}],
%% [{server, {pgsql, "server", 5432, "database", "username", "password"}}]},
%% == ODBC (MSSQL) ==
%% {rdbms, global, default, [{workers, 10}],
%% [{server, "DSN=mongooseim;UID=mongooseim;PWD=mongooseim"}]},
%% == Elastic Search ==
%% {elastic, global, default, [], [{host, "elastic.host.com"}, {port, 9042}]},
%% == Riak ==
%% {riak, global, default, [{workers, 20}], [{address, "127.0.0.1"}, {port, 8087}]},
%% == HTTP ==
%% {http, global, conn1, [{workers, 50}], [{server, "http://server:8080"}]},
%% == Cassandra ==
%% {cassandra, global, default, [{workers, 100}],
%% [
%% {servers, [
%% {"cassandra_server1.example.com", 9042},
%% {"cassandra_server2.example.com", 9042},
%% {"cassandra_server3.example.com", 9042},
%% {"cassandra_server4.example.com", 9042}
%% ]},
%% {keyspace, "big_mongooseim"}
%% ]}
%% == Extra options ==
%%
%% If you use PostgreSQL, have a large database, and need a
%% faster but inexact replacement for "select count(*) from users"
%%
%%{pgsql_users_number_estimate, true}.
%%
%% rdbms_server_type specifies what database is used over the RDBMS layer
%% Can take values mssql, pgsql, mysql
%% In some cases (for example for MAM with pgsql) it is required to set proper value.
%%
%% {rdbms_server_type, pgsql}.
%%%. ===============
%%%' TRAFFIC SHAPERS
%%
%% The "normal" shaper limits traffic speed to 1000 B/s
%%
{shaper, normal, {maxrate, 1000}}.
%%
%% The "fast" shaper limits traffic speed to 50000 B/s
%%
{shaper, fast, {maxrate, 50000}}.
%%
%% This option specifies the maximum number of elements in the queue
%% of the FSM. Refer to the documentation for details.
%%
{max_fsm_queue, 1000}.
%%%. ====================
%%%' ACCESS CONTROL LISTS
%%
%% The 'admin' ACL grants administrative privileges to XMPP accounts.
%% You can put here as many accounts as you want.
%%
%{acl, admin, {user, "alice", "localhost"}}.
%{acl, admin, {user, "a", "localhost"}}.
%%
%% Blocked users
%%
%%{acl, blocked, {user, "baduser", "example.org"}}.
%%{acl, blocked, {user, "test"}}.
%%
%% Local users: don't modify this line.
%%
{acl, local, {user_regexp, ""}}.
%%
%% More examples of ACLs
%%
%%{acl, jabberorg, {server, "jabber.org"}}.
%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}.
%%{acl, test, {user_regexp, "^test"}}.
%%{acl, test, {user_glob, "test*"}}.
%%
%% Define specific ACLs in a virtual host.
%%
%%{host_config, "localhost",
%% [
%% {acl, admin, {user, "bob-local", "localhost"}}
%% ]
%%}.
%%%. ============
%%%' ACCESS RULES
%% Maximum number of simultaneous sessions allowed for a single user:
{access, max_user_sessions, [{10, all}]}.
%% Maximum number of offline messages that users can have:
{access, max_user_offline_messages, [{5000, admin}, {100, all}]}.
%% This rule allows access only for local users:
{access, local, [{allow, local}]}.
%% Only non-blocked users can use c2s connections:
{access, c2s, [{deny, blocked},
{allow, all}]}.
%% For C2S connections, all users except admins use the "normal" shaper
{access, c2s_shaper, [{none, admin},
{normal, all}]}.
%% All S2S connections use the "fast" shaper
{access, s2s_shaper, [{fast, all}]}.
%% Admins of this server are also admins of the MUC service:
{access, muc_admin, [{allow, admin}]}.
%% Only accounts of the local ejabberd server can create rooms:
{access, muc_create, [{allow, local}]}.
%% All users are allowed to use the MUC service:
{access, muc, [{allow, all}]}.
%% In-band registration allows registration of any possible username.
%% To disable in-band registration, replace 'allow' with 'deny'.
{access, register, [{allow, all}]}.
%% By default the frequency of account registrations from the same IP
%% is limited to 1 account every 10 minutes. To disable, specify: infinity
{registration_timeout, infinity}.
%% Default settings for MAM.
%% To set non-standard value, replace 'default' with 'allow' or 'deny'.
%% Only user can access his/her archive by default.
%% An online user can read room's archive by default.
%% Only an owner can change settings and purge messages by default.
%% Empty list (i.e. `[]`) means `[{deny, all}]`.
{access, mam_set_prefs, [{default, all}]}.
{access, mam_get_prefs, [{default, all}]}.
{access, mam_lookup_messages, [{default, all}]}.
{access, mam_purge_single_message, [{default, all}]}.
{access, mam_purge_multiple_messages, [{default, all}]}.
%% 1 command of the specified type per second.
{shaper, mam_shaper, {maxrate, 1}}.
%% This shaper is primeraly for Mnesia overload protection during stress testing.
%% The limit is 1000 operations of each type per second.
{shaper, mam_global_shaper, {maxrate, 1000}}.
{access, mam_set_prefs_shaper, [{mam_shaper, all}]}.
{access, mam_get_prefs_shaper, [{mam_shaper, all}]}.
{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}.
{access, mam_purge_single_message_shaper, [{mam_shaper, all}]}.
{access, mam_purge_multiple_messages_shaper, [{mam_shaper, all}]}.
{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}.
{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}.
{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}.
{access, mam_purge_single_message_global_shaper, [{mam_global_shaper, all}]}.
{access, mam_purge_multiple_messages_global_shaper, [{mam_global_shaper, all}]}.
%%
%% Define specific Access Rules in a virtual host.
%%
%%{host_config, "localhost",
%% [
%% {access, c2s, [{allow, admin}, {deny, all}]},
%% {access, register, [{deny, all}]}
%% ]
%%}.
%%%. ================
%%%' DEFAULT LANGUAGE
%%
%% language: Default language used for server messages.
%%
{language, "en"}.
%%
%% Set a different default language in a virtual host.
%%
%%{host_config, "localhost",
%% [{language, "ru"}]
%%}.
%%%. ================
%%%' MISCELLANEOUS
{all_metrics_are_global, false }.
%%%. ========
%%%' SERVICES
%% Unlike modules, services are started per node and provide either features which are not
%% related to any particular host, or backend stuff which is used by modules.
%% This is handled by `mongoose_service` module.
{services,
[
{service_admin_extra, [{submods, [node, accounts, sessions, vcard,
roster, last, private, stanza, stats]}]}
]
}.
%%%. =======
%%%' MODULES
%%
%% Modules enabled in all mongooseim virtual hosts.
%% For list of possible modules options, check documentation.
%%
{modules,
[
%% The format for a single route is as follows:
%% {Host, Path, Method, Upstream}
%%
%% "_" can be used as wildcard for Host, Path and Method
%% Upstream can be either host (just http(s)://host:port) or uri
%% The difference is that host upstreams append whole path while
%% uri upstreams append only remainder that follows the matched Path
%% (this behaviour is similar to nginx's proxy_pass rules)
%%
%% Bindings can be used to match certain parts of host or path.
%% They will be later overlaid with parts of the upstream uri.
%%
%% {mod_revproxy,
%% [{routes, [{"www.erlang-solutions.com", "/admin", "_",
%% "https://www.erlang-solutions.com/"},
%% {":var.com", "/:var", "_", "http://localhost:8080/"},
%% {":domain.com", "/", "_", "http://localhost:8080/:domain"}]
%% }]},
% {mod_http_upload, [
%% Set max file size in bytes. Defaults to 10 MB.
%% Disabled if value is `undefined`.
% {max_file_size, 1024},
%% Use S3 storage backend
% {backend, s3},
%% Set options for S3 backend
% {s3, [
% {bucket_url, "http://s3-eu-west-1.amazonaws.com/konbucket2"},
% {region, "eu-west-1"},
% {access_key_id, "AKIAIAOAONIULXQGMOUA"},
% {secret_access_key, "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"}
% ]}
% ]},
{mod_adhoc, []},
{mod_disco, [{users_can_see_hidden_services, false}]},
{mod_commands, []},
{mod_muc_commands, []},
{mod_muc_light_commands, []},
{mod_last, []},
{mod_stream_management, [
% default 100
% size of a buffer of unacked messages
% {buffer_max, 100}
% default 1 - server sends the ack request after each stanza
% {ack_freq, 1}
% default: 600 seconds
% {resume_timeout, 600}
]},
%% {mod_muc_light, [{host, "muclight.@HOST@"}]},
%% {mod_muc, [{host, "muc.@HOST@"},
%% {access, muc},
%% {access_create, muc_create}
%% ]},
%% {mod_muc_log, [
%% {outdir, "/tmp/muclogs"},
%% {access_log, muc}
%% ]},
{mod_offline, [{access_max_user_messages, max_user_offline_messages}]},
{mod_privacy, []},
{mod_blocking, []},
{mod_private, []},
% {mod_private, [{backend, mnesia}]},
% {mod_private, [{backend, rdbms}]},
% {mod_register, [
% %%
% %% Set the minimum informational entropy for passwords.
% %%
% %%{password_strength, 32},
%
% %%
% %% After successful registration, the user receives
% %% a message with this subject and body.
% %%
% {welcome_message, {""}},
%
% %%
% %% When a user registers, send a notification to
% %% these XMPP accounts.
% %%
%
%
% %%
% %% Only clients in the server machine can register accounts
% %%
% {ip_access, [{allow, "127.0.0.0/8"},
% {deny, "0.0.0.0/0"}]},
%
% %%
% %% Local c2s or remote s2s users cannot register accounts
% %%
% %%{access_from, deny},
%
% {access, register}
% ]},
{mod_roster, []},
{mod_sic, []},
{mod_vcard, [%{matches, 1},
%{search, true},
%{ldap_search_operator, 'or'}, %% either 'or' or 'and'
%{ldap_binary_search_fields, [<<"PHOTO">>]},
%% list of binary search fields (as in vcard after mapping)
{host, "vjud.@HOST@"}
]},
{mod_bosh, []},
{mod_carboncopy, []}
%%
%% Message Archive Management (MAM, XEP-0313) for registered users and
%% Multi-User chats (MUCs).
%%
% {mod_mam_meta, [
%% Use RDBMS backend (default)
% {backend, rdbms},
%% Do not store user preferences (default)
% {user_prefs_store, false},
%% Store user preferences in RDBMS
% {user_prefs_store, rdbms},
%% Store user preferences in Mnesia (recommended).
%% The preferences store will be called each time, as a message is routed.
%% That is why Mnesia is better suited for this job.
% {user_prefs_store, mnesia},
%% Enables a pool of asynchronous writers. (default)
%% Messages will be grouped together based on archive id.
% {async_writer, true},
%% Cache information about users (default)
% {cache_users, true},
%% Enable archivization for private messages (default)
% {pm, [
%% Top-level options can be overriden here if needed, for example:
% {async_writer, false}
% ]},
%%
%% Message Archive Management (MAM) for multi-user chats (MUC).
%% Enable XEP-0313 for "muc.@HOST@".
%%
% {muc, [
% {host, "muc.@HOST@"}
%% As with pm, top-level options can be overriden for MUC archive
% ]},
%
%% Do not use a <stanza-id/> element (by default stanzaid is used)
% no_stanzaid_element,
% ]},
%%
%% MAM configuration examples
%%
%% Only MUC, no user-defined preferences, good performance.
% {mod_mam_meta, [
% {backend, rdbms},
% {pm, false},
% {muc, [
% {host, "muc.@HOST@"}
% ]}
% ]},
%% Only archives for c2c messages, good performance.
% {mod_mam_meta, [
% {backend, rdbms},
% {pm, [
% {user_prefs_store, mnesia}
% ]}
% ]},
%% Basic configuration for c2c messages, bad performance, easy to debug.
% {mod_mam_meta, [
% {backend, rdbms},
% {async_writer, false},
% {cache_users, false}
% ]},
%% Cassandra archive for c2c and MUC conversations.
%% No custom settings supported (always archive).
% {mod_mam_meta, [
% {backend, cassandra},
% {user_prefs_store, cassandra},
% {muc, [{host, "muc.@HOST@"}]}
% ]}
% {mod_event_pusher, [
% {backends, [
% %%
% %% Configuration for Amazon SNS notifications.
% %%
% {sns, [
% %% AWS credentials, region and host configuration
% {access_key_id, "AKIAJAZYHOIPY6A2PESA"},
% {secret_access_key, "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"},
% {region, "eu-west-1"},
% {account_id, "251423380551"},
% {region, "eu-west-1"},
% {sns_host, "sns.eu-west-1.amazonaws.com"},
%
% %% Messages from this MUC host will be sent to the SNS topic
% {muc_host, "muc.@HOST@"},
%
% %% Plugin module for defining custom message attributes and user identification
% {plugin_module, mod_event_pusher_sns_defaults},
%
% %% Topic name configurations. Removing a topic will disable this specific SNS notification
% {presence_updates_topic, "user_presence_updated-dev-1"}, %% For presence updates
% {pm_messages_topic, "user_message_sent-dev-1"}, %% For private chat messages
% {muc_messages_topic, "user_messagegroup_sent-dev-1"} %% For group chat messages
%
% %% Pool options
% {pool_size, 100}, %% Worker pool size for publishing notifications
% {publish_retry_count, 2}, %% Retry count in case of publish error
% {publish_retry_time_ms, 50} %% Base exponential backoff time (in ms) for publish errors
% ]}
% ]}
]}.
%%
%% Enable modules with custom options in a specific virtual host
%%
%%{host_config, "localhost",
%% [{ {add, modules},
%% [
%% {mod_some_module, []}
%% ]
%% }
%% ]}.
%%%.
%%%'
%%% $Id$
%%% Local Variables:
%%% mode: erlang
%%% End:
%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker:
%%%.

View file

@ -69,7 +69,9 @@ server {
proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_pass http://localhost:4000; # this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
# and `localhost.` resolves to [::0] on some systems: see issue #930
proxy_pass http://127.0.0.1:4000;
client_max_body_size 16m; client_max_body_size 16m;
} }

View file

@ -1,4 +1,4 @@
vcl 4.0; vcl 4.1;
import std; import std;
backend default { backend default {
@ -35,24 +35,6 @@ sub vcl_recv {
} }
return(purge); return(purge);
} }
# Pleroma MediaProxy - strip headers that will affect caching
if (req.url ~ "^/proxy/") {
unset req.http.Cookie;
unset req.http.Authorization;
unset req.http.Accept;
return (hash);
}
# Strip headers that will affect caching from all other static content
# This also permits caching of individual toots and AP Activities
if ((req.url ~ "^/(media|static)/") ||
(req.url ~ "(?i)\.(html|js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|mp4|ogg|webm|svg|swf|ttf|pdf|woff|woff2)$"))
{
unset req.http.Cookie;
unset req.http.Authorization;
return (hash);
}
} }
sub vcl_backend_response { sub vcl_backend_response {
@ -61,6 +43,12 @@ sub vcl_backend_response {
set beresp.do_gzip = true; set beresp.do_gzip = true;
} }
# Retry broken backend responses.
if (beresp.status == 503) {
set bereq.http.X-Varnish-Backend-503 = "1";
return (retry);
}
# CHUNKED SUPPORT # CHUNKED SUPPORT
if (bereq.http.x-range ~ "bytes=" && beresp.status == 206) { if (bereq.http.x-range ~ "bytes=" && beresp.status == 206) {
set beresp.ttl = 10m; set beresp.ttl = 10m;
@ -73,8 +61,6 @@ sub vcl_backend_response {
return (deliver); return (deliver);
} }
# Default object caching of 86400s;
set beresp.ttl = 86400s;
# Allow serving cached content for 6h in case backend goes down # Allow serving cached content for 6h in case backend goes down
set beresp.grace = 6h; set beresp.grace = 6h;
@ -90,20 +76,6 @@ sub vcl_backend_response {
set beresp.ttl = 30s; set beresp.ttl = 30s;
return (deliver); return (deliver);
} }
# Pleroma MediaProxy internally sets headers properly
if (bereq.url ~ "^/proxy/") {
return (deliver);
}
# Strip cache-restricting headers from Pleroma on static content that we want to cache
if (bereq.url ~ "(?i)\.(js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|mp4|ogg|webm|svg|swf|ttf|pdf|woff|woff2)$")
{
unset beresp.http.set-cookie;
unset beresp.http.Cache-Control;
unset beresp.http.x-request-id;
set beresp.http.Cache-Control = "public, max-age=86400";
}
} }
# The synthetic response for 301 redirects # The synthetic response for 301 redirects
@ -132,10 +104,32 @@ sub vcl_hash {
} }
sub vcl_backend_fetch { sub vcl_backend_fetch {
# Be more lenient for slow servers on the fediverse
if bereq.url ~ "^/proxy/" {
set bereq.first_byte_timeout = 300s;
}
# CHUNKED SUPPORT # CHUNKED SUPPORT
if (bereq.http.x-range) { if (bereq.http.x-range) {
set bereq.http.Range = bereq.http.x-range; set bereq.http.Range = bereq.http.x-range;
} }
if (bereq.retries == 0) {
# Clean up the X-Varnish-Backend-503 flag that is used internally
# to mark broken backend responses that should be retried.
unset bereq.http.X-Varnish-Backend-503;
} else {
if (bereq.http.X-Varnish-Backend-503) {
if (bereq.method != "POST" &&
std.healthy(bereq.backend) &&
bereq.retries <= 4) {
# Flush broken backend response flag & try again.
unset bereq.http.X-Varnish-Backend-503;
} else {
return (abandon);
}
}
}
} }
sub vcl_deliver { sub vcl_deliver {
@ -145,3 +139,9 @@ sub vcl_deliver {
unset resp.http.CR; unset resp.http.CR;
} }
} }
sub vcl_backend_error {
# Retry broken backend responses.
set bereq.http.X-Varnish-Backend-503 = "1";
return (retry);
}

View file

@ -29,13 +29,13 @@ def system_info do
end end
defp assign_db_info(healthcheck) do defp assign_db_info(healthcheck) do
database = Application.get_env(:pleroma, Repo)[:database] database = Pleroma.Config.get([Repo, :database])
query = query =
"select state, count(pid) from pg_stat_activity where datname = '#{database}' group by state;" "select state, count(pid) from pg_stat_activity where datname = '#{database}' group by state;"
result = Repo.query!(query) result = Repo.query!(query)
pool_size = Application.get_env(:pleroma, Repo)[:pool_size] pool_size = Pleroma.Config.get([Repo, :pool_size])
db_info = db_info =
Enum.reduce(result.rows, %{active: 0, idle: 0}, fn [state, cnt], states -> Enum.reduce(result.rows, %{active: 0, idle: 0}, fn [state, cnt], states ->

View file

@ -5,6 +5,7 @@
defmodule Mix.Tasks.Pleroma.Database do defmodule Mix.Tasks.Pleroma.Database do
alias Mix.Tasks.Pleroma.Common alias Mix.Tasks.Pleroma.Common
alias Pleroma.Conversation alias Pleroma.Conversation
alias Pleroma.Object
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
require Logger require Logger
@ -23,6 +24,10 @@ defmodule Mix.Tasks.Pleroma.Database do
Options: Options:
- `--vacuum` - run `VACUUM FULL` after the embedded objects are replaced with their references - `--vacuum` - run `VACUUM FULL` after the embedded objects are replaced with their references
## Prune old objects from the database
mix pleroma.database prune_objects
## Create a conversation for all existing DMs. Can be safely re-run. ## Create a conversation for all existing DMs. Can be safely re-run.
mix pleroma.database bump_all_conversations mix pleroma.database bump_all_conversations
@ -72,4 +77,46 @@ def run(["update_users_following_followers_counts"]) do
Enum.each(users, &User.remove_duplicated_following/1) Enum.each(users, &User.remove_duplicated_following/1)
Enum.each(users, &User.update_follower_count/1) Enum.each(users, &User.update_follower_count/1)
end end
def run(["prune_objects" | args]) do
import Ecto.Query
{options, [], []} =
OptionParser.parse(
args,
strict: [
vacuum: :boolean
]
)
Common.start_pleroma()
deadline = Pleroma.Config.get([:instance, :remote_post_retention_days])
Logger.info("Pruning objects older than #{deadline} days")
time_deadline =
NaiveDateTime.utc_now()
|> NaiveDateTime.add(-(deadline * 86_400))
public = "https://www.w3.org/ns/activitystreams#Public"
from(o in Object,
where: fragment("?->'to' \\? ? OR ?->'cc' \\? ?", o.data, ^public, o.data, ^public),
where: o.inserted_at < ^time_deadline,
where:
fragment("split_part(?->>'actor', '/', 3) != ?", o.data, ^Pleroma.Web.Endpoint.host())
)
|> Repo.delete_all(timeout: :infinity)
if Keyword.get(options, :vacuum) do
Logger.info("Runnning VACUUM FULL")
Repo.query!(
"vacuum full;",
[],
timeout: :infinity
)
end
end
end end

View file

@ -10,6 +10,7 @@ defmodule Pleroma.Activity do
alias Pleroma.Notification alias Pleroma.Notification
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.ThreadMute
alias Pleroma.User alias Pleroma.User
import Ecto.Changeset import Ecto.Changeset
@ -37,6 +38,7 @@ defmodule Pleroma.Activity do
field(:local, :boolean, default: true) field(:local, :boolean, default: true)
field(:actor, :string) field(:actor, :string)
field(:recipients, {:array, :string}, default: []) field(:recipients, {:array, :string}, default: [])
field(:thread_muted?, :boolean, virtual: true)
# This is a fake relation, do not use outside of with_preloaded_bookmark/get_bookmark # This is a fake relation, do not use outside of with_preloaded_bookmark/get_bookmark
has_one(:bookmark, Bookmark) has_one(:bookmark, Bookmark)
has_many(:notifications, Notification, on_delete: :delete_all) has_many(:notifications, Notification, on_delete: :delete_all)
@ -90,6 +92,16 @@ def with_preloaded_bookmark(query, %User{} = user) do
def with_preloaded_bookmark(query, _), do: query def with_preloaded_bookmark(query, _), do: query
def with_set_thread_muted_field(query, %User{} = user) do
from([a] in query,
left_join: tm in ThreadMute,
on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data),
select: %Activity{a | thread_muted?: not is_nil(tm.id)}
)
end
def with_set_thread_muted_field(query, _), do: query
def get_by_ap_id(ap_id) do def get_by_ap_id(ap_id) do
Repo.one( Repo.one(
from( from(

View file

@ -110,6 +110,7 @@ def start(_type, _args) do
hackney_pool_children() ++ hackney_pool_children() ++
[ [
worker(Pleroma.Web.Federator.RetryQueue, []), worker(Pleroma.Web.Federator.RetryQueue, []),
worker(Pleroma.Web.OAuth.Token.CleanWorker, []),
worker(Pleroma.Stats, []), worker(Pleroma.Stats, []),
worker(Task, [&Pleroma.Web.Push.init/0], restart: :temporary, id: :web_push_init), worker(Task, [&Pleroma.Web.Push.init/0], restart: :temporary, id: :web_push_init),
worker(Task, [&Pleroma.Web.Federator.init/0], restart: :temporary, id: :federator_init) worker(Task, [&Pleroma.Web.Federator.init/0], restart: :temporary, id: :federator_init)
@ -131,19 +132,22 @@ def start(_type, _args) do
defp setup_instrumenters do defp setup_instrumenters do
require Prometheus.Registry require Prometheus.Registry
:ok = if Application.get_env(:prometheus, Pleroma.Repo.Instrumenter) do
:telemetry.attach( :ok =
"prometheus-ecto", :telemetry.attach(
[:pleroma, :repo, :query], "prometheus-ecto",
&Pleroma.Repo.Instrumenter.handle_event/4, [:pleroma, :repo, :query],
%{} &Pleroma.Repo.Instrumenter.handle_event/4,
) %{}
)
Pleroma.Repo.Instrumenter.setup()
end
Prometheus.Registry.register_collector(:prometheus_process_collector) Prometheus.Registry.register_collector(:prometheus_process_collector)
Pleroma.Web.Endpoint.MetricsExporter.setup() Pleroma.Web.Endpoint.MetricsExporter.setup()
Pleroma.Web.Endpoint.PipelineInstrumenter.setup() Pleroma.Web.Endpoint.PipelineInstrumenter.setup()
Pleroma.Web.Endpoint.Instrumenter.setup() Pleroma.Web.Endpoint.Instrumenter.setup()
Pleroma.Repo.Instrumenter.setup()
end end
def enabled_hackney_pools do def enabled_hackney_pools do

View file

@ -22,7 +22,7 @@ defmodule Pleroma.Emoji do
@ets __MODULE__.Ets @ets __MODULE__.Ets
@ets_options [:ordered_set, :protected, :named_table, {:read_concurrency, true}] @ets_options [:ordered_set, :protected, :named_table, {:read_concurrency, true}]
@groups Application.get_env(:pleroma, :emoji)[:groups] @groups Pleroma.Config.get([:emoji, :groups])
@doc false @doc false
def start_link do def start_link do
@ -112,7 +112,7 @@ defp load do
# Compat thing for old custom emoji handling & default emoji, # Compat thing for old custom emoji handling & default emoji,
# it should run even if there are no emoji packs # it should run even if there are no emoji packs
shortcode_globs = Application.get_env(:pleroma, :emoji)[:shortcode_globs] || [] shortcode_globs = Pleroma.Config.get([:emoji, :shortcode_globs], [])
emojis = emojis =
(load_from_file("config/emoji.txt") ++ (load_from_file("config/emoji.txt") ++

View file

@ -8,7 +8,7 @@ defmodule Pleroma.Formatter do
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.MediaProxy alias Pleroma.Web.MediaProxy
@safe_mention_regex ~r/^(\s*(?<mentions>@.+?\s+)+)(?<rest>.*)/ @safe_mention_regex ~r/^(\s*(?<mentions>(@.+?\s+){1,})+)(?<rest>.*)/s
@link_regex ~r"((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+"ui @link_regex ~r"((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+"ui
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/ @markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/

View file

@ -104,7 +104,6 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
paragraphs, breaks and links are allowed through the filter. paragraphs, breaks and links are allowed through the filter.
""" """
@markup Application.get_env(:pleroma, :markup)
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], []) @valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
require HtmlSanitizeEx.Scrubber.Meta require HtmlSanitizeEx.Scrubber.Meta
@ -142,9 +141,7 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
Meta.allow_tag_with_these_attributes("span", []) Meta.allow_tag_with_these_attributes("span", [])
# allow inline images for custom emoji # allow inline images for custom emoji
@allow_inline_images Keyword.get(@markup, :allow_inline_images) if Pleroma.Config.get([:markup, :allow_inline_images]) do
if @allow_inline_images do
# restrict img tags to http/https only, because of MediaProxy. # restrict img tags to http/https only, because of MediaProxy.
Meta.allow_tag_with_uri_attributes("img", ["src"], ["http", "https"]) Meta.allow_tag_with_uri_attributes("img", ["src"], ["http", "https"])
@ -168,7 +165,6 @@ defmodule Pleroma.HTML.Scrubber.Default do
# credo:disable-for-previous-line # credo:disable-for-previous-line
# No idea how to fix this one… # No idea how to fix this one…
@markup Application.get_env(:pleroma, :markup)
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], []) @valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
Meta.remove_cdata_sections_before_scrub() Meta.remove_cdata_sections_before_scrub()
@ -213,7 +209,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.allow_tag_with_this_attribute_values("span", "class", ["h-card"]) Meta.allow_tag_with_this_attribute_values("span", "class", ["h-card"])
Meta.allow_tag_with_these_attributes("span", []) Meta.allow_tag_with_these_attributes("span", [])
@allow_inline_images Keyword.get(@markup, :allow_inline_images) @allow_inline_images Pleroma.Config.get([:markup, :allow_inline_images])
if @allow_inline_images do if @allow_inline_images do
# restrict img tags to http/https only, because of MediaProxy. # restrict img tags to http/https only, because of MediaProxy.
@ -228,9 +224,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
]) ])
end end
@allow_tables Keyword.get(@markup, :allow_tables) if Pleroma.Config.get([:markup, :allow_tables]) do
if @allow_tables do
Meta.allow_tag_with_these_attributes("table", []) Meta.allow_tag_with_these_attributes("table", [])
Meta.allow_tag_with_these_attributes("tbody", []) Meta.allow_tag_with_these_attributes("tbody", [])
Meta.allow_tag_with_these_attributes("td", []) Meta.allow_tag_with_these_attributes("td", [])
@ -239,9 +233,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.allow_tag_with_these_attributes("tr", []) Meta.allow_tag_with_these_attributes("tr", [])
end end
@allow_headings Keyword.get(@markup, :allow_headings) if Pleroma.Config.get([:markup, :allow_headings]) do
if @allow_headings do
Meta.allow_tag_with_these_attributes("h1", []) Meta.allow_tag_with_these_attributes("h1", [])
Meta.allow_tag_with_these_attributes("h2", []) Meta.allow_tag_with_these_attributes("h2", [])
Meta.allow_tag_with_these_attributes("h3", []) Meta.allow_tag_with_these_attributes("h3", [])
@ -249,9 +241,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.allow_tag_with_these_attributes("h5", []) Meta.allow_tag_with_these_attributes("h5", [])
end end
@allow_fonts Keyword.get(@markup, :allow_fonts) if Pleroma.Config.get([:markup, :allow_fonts]) do
if @allow_fonts do
Meta.allow_tag_with_these_attributes("font", ["face"]) Meta.allow_tag_with_these_attributes("font", ["face"])
end end

View file

@ -8,7 +8,7 @@ defmodule Pleroma.HTTP.Connection do
""" """
@hackney_options [ @hackney_options [
connect_timeout: 2_000, connect_timeout: 10_000,
recv_timeout: 20_000, recv_timeout: 20_000,
follow_redirect: true, follow_redirect: true,
pool: :federation pool: :federation
@ -32,9 +32,11 @@ def new(opts \\ []) do
defp hackney_options(opts) do defp hackney_options(opts) do
options = Keyword.get(opts, :adapter, []) options = Keyword.get(opts, :adapter, [])
adapter_options = Pleroma.Config.get([:http, :adapter], []) adapter_options = Pleroma.Config.get([:http, :adapter], [])
proxy_url = Pleroma.Config.get([:http, :proxy_url], nil)
@hackney_options @hackney_options
|> Keyword.merge(adapter_options) |> Keyword.merge(adapter_options)
|> Keyword.merge(options) |> Keyword.merge(options)
|> Keyword.merge(proxy: proxy_url)
end end
end end

View file

@ -65,12 +65,9 @@ defp process_sni_options(options, url) do
end end
def process_request_options(options) do def process_request_options(options) do
config = Application.get_env(:pleroma, :http, []) case Pleroma.Config.get([:http, :proxy_url]) do
proxy = Keyword.get(config, :proxy_url, nil)
case proxy do
nil -> options nil -> options
_ -> options ++ [proxy: proxy] proxy -> options ++ [proxy: proxy]
end end
end end

44
lib/pleroma/keys.ex Normal file
View file

@ -0,0 +1,44 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Keys do
# Native generation of RSA keys is only available since OTP 20+ and in default build conditions
# We try at compile time to generate natively an RSA key otherwise we fallback on the old way.
try do
_ = :public_key.generate_key({:rsa, 2048, 65_537})
def generate_rsa_pem do
key = :public_key.generate_key({:rsa, 2048, 65_537})
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
pem = :public_key.pem_encode([entry]) |> String.trim_trailing()
{:ok, pem}
end
rescue
_ ->
def generate_rsa_pem do
port = Port.open({:spawn, "openssl genrsa"}, [:binary])
{:ok, pem} =
receive do
{^port, {:data, pem}} -> {:ok, pem}
end
Port.close(port)
if Regex.match?(~r/RSA PRIVATE KEY/, pem) do
{:ok, pem}
else
:error
end
end
end
def keys_from_pem(pem) do
[private_key_code] = :public_key.pem_decode(pem)
private_key = :public_key.pem_entry_decode(private_key_code)
{:RSAPrivateKey, _, modulus, exponent, _, _, _, _, _, _, _} = private_key
public_key = {:RSAPublicKey, modulus, exponent}
{:ok, private_key, public_key}
end
end

View file

@ -133,6 +133,13 @@ def delete(%Object{data: %{"id" => id}} = object) do
end end
end end
def prune(%Object{data: %{"id" => id}} = object) do
with {:ok, object} <- Repo.delete(object),
{:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do
{:ok, object}
end
end
def set_cache(%Object{data: %{"id" => ap_id}} = object) do def set_cache(%Object{data: %{"id" => ap_id}} = object) do
Cachex.put(:object_cache, "object:#{ap_id}", object) Cachex.put(:object_cache, "object:#{ap_id}", object)
{:ok, object} {:ok, object}

View file

@ -1,4 +1,5 @@
defmodule Pleroma.Object.Fetcher do defmodule Pleroma.Object.Fetcher do
alias Pleroma.HTTP
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Object.Containment alias Pleroma.Object.Containment
alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Transmogrifier
@ -6,7 +7,18 @@ defmodule Pleroma.Object.Fetcher do
require Logger require Logger
@httpoison Application.get_env(:pleroma, :httpoison) defp reinject_object(data) do
Logger.debug("Reinjecting object #{data["id"]}")
with data <- Transmogrifier.fix_object(data),
{:ok, object} <- Object.create(data) do
{:ok, object}
else
e ->
Logger.error("Error while processing object: #{inspect(e)}")
{:error, e}
end
end
# TODO: # TODO:
# This will create a Create activity, which we need internally at the moment. # This will create a Create activity, which we need internally at the moment.
@ -26,12 +38,17 @@ def fetch_object_from_id(id) do
"object" => data "object" => data
}, },
:ok <- Containment.contain_origin(id, params), :ok <- Containment.contain_origin(id, params),
{:ok, activity} <- Transmogrifier.handle_incoming(params) do {:ok, activity} <- Transmogrifier.handle_incoming(params),
{:ok, Object.normalize(activity, false)} {:object, _data, %Object{} = object} <-
{:object, data, Object.normalize(activity, false)} do
{:ok, object}
else else
{:error, {:reject, nil}} -> {:error, {:reject, nil}} ->
{:reject, nil} {:reject, nil}
{:object, data, nil} ->
reinject_object(data)
object = %Object{} -> object = %Object{} ->
{:ok, object} {:ok, object}
@ -60,7 +77,7 @@ def fetch_and_contain_remote_object_from_id(id) do
with true <- String.starts_with?(id, "http"), with true <- String.starts_with?(id, "http"),
{:ok, %{body: body, status: code}} when code in 200..299 <- {:ok, %{body: body, status: code}} when code in 200..299 <-
@httpoison.get( HTTP.get(
id, id,
[{:Accept, "application/activity+json"}] [{:Accept, "application/activity+json"}]
), ),

View file

@ -10,7 +10,7 @@ def init(options) do
end end
def call(conn, _opts) do def call(conn, _opts) do
if Keyword.get(Application.get_env(:pleroma, :instance), :federating) do if Pleroma.Config.get([:instance, :federating]) do
conn conn
else else
conn conn

View file

@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ReverseProxy do defmodule Pleroma.ReverseProxy do
alias Pleroma.HTTP
@keep_req_headers ~w(accept user-agent accept-encoding cache-control if-modified-since) ++ @keep_req_headers ~w(accept user-agent accept-encoding cache-control if-modified-since) ++
~w(if-unmodified-since if-none-match if-range range) ~w(if-unmodified-since if-none-match if-range range)
@resp_cache_headers ~w(etag date last-modified cache-control) @resp_cache_headers ~w(etag date last-modified cache-control)
@ -59,8 +61,7 @@ defmodule Pleroma.ReverseProxy do
* `http`: options for [hackney](https://github.com/benoitc/hackney). * `http`: options for [hackney](https://github.com/benoitc/hackney).
""" """
@hackney Application.get_env(:pleroma, :hackney, :hackney) @hackney Pleroma.Config.get(:hackney, :hackney)
@httpoison Application.get_env(:pleroma, :httpoison, HTTPoison)
@default_hackney_options [] @default_hackney_options []
@ -97,7 +98,7 @@ def call(conn = %{method: method}, url, opts) when method in @methods do
hackney_opts = hackney_opts =
@default_hackney_options @default_hackney_options
|> Keyword.merge(Keyword.get(opts, :http, [])) |> Keyword.merge(Keyword.get(opts, :http, []))
|> @httpoison.process_request_options() |> HTTP.process_request_options()
req_headers = build_req_headers(conn.req_headers, opts) req_headers = build_req_headers(conn.req_headers, opts)

View file

@ -5,11 +5,10 @@
defmodule Pleroma.Signature do defmodule Pleroma.Signature do
@behaviour HTTPSignatures.Adapter @behaviour HTTPSignatures.Adapter
alias Pleroma.Keys
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.Salmon
alias Pleroma.Web.WebFinger
def fetch_public_key(conn) do def fetch_public_key(conn) do
with actor_id <- Utils.get_ap_id(conn.params["actor"]), with actor_id <- Utils.get_ap_id(conn.params["actor"]),
@ -33,8 +32,8 @@ def refetch_public_key(conn) do
end end
def sign(%User{} = user, headers) do def sign(%User{} = user, headers) do
with {:ok, %{info: %{keys: keys}}} <- WebFinger.ensure_keys_present(user), with {:ok, %{info: %{keys: keys}}} <- User.ensure_keys_present(user),
{:ok, private_key, _} <- Salmon.keys_from_pem(keys) do {:ok, private_key, _} <- Keys.keys_from_pem(keys) do
HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers) HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers)
end end
end end

View file

@ -4,11 +4,10 @@
defmodule Pleroma.Uploaders.MDII do defmodule Pleroma.Uploaders.MDII do
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.HTTP
@behaviour Pleroma.Uploaders.Uploader @behaviour Pleroma.Uploaders.Uploader
@httpoison Application.get_env(:pleroma, :httpoison)
# MDII-hosted images are never passed through the MediaPlug; only local media. # MDII-hosted images are never passed through the MediaPlug; only local media.
# Delegate to Pleroma.Uploaders.Local # Delegate to Pleroma.Uploaders.Local
def get_file(file) do def get_file(file) do
@ -25,7 +24,7 @@ def put_file(upload) do
query = "#{cgi}?#{extension}" query = "#{cgi}?#{extension}"
with {:ok, %{status: 200, body: body}} <- with {:ok, %{status: 200, body: body}} <-
@httpoison.post(query, file_data, [], adapter: [pool: :default]) do HTTP.post(query, file_data, [], adapter: [pool: :default]) do
remote_file_name = String.split(body) |> List.first() remote_file_name = String.split(body) |> List.first()
public_url = "#{files}/#{remote_file_name}.#{extension}" public_url = "#{files}/#{remote_file_name}.#{extension}"
{:ok, {:url, public_url}} {:ok, {:url, public_url}}

View file

@ -10,6 +10,7 @@ defmodule Pleroma.User do
alias Comeonin.Pbkdf2 alias Comeonin.Pbkdf2
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Keys
alias Pleroma.Notification alias Pleroma.Notification
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Registration alias Pleroma.Registration
@ -365,9 +366,7 @@ def follow_all(follower, followeds) do
end end
def follow(%User{} = follower, %User{info: info} = followed) do def follow(%User{} = follower, %User{info: info} = followed) do
user_config = Application.get_env(:pleroma, :user) deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
deny_follow_blocked = Keyword.get(user_config, :deny_follow_blocked)
ap_followers = followed.follower_address ap_followers = followed.follower_address
cond do cond do
@ -759,7 +758,7 @@ def search_query(query, for_user) do
from(s in subquery(boost_search_rank_query(distinct_query, for_user)), from(s in subquery(boost_search_rank_query(distinct_query, for_user)),
order_by: [desc: s.search_rank], order_by: [desc: s.search_rank],
limit: 20 limit: 40
) )
end end
@ -1402,4 +1401,44 @@ def toggle_confirmation(%User{} = user) do
|> put_embed(:info, info_changeset) |> put_embed(:info, info_changeset)
|> update_and_set_cache() |> update_and_set_cache()
end end
def get_mascot(%{info: %{mascot: %{} = mascot}}) when not is_nil(mascot) do
mascot
end
def get_mascot(%{info: %{mascot: mascot}}) when is_nil(mascot) do
# use instance-default
config = Pleroma.Config.get([:assets, :mascots])
default_mascot = Pleroma.Config.get([:assets, :default_mascot])
mascot = Keyword.get(config, default_mascot)
%{
"id" => "default-mascot",
"url" => mascot[:url],
"preview_url" => mascot[:url],
"pleroma" => %{
"mime_type" => mascot[:mime_type]
}
}
end
def ensure_keys_present(user) do
info = user.info
if info.keys do
{:ok, user}
else
{:ok, pem} = Keys.generate_rsa_pem()
info_cng =
info
|> User.Info.set_keys(pem)
cng =
Ecto.Changeset.change(user)
|> Ecto.Changeset.put_embed(:info, info_cng)
update_and_set_cache(cng)
end
end
end end

View file

@ -43,6 +43,7 @@ defmodule Pleroma.User.Info do
field(:hide_favorites, :boolean, default: true) field(:hide_favorites, :boolean, default: true)
field(:pinned_activities, {:array, :string}, default: []) field(:pinned_activities, {:array, :string}, default: [])
field(:flavour, :string, default: nil) field(:flavour, :string, default: nil)
field(:mascot, :map, default: nil)
field(:emoji, {:array, :map}, default: []) field(:emoji, {:array, :map}, default: [])
field(:notification_settings, :map, field(:notification_settings, :map,
@ -248,6 +249,14 @@ def mastodon_flavour_update(info, flavour) do
|> validate_required([:flavour]) |> validate_required([:flavour])
end end
def mascot_update(info, url) do
params = %{mascot: url}
info
|> cast(params, [:mascot])
|> validate_required([:mascot])
end
def set_source_data(info, source_data) do def set_source_data(info, source_data) do
params = %{source_data: source_data} params = %{source_data: source_data}

View file

@ -411,16 +411,12 @@ def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ tru
end end
def block(blocker, blocked, activity_id \\ nil, local \\ true) do def block(blocker, blocked, activity_id \\ nil, local \\ true) do
ap_config = Application.get_env(:pleroma, :activitypub) outgoing_blocks = Pleroma.Config.get([:activitypub, :outgoing_blocks])
unfollow_blocked = Keyword.get(ap_config, :unfollow_blocked) unfollow_blocked = Pleroma.Config.get([:activitypub, :unfollow_blocked])
outgoing_blocks = Keyword.get(ap_config, :outgoing_blocks)
with true <- unfollow_blocked do if unfollow_blocked do
follow_activity = fetch_latest_follow(blocker, blocked) follow_activity = fetch_latest_follow(blocker, blocked)
if follow_activity, do: unfollow(blocker, blocked, nil, local)
if follow_activity do
unfollow(blocker, blocked, nil, local)
end
end end
with true <- outgoing_blocks, with true <- outgoing_blocks,
@ -666,20 +662,6 @@ defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
defp restrict_tag(query, _), do: query defp restrict_tag(query, _), do: query
defp restrict_to_cc(query, recipients_to, recipients_cc) do
from(
activity in query,
where:
fragment(
"(?->'to' \\?| ?) or (?->'cc' \\?| ?)",
activity.data,
^recipients_to,
activity.data,
^recipients_cc
)
)
end
defp restrict_recipients(query, [], _user), do: query defp restrict_recipients(query, [], _user), do: query
defp restrict_recipients(query, recipients, nil) do defp restrict_recipients(query, recipients, nil) do
@ -859,6 +841,13 @@ defp maybe_preload_bookmarks(query, opts) do
|> Activity.with_preloaded_bookmark(opts["user"]) |> Activity.with_preloaded_bookmark(opts["user"])
end end
defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
defp maybe_set_thread_muted_field(query, opts) do
query
|> Activity.with_set_thread_muted_field(opts["user"])
end
defp maybe_order(query, %{order: :desc}) do defp maybe_order(query, %{order: :desc}) do
query query
|> order_by(desc: :id) |> order_by(desc: :id)
@ -877,6 +866,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
base_query base_query
|> maybe_preload_objects(opts) |> maybe_preload_objects(opts)
|> maybe_preload_bookmarks(opts) |> maybe_preload_bookmarks(opts)
|> maybe_set_thread_muted_field(opts)
|> maybe_order(opts) |> maybe_order(opts)
|> restrict_recipients(recipients, opts["user"]) |> restrict_recipients(recipients, opts["user"])
|> restrict_tag(opts) |> restrict_tag(opts)
@ -907,9 +897,18 @@ def fetch_activities(recipients, opts \\ %{}) do
|> Enum.reverse() |> Enum.reverse()
end end
def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
from(activity in query,
where:
fragment("? && ?", activity.recipients, ^recipients) or
(fragment("? && ?", activity.recipients, ^recipients_with_public) and
"https://www.w3.org/ns/activitystreams#Public" in activity.recipients)
)
end
def fetch_activities_bounded(recipients, recipients_with_public, opts \\ %{}) do
fetch_activities_query([], opts) fetch_activities_query([], opts)
|> restrict_to_cc(recipients_to, recipients_cc) |> fetch_activities_bounded_query(recipients, recipients_with_public)
|> Pagination.fetch_paginated(opts) |> Pagination.fetch_paginated(opts)
|> Enum.reverse() |> Enum.reverse()
end end
@ -927,7 +926,7 @@ def upload(file, opts \\ []) do
end end
end end
def user_data_from_user_object(data) do defp object_to_user_data(data) do
avatar = avatar =
data["icon"]["url"] && data["icon"]["url"] &&
%{ %{
@ -974,9 +973,19 @@ def user_data_from_user_object(data) do
{:ok, user_data} {:ok, user_data}
end end
def user_data_from_user_object(data) do
with {:ok, data} <- MRF.filter(data),
{:ok, data} <- object_to_user_data(data) do
{:ok, data}
else
e -> {:error, e}
end
end
def fetch_and_prepare_user_from_ap_id(ap_id) do def fetch_and_prepare_user_from_ap_id(ap_id) do
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
user_data_from_user_object(data) {:ok, data} <- user_data_from_user_object(data) do
{:ok, data}
else else
e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}") e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
end end

View file

@ -27,7 +27,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
plug(:relay_active? when action in [:relay]) plug(:relay_active? when action in [:relay])
def relay_active?(conn, _) do def relay_active?(conn, _) do
if Keyword.get(Application.get_env(:pleroma, :instance), :allow_relay) do if Pleroma.Config.get([:instance, :allow_relay]) do
conn conn
else else
conn conn
@ -39,7 +39,7 @@ def relay_active?(conn, _) do
def user(conn, %{"nickname" => nickname}) do def user(conn, %{"nickname" => nickname}) do
with %User{} = user <- User.get_cached_by_nickname(nickname), with %User{} = user <- User.get_cached_by_nickname(nickname),
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do {:ok, user} <- User.ensure_keys_present(user) do
conn conn
|> put_resp_header("content-type", "application/activity+json") |> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("user.json", %{user: user})) |> json(UserView.render("user.json", %{user: user}))
@ -106,7 +106,7 @@ def activity(conn, %{"uuid" => uuid}) do
def following(conn, %{"nickname" => nickname, "page" => page}) do def following(conn, %{"nickname" => nickname, "page" => page}) do
with %User{} = user <- User.get_cached_by_nickname(nickname), with %User{} = user <- User.get_cached_by_nickname(nickname),
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do {:ok, user} <- User.ensure_keys_present(user) do
{page, _} = Integer.parse(page) {page, _} = Integer.parse(page)
conn conn
@ -117,7 +117,7 @@ def following(conn, %{"nickname" => nickname, "page" => page}) do
def following(conn, %{"nickname" => nickname}) do def following(conn, %{"nickname" => nickname}) do
with %User{} = user <- User.get_cached_by_nickname(nickname), with %User{} = user <- User.get_cached_by_nickname(nickname),
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do {:ok, user} <- User.ensure_keys_present(user) do
conn conn
|> put_resp_header("content-type", "application/activity+json") |> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("following.json", %{user: user})) |> json(UserView.render("following.json", %{user: user}))
@ -126,7 +126,7 @@ def following(conn, %{"nickname" => nickname}) do
def followers(conn, %{"nickname" => nickname, "page" => page}) do def followers(conn, %{"nickname" => nickname, "page" => page}) do
with %User{} = user <- User.get_cached_by_nickname(nickname), with %User{} = user <- User.get_cached_by_nickname(nickname),
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do {:ok, user} <- User.ensure_keys_present(user) do
{page, _} = Integer.parse(page) {page, _} = Integer.parse(page)
conn conn
@ -137,7 +137,7 @@ def followers(conn, %{"nickname" => nickname, "page" => page}) do
def followers(conn, %{"nickname" => nickname}) do def followers(conn, %{"nickname" => nickname}) do
with %User{} = user <- User.get_cached_by_nickname(nickname), with %User{} = user <- User.get_cached_by_nickname(nickname),
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do {:ok, user} <- User.ensure_keys_present(user) do
conn conn
|> put_resp_header("content-type", "application/activity+json") |> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("followers.json", %{user: user})) |> json(UserView.render("followers.json", %{user: user}))
@ -146,7 +146,7 @@ def followers(conn, %{"nickname" => nickname}) do
def outbox(conn, %{"nickname" => nickname} = params) do def outbox(conn, %{"nickname" => nickname} = params) do
with %User{} = user <- User.get_cached_by_nickname(nickname), with %User{} = user <- User.get_cached_by_nickname(nickname),
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do {:ok, user} <- User.ensure_keys_present(user) do
conn conn
|> put_resp_header("content-type", "application/activity+json") |> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("outbox.json", %{user: user, max_id: params["max_id"]})) |> json(UserView.render("outbox.json", %{user: user, max_id: params["max_id"]}))
@ -195,7 +195,7 @@ def inbox(conn, params) do
def relay(conn, _params) do def relay(conn, _params) do
with %User{} = user <- Relay.get_actor(), with %User{} = user <- Relay.get_actor(),
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do {:ok, user} <- User.ensure_keys_present(user) do
conn conn
|> put_resp_header("content-type", "application/activity+json") |> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("user.json", %{user: user})) |> json(UserView.render("user.json", %{user: user}))

View file

@ -17,9 +17,7 @@ def filter(object) do
end end
def get_policies do def get_policies do
Application.get_env(:pleroma, :instance, []) Pleroma.Config.get([:instance, :rewrite_policy], []) |> get_policies()
|> Keyword.get(:rewrite_policy, [])
|> get_policies()
end end
defp get_policies(policy) when is_atom(policy), do: [policy] defp get_policies(policy) when is_atom(policy), do: [policy]

View file

@ -74,8 +74,7 @@ defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do
actor_host actor_host
), ),
user <- User.get_cached_by_ap_id(object["actor"]), user <- User.get_cached_by_ap_id(object["actor"]),
true <- "https://www.w3.org/ns/activitystreams#Public" in object["to"], true <- "https://www.w3.org/ns/activitystreams#Public" in object["to"] do
true <- user.follower_address in object["cc"] do
to = to =
List.delete(object["to"], "https://www.w3.org/ns/activitystreams#Public") ++ List.delete(object["to"], "https://www.w3.org/ns/activitystreams#Public") ++
[user.follower_address] [user.follower_address]
@ -94,18 +93,63 @@ defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do
{:ok, object} {:ok, object}
end end
defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do
if actor_host in Pleroma.Config.get([:mrf_simple, :report_removal]) do
{:reject, nil}
else
{:ok, object}
end
end
defp check_report_removal(_actor_info, object), do: {:ok, object}
defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = object) do
if actor_host in Pleroma.Config.get([:mrf_simple, :avatar_removal]) do
{:ok, Map.delete(object, "icon")}
else
{:ok, object}
end
end
defp check_avatar_removal(_actor_info, object), do: {:ok, object}
defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = object) do
if actor_host in Pleroma.Config.get([:mrf_simple, :banner_removal]) do
{:ok, Map.delete(object, "image")}
else
{:ok, object}
end
end
defp check_banner_removal(_actor_info, object), do: {:ok, object}
@impl true @impl true
def filter(object) do def filter(%{"actor" => actor} = object) do
actor_info = URI.parse(object["actor"]) actor_info = URI.parse(actor)
with {:ok, object} <- check_accept(actor_info, object), with {:ok, object} <- check_accept(actor_info, object),
{:ok, object} <- check_reject(actor_info, object), {:ok, object} <- check_reject(actor_info, object),
{:ok, object} <- check_media_removal(actor_info, object), {:ok, object} <- check_media_removal(actor_info, object),
{:ok, object} <- check_media_nsfw(actor_info, object), {:ok, object} <- check_media_nsfw(actor_info, object),
{:ok, object} <- check_ftl_removal(actor_info, object) do {:ok, object} <- check_ftl_removal(actor_info, object),
{:ok, object} <- check_report_removal(actor_info, object) do
{:ok, object} {:ok, object}
else else
_e -> {:reject, nil} _e -> {:reject, nil}
end end
end end
def filter(%{"id" => actor, "type" => obj_type} = object)
when obj_type in ["Application", "Group", "Organization", "Person", "Service"] do
actor_info = URI.parse(actor)
with {:ok, object} <- check_avatar_removal(actor_info, object),
{:ok, object} <- check_banner_removal(actor_info, object) do
{:ok, object}
else
_e -> {:reject, nil}
end
end
def filter(object), do: {:ok, object}
end end

View file

@ -19,10 +19,12 @@ defp filter_by_list(%{"actor" => actor} = object, allow_list) do
end end
@impl true @impl true
def filter(object) do def filter(%{"actor" => actor} = object) do
actor_info = URI.parse(object["actor"]) actor_info = URI.parse(actor)
allow_list = Config.get([:mrf_user_allowlist, String.to_atom(actor_info.host)], []) allow_list = Config.get([:mrf_user_allowlist, String.to_atom(actor_info.host)], [])
filter_by_list(object, allow_list) filter_by_list(object, allow_list)
end end
def filter(object), do: {:ok, object}
end end

View file

@ -5,6 +5,7 @@
defmodule Pleroma.Web.ActivityPub.Publisher do defmodule Pleroma.Web.ActivityPub.Publisher do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.HTTP
alias Pleroma.Instances alias Pleroma.Instances
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Relay
@ -16,8 +17,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
require Logger require Logger
@httpoison Application.get_env(:pleroma, :httpoison)
@moduledoc """ @moduledoc """
ActivityPub outgoing federation module. ActivityPub outgoing federation module.
""" """
@ -63,7 +62,7 @@ def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = pa
with {:ok, %{status: code}} when code in 200..299 <- with {:ok, %{status: code}} when code in 200..299 <-
result = result =
@httpoison.post( HTTP.post(
inbox, inbox,
json, json,
[ [

View file

@ -94,7 +94,10 @@ def fix_explicit_addressing(object) do
object object
|> Utils.determine_explicit_mentions() |> Utils.determine_explicit_mentions()
explicit_mentions = explicit_mentions ++ ["https://www.w3.org/ns/activitystreams#Public"] follower_collection = User.get_cached_by_ap_id(Containment.get_actor(object)).follower_address
explicit_mentions =
explicit_mentions ++ ["https://www.w3.org/ns/activitystreams#Public", follower_collection]
object object
|> fix_explicit_addressing(explicit_mentions) |> fix_explicit_addressing(explicit_mentions)

View file

@ -5,6 +5,7 @@
defmodule Pleroma.Web.ActivityPub.UserView do defmodule Pleroma.Web.ActivityPub.UserView do
use Pleroma.Web, :view use Pleroma.Web, :view
alias Pleroma.Keys
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
@ -12,8 +13,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.Endpoint alias Pleroma.Web.Endpoint
alias Pleroma.Web.Router.Helpers alias Pleroma.Web.Router.Helpers
alias Pleroma.Web.Salmon
alias Pleroma.Web.WebFinger
import Ecto.Query import Ecto.Query
@ -34,8 +33,8 @@ def render("endpoints.json", _), do: %{}
# the instance itself is not a Person, but instead an Application # the instance itself is not a Person, but instead an Application
def render("user.json", %{user: %{nickname: nil} = user}) do def render("user.json", %{user: %{nickname: nil} = user}) do
{:ok, user} = WebFinger.ensure_keys_present(user) {:ok, user} = User.ensure_keys_present(user)
{:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys) {:ok, _, public_key} = Keys.keys_from_pem(user.info.keys)
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
public_key = :public_key.pem_encode([public_key]) public_key = :public_key.pem_encode([public_key])
@ -62,8 +61,8 @@ def render("user.json", %{user: %{nickname: nil} = user}) do
end end
def render("user.json", %{user: user}) do def render("user.json", %{user: user}) do
{:ok, user} = WebFinger.ensure_keys_present(user) {:ok, user} = User.ensure_keys_present(user)
{:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys) {:ok, _, public_key} = Keys.keys_from_pem(user.info.keys)
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
public_key = :public_key.pem_encode([public_key]) public_key = :public_key.pem_encode([public_key])

View file

@ -16,17 +16,32 @@ defmodule Pleroma.Web.Endpoint do
plug(Pleroma.Plugs.UploadedMedia) plug(Pleroma.Plugs.UploadedMedia)
@static_cache_control "public, no-cache"
# InstanceStatic needs to be before Plug.Static to be able to override shipped-static files # InstanceStatic needs to be before Plug.Static to be able to override shipped-static files
# If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well # If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well
plug(Pleroma.Plugs.InstanceStatic, at: "/") # Cache-control headers are duplicated in case we turn off etags in the future
plug(Pleroma.Plugs.InstanceStatic,
at: "/",
gzip: true,
cache_control_for_etags: @static_cache_control,
headers: %{
"cache-control" => @static_cache_control
}
)
plug( plug(
Plug.Static, Plug.Static,
at: "/", at: "/",
from: :pleroma, from: :pleroma,
only: only:
~w(index.html robots.txt static finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc) ~w(index.html robots.txt static finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc),
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
gzip: true,
cache_control_for_etags: @static_cache_control,
headers: %{
"cache-control" => @static_cache_control
}
) )
plug(Plug.Static.IndexHtml, at: "/pleroma/admin/") plug(Plug.Static.IndexHtml, at: "/pleroma/admin/")
@ -51,7 +66,7 @@ defmodule Pleroma.Web.Endpoint do
parsers: [:urlencoded, :multipart, :json], parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"], pass: ["*/*"],
json_decoder: Jason, json_decoder: Jason,
length: Application.get_env(:pleroma, :instance) |> Keyword.get(:upload_limit), length: Pleroma.Config.get([:instance, :upload_limit]),
body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []} body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
) )

View file

@ -11,14 +11,11 @@ defmodule Pleroma.Web.Federator do
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.Federator.Publisher
alias Pleroma.Web.Federator.RetryQueue alias Pleroma.Web.Federator.RetryQueue
alias Pleroma.Web.WebFinger alias Pleroma.Web.OStatus
alias Pleroma.Web.Websub alias Pleroma.Web.Websub
require Logger require Logger
@websub Application.get_env(:pleroma, :websub)
@ostatus Application.get_env(:pleroma, :ostatus)
def init do def init do
# 1 minute # 1 minute
Process.sleep(1000 * 60) Process.sleep(1000 * 60)
@ -77,9 +74,8 @@ def perform(:request_subscription, websub) do
def perform(:publish, activity) do def perform(:publish, activity) do
Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end) Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end)
with actor when not is_nil(actor) <- User.get_cached_by_ap_id(activity.data["actor"]) do with %User{} = actor <- User.get_cached_by_ap_id(activity.data["actor"]),
{:ok, actor} = WebFinger.ensure_keys_present(actor) {:ok, actor} <- User.ensure_keys_present(actor) do
Publisher.publish(actor, activity) Publisher.publish(actor, activity)
end end
end end
@ -89,12 +85,12 @@ def perform(:verify_websub, websub) do
"Running WebSub verification for #{websub.id} (#{websub.topic}, #{websub.callback})" "Running WebSub verification for #{websub.id} (#{websub.topic}, #{websub.callback})"
end) end)
@websub.verify(websub) Websub.verify(websub)
end end
def perform(:incoming_doc, doc) do def perform(:incoming_doc, doc) do
Logger.info("Got document, trying to parse") Logger.info("Got document, trying to parse")
@ostatus.handle_incoming(doc) OStatus.handle_incoming(doc)
end end
def perform(:incoming_ap_doc, params) do def perform(:incoming_ap_doc, params) do

View file

@ -11,6 +11,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
alias Pleroma.Conversation.Participation alias Pleroma.Conversation.Participation
alias Pleroma.Filter alias Pleroma.Filter
alias Pleroma.Formatter alias Pleroma.Formatter
alias Pleroma.HTTP
alias Pleroma.Notification alias Pleroma.Notification
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Object.Fetcher alias Pleroma.Object.Fetcher
@ -55,7 +56,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
when action in [:account_register] when action in [:account_register]
) )
@httpoison Application.get_env(:pleroma, :httpoison)
@local_mastodon_name "Mastodon-Local" @local_mastodon_name "Mastodon-Local"
action_fallback(:errors) action_fallback(:errors)
@ -772,6 +772,41 @@ def upload(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do
end end
end end
def set_mascot(%{assigns: %{user: user}} = conn, %{"file" => file}) do
with {:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)),
%{} = attachment_data <- Map.put(object.data, "id", object.id),
%{type: type} = rendered <-
StatusView.render("attachment.json", %{attachment: attachment_data}) do
# Reject if not an image
if type == "image" do
# Sure!
# Save to the user's info
info_changeset = User.Info.mascot_update(user.info, rendered)
user_changeset =
user
|> Ecto.Changeset.change()
|> Ecto.Changeset.put_embed(:info, info_changeset)
{:ok, _user} = User.update_and_set_cache(user_changeset)
conn
|> json(rendered)
else
conn
|> put_resp_content_type("application/json")
|> send_resp(415, Jason.encode!(%{"error" => "mascots can only be images"}))
end
end
end
def get_mascot(%{assigns: %{user: user}} = conn, _params) do
mascot = User.get_mascot(user)
conn
|> json(mascot)
end
def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{data: %{"object" => object}} <- Repo.get(Activity, id), with %Activity{data: %{"object" => object}} <- Repo.get(Activity, id),
%Object{data: %{"likes" => likes}} <- Object.normalize(object) do %Object{data: %{"likes" => likes}} <- Object.normalize(object) do
@ -1114,7 +1149,7 @@ def status_search(user, query) do
from([a, o] in Activity.with_preloaded_object(Activity), from([a, o] in Activity.with_preloaded_object(Activity),
where: fragment("?->>'type' = 'Create'", a.data), where: fragment("?->>'type' = 'Create'", a.data),
where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients, where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients,
limit: 20 limit: 40
) )
q = q =
@ -1394,7 +1429,7 @@ def index(%{assigns: %{user: user}} = conn, _params) do
display_sensitive_media: false, display_sensitive_media: false,
reduce_motion: false, reduce_motion: false,
max_toot_chars: limit, max_toot_chars: limit,
mascot: "/images/pleroma-fox-tan-smol.png" mascot: User.get_mascot(user)["url"]
}, },
poll_limits: Config.get([:instance, :poll_limits]), poll_limits: Config.get([:instance, :poll_limits]),
rights: %{ rights: %{
@ -1722,7 +1757,7 @@ def suggestions(%{assigns: %{user: user}} = conn, _) do
|> String.replace("{{user}}", user) |> String.replace("{{user}}", user)
with {:ok, %{status: 200, body: body}} <- with {:ok, %{status: 200, body: body}} <-
@httpoison.get( HTTP.get(
url, url,
[], [],
adapter: [ adapter: [

View file

@ -112,7 +112,7 @@ defp do_render("account.json", %{user: user} = opts) do
fields: fields, fields: fields,
bot: bot, bot: bot,
source: %{ source: %{
note: "", note: HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
sensitive: false, sensitive: false,
pleroma: %{} pleroma: %{}
}, },

View file

@ -157,6 +157,12 @@ def render("status.json", %{activity: %{data: %{"object" => _object}} = activity
bookmarked = Activity.get_bookmark(activity, opts[:for]) != nil bookmarked = Activity.get_bookmark(activity, opts[:for]) != nil
thread_muted? =
case activity.thread_muted? do
thread_muted? when is_boolean(thread_muted?) -> thread_muted?
nil -> CommonAPI.thread_muted?(user, activity)
end
attachment_data = object.data["attachment"] || [] attachment_data = object.data["attachment"] || []
attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment) attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
@ -228,7 +234,7 @@ def render("status.json", %{activity: %{data: %{"object" => _object}} = activity
reblogged: reblogged?(activity, opts[:for]), reblogged: reblogged?(activity, opts[:for]),
favourited: present?(favorited), favourited: present?(favorited),
bookmarked: present?(bookmarked), bookmarked: present?(bookmarked),
muted: CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user), muted: thread_muted? || User.mutes?(opts[:for], user),
pinned: pinned?(activity, user), pinned: pinned?(activity, user),
sensitive: sensitive, sensitive: sensitive,
spoiler_text: summary_html, spoiler_text: summary_html,
@ -285,8 +291,8 @@ def render("card.json", %{rich_media: rich_media, page_url: page_url}) do
provider_url: page_url_data.scheme <> "://" <> page_url_data.host, provider_url: page_url_data.scheme <> "://" <> page_url_data.host,
url: page_url, url: page_url,
image: image_url |> MediaProxy.url(), image: image_url |> MediaProxy.url(),
title: rich_media[:title], title: rich_media[:title] || "",
description: rich_media[:description], description: rich_media[:description] || "",
pleroma: %{ pleroma: %{
opengraph: rich_media opengraph: rich_media
} }

View file

@ -12,25 +12,27 @@ def url(""), do: nil
def url("/" <> _ = url), do: url def url("/" <> _ = url), do: url
def url(url) do def url(url) do
config = Application.get_env(:pleroma, :media_proxy, []) if !enabled?() or local?(url) or whitelisted?(url) do
domain = URI.parse(url).host url
else
cond do encode_url(url)
!Keyword.get(config, :enabled, false) or String.starts_with?(url, Pleroma.Web.base_url()) ->
url
Enum.any?(Pleroma.Config.get([:media_proxy, :whitelist]), fn pattern ->
String.equivalent?(domain, pattern)
end) ->
url
true ->
encode_url(url)
end end
end end
defp enabled?, do: Pleroma.Config.get([:media_proxy, :enabled], false)
defp local?(url), do: String.starts_with?(url, Pleroma.Web.base_url())
defp whitelisted?(url) do
%{host: domain} = URI.parse(url)
Enum.any?(Pleroma.Config.get([:media_proxy, :whitelist]), fn pattern ->
String.equivalent?(domain, pattern)
end)
end
def encode_url(url) do def encode_url(url) do
secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base] secret = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
# Must preserve `%2F` for compatibility with S3 # Must preserve `%2F` for compatibility with S3
# https://git.pleroma.social/pleroma/pleroma/issues/580 # https://git.pleroma.social/pleroma/pleroma/issues/580
@ -52,7 +54,7 @@ def encode_url(url) do
end end
def decode_url(sig, url) do def decode_url(sig, url) do
secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base] secret = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
sig = Base.url_decode64!(sig, @base64_opts) sig = Base.url_decode64!(sig, @base64_opts)
local_sig = :crypto.hmac(:sha, secret, url) local_sig = :crypto.hmac(:sha, secret, url)

View file

@ -0,0 +1,41 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MongooseIM.MongooseIMController do
use Pleroma.Web, :controller
alias Comeonin.Pbkdf2
alias Pleroma.Repo
alias Pleroma.User
def user_exists(conn, %{"user" => username}) do
with %User{} <- Repo.get_by(User, nickname: username, local: true) do
conn
|> json(true)
else
_ ->
conn
|> put_status(:not_found)
|> json(false)
end
end
def check_password(conn, %{"user" => username, "pass" => password}) do
with %User{password_hash: password_hash} <-
Repo.get_by(User, nickname: username, local: true),
true <- Pbkdf2.checkpw(password, password_hash) do
conn
|> json(true)
else
false ->
conn
|> put_status(403)
|> json(false)
_ ->
conn
|> put_status(:not_found)
|> json(false)
end
end
end

View file

@ -12,8 +12,6 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
alias Pleroma.Web.ActivityPub.MRF alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.Federator.Publisher
plug(Pleroma.Web.FederatingPlug)
def schemas(conn, _params) do def schemas(conn, _params) do
response = %{ response = %{
links: [ links: [
@ -34,20 +32,15 @@ def schemas(conn, _params) do
# returns a nodeinfo 2.0 map, since 2.1 just adds a repository field # returns a nodeinfo 2.0 map, since 2.1 just adds a repository field
# under software. # under software.
def raw_nodeinfo do def raw_nodeinfo do
instance = Application.get_env(:pleroma, :instance)
media_proxy = Application.get_env(:pleroma, :media_proxy)
suggestions = Application.get_env(:pleroma, :suggestions)
chat = Application.get_env(:pleroma, :chat)
gopher = Application.get_env(:pleroma, :gopher)
stats = Stats.get_stats() stats = Stats.get_stats()
mrf_simple = mrf_simple =
Application.get_env(:pleroma, :mrf_simple) Config.get(:mrf_simple)
|> Enum.into(%{}) |> Enum.into(%{})
# This horror is needed to convert regex sigils to strings # This horror is needed to convert regex sigils to strings
mrf_keyword = mrf_keyword =
Application.get_env(:pleroma, :mrf_keyword, []) Config.get(:mrf_keyword, [])
|> Enum.map(fn {key, value} -> |> Enum.map(fn {key, value} ->
{key, {key,
Enum.map(value, fn Enum.map(value, fn
@ -76,14 +69,7 @@ def raw_nodeinfo do
MRF.get_policies() MRF.get_policies()
|> Enum.map(fn policy -> to_string(policy) |> String.split(".") |> List.last() end) |> Enum.map(fn policy -> to_string(policy) |> String.split(".") |> List.last() end)
quarantined = Keyword.get(instance, :quarantined_instances) quarantined = Config.get([:instance, :quarantined_instances], [])
quarantined =
if is_list(quarantined) do
quarantined
else
[]
end
staff_accounts = staff_accounts =
User.all_superusers() User.all_superusers()
@ -94,7 +80,7 @@ def raw_nodeinfo do
|> Enum.into(%{}, fn {k, v} -> {k, length(v)} end) |> Enum.into(%{}, fn {k, v} -> {k, length(v)} end)
federation_response = federation_response =
if Keyword.get(instance, :mrf_transparency) do if Config.get([:instance, :mrf_transparency]) do
%{ %{
mrf_policies: mrf_policies, mrf_policies: mrf_policies,
mrf_simple: mrf_simple, mrf_simple: mrf_simple,
@ -111,22 +97,22 @@ def raw_nodeinfo do
"pleroma_api", "pleroma_api",
"mastodon_api", "mastodon_api",
"mastodon_api_streaming", "mastodon_api_streaming",
if Keyword.get(media_proxy, :enabled) do if Config.get([:media_proxy, :enabled]) do
"media_proxy" "media_proxy"
end, end,
if Keyword.get(gopher, :enabled) do if Config.get([:gopher, :enabled]) do
"gopher" "gopher"
end, end,
if Keyword.get(chat, :enabled) do if Config.get([:chat, :enabled]) do
"chat" "chat"
end, end,
if Keyword.get(suggestions, :enabled) do if Config.get([:suggestions, :enabled]) do
"suggestions" "suggestions"
end, end,
if Keyword.get(instance, :allow_relay) do if Config.get([:instance, :allow_relay]) do
"relay" "relay"
end, end,
if Keyword.get(instance, :safe_dm_mentions) do if Config.get([:instance, :safe_dm_mentions]) do
"safe_dm_mentions" "safe_dm_mentions"
end end
] ]
@ -143,7 +129,7 @@ def raw_nodeinfo do
inbound: [], inbound: [],
outbound: [] outbound: []
}, },
openRegistrations: Keyword.get(instance, :registrations_open), openRegistrations: Config.get([:instance, :registrations_open]),
usage: %{ usage: %{
users: %{ users: %{
total: stats.user_count || 0 total: stats.user_count || 0
@ -151,29 +137,29 @@ def raw_nodeinfo do
localPosts: stats.status_count || 0 localPosts: stats.status_count || 0
}, },
metadata: %{ metadata: %{
nodeName: Keyword.get(instance, :name), nodeName: Config.get([:instance, :name]),
nodeDescription: Keyword.get(instance, :description), nodeDescription: Config.get([:instance, :description]),
private: !Keyword.get(instance, :public, true), private: !Config.get([:instance, :public], true),
suggestions: %{ suggestions: %{
enabled: Keyword.get(suggestions, :enabled, false), enabled: Config.get([:suggestions, :enabled], false),
thirdPartyEngine: Keyword.get(suggestions, :third_party_engine, ""), thirdPartyEngine: Config.get([:suggestions, :third_party_engine], ""),
timeout: Keyword.get(suggestions, :timeout, 5000), timeout: Config.get([:suggestions, :timeout], 5000),
limit: Keyword.get(suggestions, :limit, 23), limit: Config.get([:suggestions, :limit], 23),
web: Keyword.get(suggestions, :web, "") web: Config.get([:suggestions, :web], "")
}, },
staffAccounts: staff_accounts, staffAccounts: staff_accounts,
federation: federation_response, federation: federation_response,
postFormats: Keyword.get(instance, :allowed_post_formats), postFormats: Config.get([:instance, :allowed_post_formats]),
uploadLimits: %{ uploadLimits: %{
general: Keyword.get(instance, :upload_limit), general: Config.get([:instance, :upload_limit]),
avatar: Keyword.get(instance, :avatar_upload_limit), avatar: Config.get([:instance, :avatar_upload_limit]),
banner: Keyword.get(instance, :banner_upload_limit), banner: Config.get([:instance, :banner_upload_limit]),
background: Keyword.get(instance, :background_upload_limit) background: Config.get([:instance, :background_upload_limit])
}, },
accountActivationRequired: Keyword.get(instance, :account_activation_required, false), accountActivationRequired: Config.get([:instance, :account_activation_required], false),
invitesEnabled: Keyword.get(instance, :invites_enabled, false), invitesEnabled: Config.get([:instance, :invites_enabled], false),
features: features, features: features,
restrictedNicknames: Pleroma.Config.get([Pleroma.User, :restricted_nicknames]) restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames])
} }
} }
end end

View file

@ -5,7 +5,6 @@
defmodule Pleroma.Web.OAuth.Token do defmodule Pleroma.Web.OAuth.Token do
use Ecto.Schema use Ecto.Schema
import Ecto.Query
import Ecto.Changeset import Ecto.Changeset
alias Pleroma.Repo alias Pleroma.Repo
@ -13,6 +12,7 @@ defmodule Pleroma.Web.OAuth.Token do
alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.App
alias Pleroma.Web.OAuth.Authorization alias Pleroma.Web.OAuth.Authorization
alias Pleroma.Web.OAuth.Token alias Pleroma.Web.OAuth.Token
alias Pleroma.Web.OAuth.Token.Query
@expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600) @expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600)
@type t :: %__MODULE__{} @type t :: %__MODULE__{}
@ -31,17 +31,17 @@ defmodule Pleroma.Web.OAuth.Token do
@doc "Gets token for app by access token" @doc "Gets token for app by access token"
@spec get_by_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found} @spec get_by_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found}
def get_by_token(%App{id: app_id} = _app, token) do def get_by_token(%App{id: app_id} = _app, token) do
from(t in __MODULE__, where: t.app_id == ^app_id and t.token == ^token) Query.get_by_app(app_id)
|> Query.get_by_token(token)
|> Repo.find_resource() |> Repo.find_resource()
end end
@doc "Gets token for app by refresh token" @doc "Gets token for app by refresh token"
@spec get_by_refresh_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found} @spec get_by_refresh_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found}
def get_by_refresh_token(%App{id: app_id} = _app, token) do def get_by_refresh_token(%App{id: app_id} = _app, token) do
from(t in __MODULE__, Query.get_by_app(app_id)
where: t.app_id == ^app_id and t.refresh_token == ^token, |> Query.get_by_refresh_token(token)
preload: [:user] |> Query.preload([:user])
)
|> Repo.find_resource() |> Repo.find_resource()
end end
@ -97,29 +97,25 @@ def create_token(%App{} = app, %User{} = user, attrs \\ %{}) do
end end
def delete_user_tokens(%User{id: user_id}) do def delete_user_tokens(%User{id: user_id}) do
from( Query.get_by_user(user_id)
t in Token,
where: t.user_id == ^user_id
)
|> Repo.delete_all() |> Repo.delete_all()
end end
def delete_user_token(%User{id: user_id}, token_id) do def delete_user_token(%User{id: user_id}, token_id) do
from( Query.get_by_user(user_id)
t in Token, |> Query.get_by_id(token_id)
where: t.user_id == ^user_id, |> Repo.delete_all()
where: t.id == ^token_id end
)
def delete_expired_tokens do
Query.get_expired_tokens()
|> Repo.delete_all() |> Repo.delete_all()
end end
def get_user_tokens(%User{id: user_id}) do def get_user_tokens(%User{id: user_id}) do
from( Query.get_by_user(user_id)
t in Token, |> Query.preload([:app])
where: t.user_id == ^user_id
)
|> Repo.all() |> Repo.all()
|> Repo.preload(:app)
end end
def is_expired?(%__MODULE__{valid_until: valid_until}) do def is_expired?(%__MODULE__{valid_until: valid_until}) do

View file

@ -0,0 +1,41 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.Token.CleanWorker do
@moduledoc """
The module represents functions to clean an expired oauth tokens.
"""
# 10 seconds
@start_interval 10_000
@interval Pleroma.Config.get(
# 24 hours
[:oauth2, :clean_expired_tokens_interval],
86_400_000
)
@queue :background
alias Pleroma.Web.OAuth.Token
def start_link, do: GenServer.start_link(__MODULE__, nil)
def init(_) do
if Pleroma.Config.get([:oauth2, :clean_expired_tokens], false) do
Process.send_after(self(), :perform, @start_interval)
{:ok, nil}
else
:ignore
end
end
@doc false
def handle_info(:perform, state) do
Process.send_after(self(), :perform, @interval)
PleromaJobQueue.enqueue(@queue, __MODULE__, [:clean])
{:noreply, state}
end
# Job Worker Callbacks
def perform(:clean), do: Token.delete_expired_tokens()
end

View file

@ -0,0 +1,55 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.Token.Query do
@moduledoc """
Contains queries for OAuth Token.
"""
import Ecto.Query, only: [from: 2]
@type query :: Ecto.Queryable.t() | Token.t()
alias Pleroma.Web.OAuth.Token
@spec get_by_refresh_token(query, String.t()) :: query
def get_by_refresh_token(query \\ Token, refresh_token) do
from(q in query, where: q.refresh_token == ^refresh_token)
end
@spec get_by_token(query, String.t()) :: query
def get_by_token(query \\ Token, token) do
from(q in query, where: q.token == ^token)
end
@spec get_by_app(query, String.t()) :: query
def get_by_app(query \\ Token, app_id) do
from(q in query, where: q.app_id == ^app_id)
end
@spec get_by_id(query, String.t()) :: query
def get_by_id(query \\ Token, id) do
from(q in query, where: q.id == ^id)
end
@spec get_expired_tokens(query, DateTime.t() | nil) :: query
def get_expired_tokens(query \\ Token, date \\ nil) do
expired_date = date || Timex.now()
from(q in query, where: fragment("?", q.valid_until) < ^expired_date)
end
@spec get_by_user(query, String.t()) :: query
def get_by_user(query \\ Token, user_id) do
from(q in query, where: q.user_id == ^user_id)
end
@spec preload(query, any) :: query
def preload(query \\ Token, assoc_preload \\ [])
def preload(query, assoc_preload) when is_list(assoc_preload) do
from(q in query, preload: ^assoc_preload)
end
def preload(query, _assoc_preload), do: query
end

View file

@ -3,13 +3,12 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OStatus do defmodule Pleroma.Web.OStatus do
@httpoison Application.get_env(:pleroma, :httpoison)
import Ecto.Query import Ecto.Query
import Pleroma.Web.XML import Pleroma.Web.XML
require Logger require Logger
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.HTTP
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
@ -363,7 +362,7 @@ def get_atom_url(body) do
def fetch_activity_from_atom_url(url) do def fetch_activity_from_atom_url(url) do
with true <- String.starts_with?(url, "http"), with true <- String.starts_with?(url, "http"),
{:ok, %{body: body, status: code}} when code in 200..299 <- {:ok, %{body: body, status: code}} when code in 200..299 <-
@httpoison.get( HTTP.get(
url, url,
[{:Accept, "application/atom+xml"}] [{:Accept, "application/atom+xml"}]
) do ) do
@ -380,7 +379,7 @@ def fetch_activity_from_html_url(url) do
Logger.debug("Trying to fetch #{url}") Logger.debug("Trying to fetch #{url}")
with true <- String.starts_with?(url, "http"), with true <- String.starts_with?(url, "http"),
{:ok, %{body: body}} <- @httpoison.get(url, []), {:ok, %{body: body}} <- HTTP.get(url, []),
{:ok, atom_url} <- get_atom_url(body) do {:ok, atom_url} <- get_atom_url(body) do
fetch_activity_from_atom_url(atom_url) fetch_activity_from_atom_url(atom_url)
else else

View file

@ -37,7 +37,10 @@ defp parse_url(url) do
try do try do
{:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: @hackney_options) {:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: @hackney_options)
html |> maybe_parse() |> clean_parsed_data() |> check_parsed_data() html
|> maybe_parse()
|> clean_parsed_data()
|> check_parsed_data()
rescue rescue
e -> e ->
{:error, "Parsing error: #{inspect(e)}"} {:error, "Parsing error: #{inspect(e)}"}

View file

@ -354,6 +354,9 @@ defmodule Pleroma.Web.Router do
post("/pleroma/flavour/:flavour", MastodonAPIController, :set_flavour) post("/pleroma/flavour/:flavour", MastodonAPIController, :set_flavour)
get("/pleroma/mascot", MastodonAPIController, :get_mascot)
put("/pleroma/mascot", MastodonAPIController, :set_mascot)
post("/reports", MastodonAPIController, :reports) post("/reports", MastodonAPIController, :reports)
end end
@ -708,9 +711,15 @@ defmodule Pleroma.Web.Router do
end end
end end
scope "/", Pleroma.Web.MongooseIM do
get("/user_exists", MongooseIMController, :user_exists)
get("/check_password", MongooseIMController, :check_password)
end
scope "/", Fallback do scope "/", Fallback do
get("/registration/:token", RedirectController, :registration_page) get("/registration/:token", RedirectController, :registration_page)
get("/:maybe_nickname_or_id", RedirectController, :redirector_with_meta) get("/:maybe_nickname_or_id", RedirectController, :redirector_with_meta)
get("/api*path", RedirectController, :api_not_implemented)
get("/*path", RedirectController, :redirector) get("/*path", RedirectController, :redirector)
options("/*path", RedirectController, :empty) options("/*path", RedirectController, :empty)
@ -722,6 +731,12 @@ defmodule Fallback.RedirectController do
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.Metadata alias Pleroma.Web.Metadata
def api_not_implemented(conn, _params) do
conn
|> put_status(404)
|> json(%{error: "Not implemented"})
end
def redirector(conn, _params, code \\ 200) do def redirector(conn, _params, code \\ 200) do
conn conn
|> put_resp_content_type("text/html") |> put_resp_content_type("text/html")

View file

@ -5,12 +5,12 @@
defmodule Pleroma.Web.Salmon do defmodule Pleroma.Web.Salmon do
@behaviour Pleroma.Web.Federator.Publisher @behaviour Pleroma.Web.Federator.Publisher
@httpoison Application.get_env(:pleroma, :httpoison)
use Bitwise use Bitwise
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.HTTP
alias Pleroma.Instances alias Pleroma.Instances
alias Pleroma.Keys
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.Federator.Publisher
@ -89,45 +89,6 @@ def encode_key({:RSAPublicKey, modulus, exponent}) do
"RSA.#{modulus_enc}.#{exponent_enc}" "RSA.#{modulus_enc}.#{exponent_enc}"
end end
# Native generation of RSA keys is only available since OTP 20+ and in default build conditions
# We try at compile time to generate natively an RSA key otherwise we fallback on the old way.
try do
_ = :public_key.generate_key({:rsa, 2048, 65_537})
def generate_rsa_pem do
key = :public_key.generate_key({:rsa, 2048, 65_537})
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
pem = :public_key.pem_encode([entry]) |> String.trim_trailing()
{:ok, pem}
end
rescue
_ ->
def generate_rsa_pem do
port = Port.open({:spawn, "openssl genrsa"}, [:binary])
{:ok, pem} =
receive do
{^port, {:data, pem}} -> {:ok, pem}
end
Port.close(port)
if Regex.match?(~r/RSA PRIVATE KEY/, pem) do
{:ok, pem}
else
:error
end
end
end
def keys_from_pem(pem) do
[private_key_code] = :public_key.pem_decode(pem)
private_key = :public_key.pem_entry_decode(private_key_code)
{:RSAPrivateKey, _, modulus, exponent, _, _, _, _, _, _, _} = private_key
public_key = {:RSAPublicKey, modulus, exponent}
{:ok, private_key, public_key}
end
def encode(private_key, doc) do def encode(private_key, doc) do
type = "application/atom+xml" type = "application/atom+xml"
encoding = "base64url" encoding = "base64url"
@ -176,7 +137,7 @@ def publish_one(%{recipient: %{info: %{salmon: salmon}}} = params),
def publish_one(%{recipient: url, feed: feed} = params) when is_binary(url) do def publish_one(%{recipient: url, feed: feed} = params) when is_binary(url) do
with {:ok, %{status: code}} when code in 200..299 <- with {:ok, %{status: code}} when code in 200..299 <-
@httpoison.post( HTTP.post(
url, url,
feed, feed,
[{"Content-Type", "application/magic-envelope+xml"}] [{"Content-Type", "application/magic-envelope+xml"}]
@ -227,7 +188,7 @@ def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity
|> :xmerl.export_simple(:xmerl_xml) |> :xmerl.export_simple(:xmerl_xml)
|> to_string |> to_string
{:ok, private, _} = keys_from_pem(keys) {:ok, private, _} = Keys.keys_from_pem(keys)
{:ok, feed} = encode(private, feed) {:ok, feed} = encode(private, feed)
remote_users = remote_users(activity) remote_users = remote_users(activity)
@ -253,7 +214,7 @@ def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity
def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end) def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end)
def gather_webfinger_links(%User{} = user) do def gather_webfinger_links(%User{} = user) do
{:ok, _private, public} = keys_from_pem(user.info.keys) {:ok, _private, public} = Keys.keys_from_pem(user.info.keys)
magic_key = encode_key(public) magic_key = encode_key(public)
[ [

View file

@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimal-ui" /> <meta name="viewport" content="width=device-width,initial-scale=1,minimal-ui" />
<title> <title>
<%= Application.get_env(:pleroma, :instance)[:name] %> <%= Pleroma.Config.get([:instance, :name]) %>
</title> </title>
<style> <style>
body { body {
@ -194,7 +194,7 @@
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h1><%= Application.get_env(:pleroma, :instance)[:name] %></h1> <h1><%= Pleroma.Config.get([:instance, :name]) %></h1>
<%= render @view_module, @view_template, assigns %> <%= render @view_module, @view_template, assigns %>
</div> </div>
</body> </body>

View file

@ -4,7 +4,7 @@
<meta charset='utf-8'> <meta charset='utf-8'>
<meta content='width=device-width, initial-scale=1' name='viewport'> <meta content='width=device-width, initial-scale=1' name='viewport'>
<title> <title>
<%= Application.get_env(:pleroma, :instance)[:name] %> <%= Pleroma.Config.get([:instance, :name]) %>
</title> </title>
<link rel="icon" type="image/png" href="/favicon.png"/> <link rel="icon" type="image/png" href="/favicon.png"/>
<script crossorigin='anonymous' src="/packs/locales.js"></script> <script crossorigin='anonymous' src="/packs/locales.js"></script>

View file

@ -728,7 +728,7 @@ defp forbidden_json_reply(conn, error_message) do
def only_if_public_instance(%{assigns: %{user: %User{}}} = conn, _), do: conn def only_if_public_instance(%{assigns: %{user: %User{}}} = conn, _), do: conn
def only_if_public_instance(conn, _) do def only_if_public_instance(conn, _) do
if Keyword.get(Application.get_env(:pleroma, :instance), :public) do if Pleroma.Config.get([:instance, :public]) do
conn conn
else else
conn conn

View file

@ -284,6 +284,12 @@ def render(
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
) )
thread_muted? =
case activity.thread_muted? do
thread_muted? when is_boolean(thread_muted?) -> thread_muted?
nil -> CommonAPI.thread_muted?(user, activity)
end
%{ %{
"id" => activity.id, "id" => activity.id,
"uri" => object.data["id"], "uri" => object.data["id"],
@ -314,7 +320,7 @@ def render(
"summary" => summary, "summary" => summary,
"summary_html" => summary |> Formatter.emojify(object.data["emoji"]), "summary_html" => summary |> Formatter.emojify(object.data["emoji"]),
"card" => card, "card" => card,
"muted" => CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user) "muted" => thread_muted? || User.mutes?(opts[:for], user)
} }
end end

View file

@ -3,12 +3,10 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.WebFinger do defmodule Pleroma.Web.WebFinger do
@httpoison Application.get_env(:pleroma, :httpoison) alias Pleroma.HTTP
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web alias Pleroma.Web
alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.Federator.Publisher
alias Pleroma.Web.Salmon
alias Pleroma.Web.XML alias Pleroma.Web.XML
alias Pleroma.XmlBuilder alias Pleroma.XmlBuilder
require Jason require Jason
@ -61,7 +59,7 @@ defp gather_links(%User{} = user) do
end end
def represent_user(user, "JSON") do def represent_user(user, "JSON") do
{:ok, user} = ensure_keys_present(user) {:ok, user} = User.ensure_keys_present(user)
%{ %{
"subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}", "subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}",
@ -71,7 +69,7 @@ def represent_user(user, "JSON") do
end end
def represent_user(user, "XML") do def represent_user(user, "XML") do
{:ok, user} = ensure_keys_present(user) {:ok, user} = User.ensure_keys_present(user)
links = links =
gather_links(user) gather_links(user)
@ -88,27 +86,6 @@ def represent_user(user, "XML") do
|> XmlBuilder.to_doc() |> XmlBuilder.to_doc()
end end
# This seems a better fit in Salmon
def ensure_keys_present(user) do
info = user.info
if info.keys do
{:ok, user}
else
{:ok, pem} = Salmon.generate_rsa_pem()
info_cng =
info
|> User.Info.set_keys(pem)
cng =
Ecto.Changeset.change(user)
|> Ecto.Changeset.put_embed(:info, info_cng)
User.update_and_set_cache(cng)
end
end
defp get_magic_key(magic_key) do defp get_magic_key(magic_key) do
"data:application/magic-public-key," <> magic_key = magic_key "data:application/magic-public-key," <> magic_key = magic_key
{:ok, magic_key} {:ok, magic_key}
@ -198,11 +175,11 @@ def get_template_from_xml(body) do
def find_lrdd_template(domain) do def find_lrdd_template(domain) do
with {:ok, %{status: status, body: body}} when status in 200..299 <- with {:ok, %{status: status, body: body}} when status in 200..299 <-
@httpoison.get("http://#{domain}/.well-known/host-meta", []) do HTTP.get("http://#{domain}/.well-known/host-meta", []) do
get_template_from_xml(body) get_template_from_xml(body)
else else
_ -> _ ->
with {:ok, %{body: body}} <- @httpoison.get("https://#{domain}/.well-known/host-meta", []) do with {:ok, %{body: body}} <- HTTP.get("https://#{domain}/.well-known/host-meta", []) do
get_template_from_xml(body) get_template_from_xml(body)
else else
e -> {:error, "Can't find LRDD template: #{inspect(e)}"} e -> {:error, "Can't find LRDD template: #{inspect(e)}"}
@ -231,7 +208,7 @@ def finger(account) do
end end
with response <- with response <-
@httpoison.get( HTTP.get(
address, address,
Accept: "application/xrd+xml,application/jrd+json" Accept: "application/xrd+xml,application/jrd+json"
), ),

View file

@ -5,6 +5,7 @@
defmodule Pleroma.Web.Websub do defmodule Pleroma.Web.Websub do
alias Ecto.Changeset alias Ecto.Changeset
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.HTTP
alias Pleroma.Instances alias Pleroma.Instances
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
@ -24,9 +25,7 @@ defmodule Pleroma.Web.Websub do
@behaviour Pleroma.Web.Federator.Publisher @behaviour Pleroma.Web.Federator.Publisher
@httpoison Application.get_env(:pleroma, :httpoison) def verify(subscription, getter \\ &HTTP.get/3) do
def verify(subscription, getter \\ &@httpoison.get/3) do
challenge = Base.encode16(:crypto.strong_rand_bytes(8)) challenge = Base.encode16(:crypto.strong_rand_bytes(8))
lease_seconds = NaiveDateTime.diff(subscription.valid_until, subscription.updated_at) lease_seconds = NaiveDateTime.diff(subscription.valid_until, subscription.updated_at)
lease_seconds = lease_seconds |> to_string lease_seconds = lease_seconds |> to_string
@ -207,7 +206,7 @@ def subscribe(subscriber, subscribed, requester \\ &request_subscription/1) do
requester.(subscription) requester.(subscription)
end end
def gather_feed_data(topic, getter \\ &@httpoison.get/1) do def gather_feed_data(topic, getter \\ &HTTP.get/1) do
with {:ok, response} <- getter.(topic), with {:ok, response} <- getter.(topic),
status when status in 200..299 <- response.status, status when status in 200..299 <- response.status,
body <- response.body, body <- response.body,
@ -236,7 +235,7 @@ def gather_feed_data(topic, getter \\ &@httpoison.get/1) do
end end
end end
def request_subscription(websub, poster \\ &@httpoison.post/3, timeout \\ 10_000) do def request_subscription(websub, poster \\ &HTTP.post/3, timeout \\ 10_000) do
data = [ data = [
"hub.mode": "subscribe", "hub.mode": "subscribe",
"hub.topic": websub.topic, "hub.topic": websub.topic,
@ -294,7 +293,7 @@ def publish_one(%{xml: xml, topic: topic, callback: callback, secret: secret} =
Logger.info(fn -> "Pushing #{topic} to #{callback}" end) Logger.info(fn -> "Pushing #{topic} to #{callback}" end)
with {:ok, %{status: code}} when code in 200..299 <- with {:ok, %{status: code}} when code in 200..299 <-
@httpoison.post( HTTP.post(
callback, callback,
xml, xml,
[ [

View file

@ -42,7 +42,7 @@ def project do
def application do def application do
[ [
mod: {Pleroma.Application, []}, mod: {Pleroma.Application, []},
extra_applications: [:logger, :runtime_tools, :comeonin, :esshd, :quack], extra_applications: [:logger, :runtime_tools, :comeonin, :quack],
included_applications: [:ex_syslogger] included_applications: [:ex_syslogger]
] ]
end end
@ -66,10 +66,7 @@ defp deps do
{:plug_cowboy, "~> 2.0"}, {:plug_cowboy, "~> 2.0"},
{:phoenix_pubsub, "~> 1.1"}, {:phoenix_pubsub, "~> 1.1"},
{:phoenix_ecto, "~> 4.0"}, {:phoenix_ecto, "~> 4.0"},
{:ecto_sql, {:ecto_sql, "~> 3.1"},
git: "https://github.com/elixir-ecto/ecto_sql",
ref: "14cb065a74c488d737d973f7a91bc036c6245f78",
override: true},
{:postgrex, ">= 0.13.5"}, {:postgrex, ">= 0.13.5"},
{:gettext, "~> 0.15"}, {:gettext, "~> 0.15"},
{:comeonin, "~> 4.1.1"}, {:comeonin, "~> 4.1.1"},
@ -120,7 +117,7 @@ defp deps do
{:recon, github: "ferd/recon", tag: "2.4.0"}, {:recon, github: "ferd/recon", tag: "2.4.0"},
{:quack, "~> 0.1.1"}, {:quack, "~> 0.1.1"},
{:benchee, "~> 1.0"}, {:benchee, "~> 1.0"},
{:esshd, "~> 0.1.0"}, {:esshd, "~> 0.1.0", runtime: Application.get_env(:esshd, :enabled, false)},
{:ex_rated, "~> 1.2"}, {:ex_rated, "~> 1.2"},
{:plug_static_index_html, "~> 1.0.0"}, {:plug_static_index_html, "~> 1.0.0"},
{:excoveralls, "~> 0.11.1", only: :test} {:excoveralls, "~> 0.11.1", only: :test}

View file

@ -21,7 +21,7 @@
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm"},
"earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"}, "earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"},
"ecto": {:hex, :ecto, "3.1.4", "69d852da7a9f04ede725855a35ede48d158ca11a404fe94f8b2fb3b2162cd3c9", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, "ecto": {:hex, :ecto, "3.1.4", "69d852da7a9f04ede725855a35ede48d158ca11a404fe94f8b2fb3b2162cd3c9", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"ecto_sql": {:git, "https://github.com/elixir-ecto/ecto_sql", "14cb065a74c488d737d973f7a91bc036c6245f78", [ref: "14cb065a74c488d737d973f7a91bc036c6245f78"]}, "ecto_sql": {:hex, :ecto_sql, "3.1.3", "2c536139190492d9de33c5fefac7323c5eaaa82e1b9bf93482a14649042f7cd9", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.1.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:myxql, "~> 0.2.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
"esshd": {:hex, :esshd, "0.1.0", "6f93a2062adb43637edad0ea7357db2702a4b80dd9683482fe00f5134e97f4c1", [:mix], [], "hexpm"}, "esshd": {:hex, :esshd, "0.1.0", "6f93a2062adb43637edad0ea7357db2702a4b80dd9683482fe00f5134e97f4c1", [:mix], [], "hexpm"},
"eternal": {:hex, :eternal, "1.2.0", "e2a6b6ce3b8c248f7dc31451aefca57e3bdf0e48d73ae5043229380a67614c41", [:mix], [], "hexpm"}, "eternal": {:hex, :eternal, "1.2.0", "e2a6b6ce3b8c248f7dc31451aefca57e3bdf0e48d73ae5043229380a67614c41", [:mix], [], "hexpm"},
"ex2ms": {:hex, :ex2ms, "1.5.0", "19e27f9212be9a96093fed8cdfbef0a2b56c21237196d26760f11dfcfae58e97", [:mix], [], "hexpm"}, "ex2ms": {:hex, :ex2ms, "1.5.0", "19e27f9212be9a96093fed8cdfbef0a2b56c21237196d26760f11dfcfae58e97", [:mix], [], "hexpm"},

View file

@ -6,6 +6,7 @@ defmodule Pleroma.ActivityTest do
use Pleroma.DataCase use Pleroma.DataCase
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Bookmark alias Pleroma.Bookmark
alias Pleroma.ThreadMute
import Pleroma.Factory import Pleroma.Factory
test "returns an activity by it's AP id" do test "returns an activity by it's AP id" do
@ -47,6 +48,31 @@ test "preloading a bookmark" do
assert queried_activity.bookmark == bookmark3 assert queried_activity.bookmark == bookmark3
end end
test "setting thread_muted?" do
activity = insert(:note_activity)
user = insert(:user)
annoyed_user = insert(:user)
{:ok, _} = ThreadMute.add_mute(annoyed_user.id, activity.data["context"])
activity_with_unset_thread_muted_field =
Ecto.Query.from(Activity)
|> Repo.one()
activity_for_user =
Ecto.Query.from(Activity)
|> Activity.with_set_thread_muted_field(user)
|> Repo.one()
activity_for_annoyed_user =
Ecto.Query.from(Activity)
|> Activity.with_set_thread_muted_field(annoyed_user)
|> Repo.one()
assert activity_with_unset_thread_muted_field.thread_muted? == nil
assert activity_for_user.thread_muted? == false
assert activity_for_annoyed_user.thread_muted? == true
end
describe "getting a bookmark" do describe "getting a bookmark" do
test "when association is loaded" do test "when association is loaded" do
user = insert(:user) user = insert(:user)

View file

@ -0,0 +1,8 @@
<html prefix="og: http://ogp.me/ns#">
<head>
<title>Pleroma</title>
<meta property="og:title" content="Pleroma" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://pleroma.social/" />
</head>
</html>

View file

@ -5,5 +5,6 @@
<meta property="og:type" content="video.movie" /> <meta property="og:type" content="video.movie" />
<meta property="og:url" content="http://www.imdb.com/title/tt0117500/" /> <meta property="og:url" content="http://www.imdb.com/title/tt0117500/" />
<meta property="og:image" content="http://ia.media-imdb.com/images/rock.jpg" /> <meta property="og:image" content="http://ia.media-imdb.com/images/rock.jpg" />
<meta property="og:description" content="Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.">
</head> </head>
</html> </html>

BIN
test/fixtures/sound.mp3 vendored Normal file

Binary file not shown.

View file

@ -184,17 +184,19 @@ test "does not give a replacement for single-character local nicknames who don't
test "given the 'safe_mention' option, it will only mention people in the beginning" do test "given the 'safe_mention' option, it will only mention people in the beginning" do
user = insert(:user) user = insert(:user)
_other_user = insert(:user) other_user = insert(:user)
third_user = insert(:user) third_user = insert(:user)
text = " @#{user.nickname} hey dude i hate @#{third_user.nickname}" text = " @#{user.nickname} @#{other_user.nickname} hey dudes i hate @#{third_user.nickname}"
{expected_text, mentions, [] = _tags} = Formatter.linkify(text, safe_mention: true) {expected_text, mentions, [] = _tags} = Formatter.linkify(text, safe_mention: true)
assert mentions == [{"@#{user.nickname}", user}] assert mentions == [{"@#{user.nickname}", user}, {"@#{other_user.nickname}", other_user}]
assert expected_text == assert expected_text ==
"<span class='h-card'><a data-user='#{user.id}' class='u-url mention' href='#{ "<span class='h-card'><a data-user='#{user.id}' class='u-url mention' href='#{
user.ap_id user.ap_id
}'>@<span>#{user.nickname}</span></a></span> hey dude i hate <span class='h-card'><a data-user='#{ }'>@<span>#{user.nickname}</span></a></span> <span class='h-card'><a data-user='#{
other_user.id
}' class='u-url mention' href='#{other_user.ap_id}'>@<span>#{other_user.nickname}</span></a></span> hey dudes i hate <span class='h-card'><a data-user='#{
third_user.id third_user.id
}' class='u-url mention' href='#{third_user.ap_id}'>@<span>#{third_user.nickname}</span></a></span>" }' class='u-url mention' href='#{third_user.ap_id}'>@<span>#{third_user.nickname}</span></a></span>"
end end
@ -206,6 +208,15 @@ test "given the 'safe_mention' option, it will still work without any mention" d
assert mentions == [] assert mentions == []
assert expected_text == text assert expected_text == text
end end
test "given the 'safe_mention' option, it will keep text after newlines" do
user = insert(:user)
text = " @#{user.nickname}\n hey dude\n\nhow are you doing?"
{expected_text, _, _} = Formatter.linkify(text, safe_mention: true)
assert expected_text =~ "how are you doing?"
end
end end
describe ".parse_tags" do describe ".parse_tags" do

20
test/keys_test.exs Normal file
View file

@ -0,0 +1,20 @@
defmodule Pleroma.KeysTest do
use Pleroma.DataCase
alias Pleroma.Keys
test "generates an RSA private key pem" do
{:ok, key} = Keys.generate_rsa_pem()
assert is_binary(key)
assert Regex.match?(~r/RSA/, key)
end
test "returns a public and private key from a pem" do
pem = File.read!("test/fixtures/private_key.pem")
{:ok, private, public} = Keys.keys_from_pem(pem)
assert elem(private, 0) == :RSAPrivateKey
assert elem(public, 0) == :RSAPublicKey
end
end

View file

@ -87,4 +87,23 @@ test "all objects with fake directions are rejected by the object fetcher" do
) )
end end
end end
describe "pruning" do
test "it can refetch pruned objects" do
object_id = "http://mastodon.example.org/@admin/99541947525187367"
{:ok, object} = Fetcher.fetch_object_from_id(object_id)
assert object
{:ok, _object} = Object.prune(object)
refute Object.get_by_ap_id(object_id)
{:ok, %Object{} = object_two} = Fetcher.fetch_object_from_id(object_id)
assert object.data["id"] == object_two.data["id"]
assert object.id != object_two.id
end
end
end end

View file

@ -0,0 +1,20 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.CacheControlTest do
use Pleroma.Web.ConnCase
alias Plug.Conn
test "Verify Cache-Control header on static assets", %{conn: conn} do
conn = get(conn, "/index.html")
assert Conn.get_resp_header(conn, "cache-control") == ["public, no-cache"]
end
test "Verify Cache-Control header on the API", %{conn: conn} do
conn = get(conn, "/api/v1/instance")
assert Conn.get_resp_header(conn, "cache-control") == ["max-age=0, private, must-revalidate"]
end
end

View file

@ -736,6 +736,14 @@ def get("http://example.com/ogp", _, _, _) do
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}} {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}}
end end
def get("http://example.com/ogp-missing-data", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/rich_media/ogp-missing-data.html")
}}
end
def get("http://example.com/malformed", _, _, _) do def get("http://example.com/malformed", _, _, _) do
{:ok, {:ok,
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/malformed-data.html")}} %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/malformed-data.html")}}

View file

@ -1,11 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OStatusMock do
import Pleroma.Factory
def handle_incoming(_doc) do
insert(:note_activity)
end
end

View file

@ -1,9 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.WebsubMock do
def verify(sub) do
{:ok, sub}
end
end

View file

@ -902,7 +902,7 @@ test "hide a user's statuses from timelines and notifications" do
assert [activity] == ActivityPub.fetch_public_activities(%{}) |> Repo.preload(:bookmark) assert [activity] == ActivityPub.fetch_public_activities(%{}) |> Repo.preload(:bookmark)
assert [activity] == assert [%{activity | thread_muted?: CommonAPI.thread_muted?(user2, activity)}] ==
ActivityPub.fetch_activities([user2.ap_id | user2.following], %{"user" => user2}) ActivityPub.fetch_activities([user2.ap_id | user2.following], %{"user" => user2})
{:ok, _user} = User.deactivate(user) {:ok, _user} = User.deactivate(user)
@ -1251,4 +1251,19 @@ test "if user is unconfirmed" do
refute user.info.confirmation_token refute user.info.confirmation_token
end end
end end
describe "ensure_keys_present" do
test "it creates keys for a user and stores them in info" do
user = insert(:user)
refute is_binary(user.info.keys)
{:ok, user} = User.ensure_keys_present(user)
assert is_binary(user.info.keys)
end
test "it doesn't create keys if there already are some" do
user = insert(:user, %{info: %{keys: "xxx"}})
{:ok, user} = User.ensure_keys_present(user)
assert user.info.keys == "xxx"
end
end
end end

View file

@ -1005,7 +1005,7 @@ test "it filters broken threads" do
describe "update" do describe "update" do
test "it creates an update activity with the new user data" do test "it creates an update activity with the new user data" do
user = insert(:user) user = insert(:user)
{:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) {:ok, user} = User.ensure_keys_present(user)
user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user}) user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
{:ok, update} = {:ok, update} =
@ -1186,4 +1186,33 @@ test "it can create a Flag activity" do
def data_uri do def data_uri do
File.read!("test/fixtures/avatar_data_uri") File.read!("test/fixtures/avatar_data_uri")
end end
describe "fetch_activities_bounded" do
test "fetches private posts for followed users" do
user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{
"status" => "thought I looked cute might delete later :3",
"visibility" => "private"
})
[result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
assert result.id == activity.id
end
test "fetches only public posts for other users" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe", "visibility" => "public"})
{:ok, _private_activity} =
CommonAPI.post(user, %{
"status" => "why is tenshi eating a corndog so cute?",
"visibility" => "private"
})
[result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
assert result.id == activity.id
end
end
end end

View file

@ -15,8 +15,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
media_removal: [], media_removal: [],
media_nsfw: [], media_nsfw: [],
federated_timeline_removal: [], federated_timeline_removal: [],
report_removal: [],
reject: [], reject: [],
accept: [] accept: [],
avatar_removal: [],
banner_removal: []
) )
on_exit(fn -> on_exit(fn ->
@ -85,6 +88,33 @@ defp build_media_message do
} }
end end
describe "when :report_removal" do
test "is empty" do
Config.put([:mrf_simple, :report_removal], [])
report_message = build_report_message()
local_message = build_local_message()
assert SimplePolicy.filter(report_message) == {:ok, report_message}
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
test "has a matching host" do
Config.put([:mrf_simple, :report_removal], ["remote.instance"])
report_message = build_report_message()
local_message = build_local_message()
assert SimplePolicy.filter(report_message) == {:reject, nil}
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
end
defp build_report_message do
%{
"actor" => "https://remote.instance/users/bob",
"type" => "Flag"
}
end
describe "when :federated_timeline_removal" do describe "when :federated_timeline_removal" do
test "is empty" do test "is empty" do
Config.put([:mrf_simple, :federated_timeline_removal], []) Config.put([:mrf_simple, :federated_timeline_removal], [])
@ -115,6 +145,24 @@ test "has a matching host" do
assert SimplePolicy.filter(local_message) == {:ok, local_message} assert SimplePolicy.filter(local_message) == {:ok, local_message}
end end
test "has a matching host but only as:Public in to" do
{_actor, ftl_message} = build_ftl_actor_and_message()
ftl_message_actor_host =
ftl_message
|> Map.fetch!("actor")
|> URI.parse()
|> Map.fetch!(:host)
ftl_message = Map.put(ftl_message, "cc", [])
Config.put([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host])
assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message)
refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"]
assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"]
end
end end
defp build_ftl_actor_and_message do defp build_ftl_actor_and_message do
@ -178,6 +226,60 @@ test "has a matching host" do
end end
end end
describe "when :avatar_removal" do
test "is empty" do
Config.put([:mrf_simple, :avatar_removal], [])
remote_user = build_remote_user()
assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
end
test "is not empty but it doesn't have a matching host" do
Config.put([:mrf_simple, :avatar_removal], ["non.matching.remote"])
remote_user = build_remote_user()
assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
end
test "has a matching host" do
Config.put([:mrf_simple, :avatar_removal], ["remote.instance"])
remote_user = build_remote_user()
{:ok, filtered} = SimplePolicy.filter(remote_user)
refute filtered["icon"]
end
end
describe "when :banner_removal" do
test "is empty" do
Config.put([:mrf_simple, :banner_removal], [])
remote_user = build_remote_user()
assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
end
test "is not empty but it doesn't have a matching host" do
Config.put([:mrf_simple, :banner_removal], ["non.matching.remote"])
remote_user = build_remote_user()
assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
end
test "has a matching host" do
Config.put([:mrf_simple, :banner_removal], ["remote.instance"])
remote_user = build_remote_user()
{:ok, filtered} = SimplePolicy.filter(remote_user)
refute filtered["image"]
end
end
defp build_local_message do defp build_local_message do
%{ %{
"actor" => "#{Pleroma.Web.base_url()}/users/alice", "actor" => "#{Pleroma.Web.base_url()}/users/alice",
@ -189,4 +291,19 @@ defp build_local_message do
defp build_remote_message do defp build_remote_message do
%{"actor" => "https://remote.instance/users/bob"} %{"actor" => "https://remote.instance/users/bob"}
end end
defp build_remote_user do
%{
"id" => "https://remote.instance/users/bob",
"icon" => %{
"url" => "http://example.com/image.jpg",
"type" => "Image"
},
"image" => %{
"url" => "http://example.com/image.jpg",
"type" => "Image"
},
"type" => "Person"
}
end
end end

View file

@ -1282,4 +1282,44 @@ test "Rewrites Answers to Notes" do
assert data["object"]["type"] == "Note" assert data["object"]["type"] == "Note"
end end
describe "fix_explicit_addressing" do
test "moves non-explicitly mentioned actors to cc" do
user = insert(:user)
explicitly_mentioned_actors = [
"https://pleroma.gold/users/user1",
"https://pleroma.gold/user2"
]
object = %{
"actor" => user.ap_id,
"to" => explicitly_mentioned_actors ++ ["https://social.beepboop.ga/users/dirb"],
"cc" => [],
"tag" =>
Enum.map(explicitly_mentioned_actors, fn href ->
%{"type" => "Mention", "href" => href}
end)
}
fixed_object = Transmogrifier.fix_explicit_addressing(object)
assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"]))
refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"]
assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"]
end
test "does not move actor's follower collection to cc" do
user = insert(:user)
object = %{
"actor" => user.ap_id,
"to" => [user.follower_address],
"cc" => []
}
fixed_object = Transmogrifier.fix_explicit_addressing(object)
assert user.follower_address in fixed_object["to"]
refute user.follower_address in fixed_object["cc"]
end
end
end end

View file

@ -2,11 +2,12 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
use Pleroma.DataCase use Pleroma.DataCase
import Pleroma.Factory import Pleroma.Factory
alias Pleroma.User
alias Pleroma.Web.ActivityPub.UserView alias Pleroma.Web.ActivityPub.UserView
test "Renders a user, including the public key" do test "Renders a user, including the public key" do
user = insert(:user) user = insert(:user)
{:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) {:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user}) result = UserView.render("user.json", %{user: user})
@ -18,7 +19,7 @@ test "Renders a user, including the public key" do
test "Does not add an avatar image if the user hasn't set one" do test "Does not add an avatar image if the user hasn't set one" do
user = insert(:user) user = insert(:user)
{:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) {:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user}) result = UserView.render("user.json", %{user: user})
refute result["icon"] refute result["icon"]
@ -32,7 +33,7 @@ test "Does not add an avatar image if the user hasn't set one" do
} }
) )
{:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) {:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user}) result = UserView.render("user.json", %{user: user})
assert result["icon"]["url"] == "https://someurl" assert result["icon"]["url"] == "https://someurl"
@ -42,7 +43,7 @@ test "Does not add an avatar image if the user hasn't set one" do
describe "endpoints" do describe "endpoints" do
test "local users have a usable endpoints structure" do test "local users have a usable endpoints structure" do
user = insert(:user) user = insert(:user)
{:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) {:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user}) result = UserView.render("user.json", %{user: user})
@ -58,7 +59,7 @@ test "local users have a usable endpoints structure" do
test "remote users have an empty endpoints structure" do test "remote users have an empty endpoints structure" do
user = insert(:user, local: false) user = insert(:user, local: false)
{:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) {:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user}) result = UserView.render("user.json", %{user: user})
@ -68,7 +69,7 @@ test "remote users have an empty endpoints structure" do
test "instance users do not expose oAuth endpoints" do test "instance users do not expose oAuth endpoints" do
user = insert(:user, nickname: nil, local: true) user = insert(:user, nickname: nil, local: true)
{:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) {:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user}) result = UserView.render("user.json", %{user: user})

View file

@ -397,14 +397,14 @@ test "it returns 500 if `registrations_open` is enabled", %{conn: conn, user: us
end end
end end
test "/api/pleroma/admin/invite_token" do test "/api/pleroma/admin/users/invite_token" do
admin = insert(:user, info: %{is_admin: true}) admin = insert(:user, info: %{is_admin: true})
conn = conn =
build_conn() build_conn()
|> assign(:user, admin) |> assign(:user, admin)
|> put_req_header("accept", "application/json") |> put_req_header("accept", "application/json")
|> get("/api/pleroma/admin/invite_token") |> get("/api/pleroma/admin/users/invite_token")
assert conn.status == 200 assert conn.status == 200
end end
@ -437,27 +437,31 @@ test "renders users array for the first page", %{conn: conn, admin: admin} do
user = insert(:user, local: false, tags: ["foo", "bar"]) user = insert(:user, local: false, tags: ["foo", "bar"])
conn = get(conn, "/api/pleroma/admin/users?page=1") conn = get(conn, "/api/pleroma/admin/users?page=1")
users =
[
%{
"deactivated" => admin.info.deactivated,
"id" => admin.id,
"nickname" => admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
"local" => true,
"tags" => []
},
%{
"deactivated" => user.info.deactivated,
"id" => user.id,
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => false,
"tags" => ["foo", "bar"]
}
]
|> Enum.sort_by(& &1["nickname"])
assert json_response(conn, 200) == %{ assert json_response(conn, 200) == %{
"count" => 2, "count" => 2,
"page_size" => 50, "page_size" => 50,
"users" => [ "users" => users
%{
"deactivated" => admin.info.deactivated,
"id" => admin.id,
"nickname" => admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
"local" => true,
"tags" => []
},
%{
"deactivated" => user.info.deactivated,
"id" => user.id,
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => false,
"tags" => ["foo", "bar"]
}
]
} }
end end
@ -659,35 +663,39 @@ test "only local users with no query", %{admin: old_admin} do
|> assign(:user, admin) |> assign(:user, admin)
|> get("/api/pleroma/admin/users?filters=local") |> get("/api/pleroma/admin/users?filters=local")
users =
[
%{
"deactivated" => user.info.deactivated,
"id" => user.id,
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => true,
"tags" => []
},
%{
"deactivated" => admin.info.deactivated,
"id" => admin.id,
"nickname" => admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
"local" => true,
"tags" => []
},
%{
"deactivated" => false,
"id" => old_admin.id,
"local" => true,
"nickname" => old_admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
"tags" => []
}
]
|> Enum.sort_by(& &1["nickname"])
assert json_response(conn, 200) == %{ assert json_response(conn, 200) == %{
"count" => 3, "count" => 3,
"page_size" => 50, "page_size" => 50,
"users" => [ "users" => users
%{
"deactivated" => user.info.deactivated,
"id" => user.id,
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => true,
"tags" => []
},
%{
"deactivated" => admin.info.deactivated,
"id" => admin.id,
"nickname" => admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
"local" => true,
"tags" => []
},
%{
"deactivated" => false,
"id" => old_admin.id,
"local" => true,
"nickname" => old_admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
"tags" => []
}
]
} }
end end
@ -698,27 +706,31 @@ test "load only admins", %{conn: conn, admin: admin} do
conn = get(conn, "/api/pleroma/admin/users?filters=is_admin") conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
users =
[
%{
"deactivated" => false,
"id" => admin.id,
"nickname" => admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
"local" => admin.local,
"tags" => []
},
%{
"deactivated" => false,
"id" => second_admin.id,
"nickname" => second_admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
"local" => second_admin.local,
"tags" => []
}
]
|> Enum.sort_by(& &1["nickname"])
assert json_response(conn, 200) == %{ assert json_response(conn, 200) == %{
"count" => 2, "count" => 2,
"page_size" => 50, "page_size" => 50,
"users" => [ "users" => users
%{
"deactivated" => false,
"id" => admin.id,
"nickname" => admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
"local" => admin.local,
"tags" => []
},
%{
"deactivated" => false,
"id" => second_admin.id,
"nickname" => second_admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
"local" => second_admin.local,
"tags" => []
}
]
} }
end end
@ -753,27 +765,31 @@ test "load users with tags list", %{conn: conn} do
conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second") conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
users =
[
%{
"deactivated" => false,
"id" => user1.id,
"nickname" => user1.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => user1.local,
"tags" => ["first"]
},
%{
"deactivated" => false,
"id" => user2.id,
"nickname" => user2.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => user2.local,
"tags" => ["second"]
}
]
|> Enum.sort_by(& &1["nickname"])
assert json_response(conn, 200) == %{ assert json_response(conn, 200) == %{
"count" => 2, "count" => 2,
"page_size" => 50, "page_size" => 50,
"users" => [ "users" => users
%{
"deactivated" => false,
"id" => user1.id,
"nickname" => user1.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => user1.local,
"tags" => ["first"]
},
%{
"deactivated" => false,
"id" => user2.id,
"nickname" => user2.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => user2.local,
"tags" => ["second"]
}
]
} }
end end

View file

@ -0,0 +1,52 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.FallbackTest do
use Pleroma.Web.ConnCase
import Pleroma.Factory
test "GET /registration/:token", %{conn: conn} do
assert conn
|> get("/registration/foo")
|> html_response(200) =~ "<!--server-generated-meta-->"
end
test "GET /:maybe_nickname_or_id", %{conn: conn} do
user = insert(:user)
assert conn
|> get("/foo")
|> html_response(200) =~ "<!--server-generated-meta-->"
refute conn
|> get("/" <> user.nickname)
|> html_response(200) =~ "<!--server-generated-meta-->"
end
test "GET /api*path", %{conn: conn} do
assert conn
|> get("/api/foo")
|> json_response(404) == %{"error" => "Not implemented"}
end
test "GET /*path", %{conn: conn} do
assert conn
|> get("/foo")
|> html_response(200) =~ "<!--server-generated-meta-->"
assert conn
|> get("/foo/bar")
|> html_response(200) =~ "<!--server-generated-meta-->"
end
test "OPTIONS /*path", %{conn: conn} do
assert conn
|> options("/foo")
|> response(204) == ""
assert conn
|> options("/foo/bar")
|> response(204) == ""
end
end

View file

@ -55,7 +55,7 @@ test "Represent a user account" do
fields: [], fields: [],
bot: false, bot: false,
source: %{ source: %{
note: "", note: "valid html",
sensitive: false, sensitive: false,
pleroma: %{} pleroma: %{}
}, },
@ -120,7 +120,7 @@ test "Represent a Service(bot) account" do
fields: [], fields: [],
bot: true, bot: true,
source: %{ source: %{
note: "", note: user.bio,
sensitive: false, sensitive: false,
pleroma: %{} pleroma: %{}
}, },
@ -209,7 +209,7 @@ test "represent an embedded relationship" do
fields: [], fields: [],
bot: true, bot: true,
source: %{ source: %{
note: "", note: user.bio,
sensitive: false, sensitive: false,
pleroma: %{} pleroma: %{}
}, },

View file

@ -1552,6 +1552,72 @@ test "media upload", %{conn: conn} do
assert object.data["actor"] == User.ap_id(user) assert object.data["actor"] == User.ap_id(user)
end end
test "mascot upload", %{conn: conn} do
user = insert(:user)
non_image_file = %Plug.Upload{
content_type: "audio/mpeg",
path: Path.absname("test/fixtures/sound.mp3"),
filename: "sound.mp3"
}
conn =
conn
|> assign(:user, user)
|> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
assert json_response(conn, 415)
file = %Plug.Upload{
content_type: "image/jpg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
conn =
build_conn()
|> assign(:user, user)
|> put("/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")
assert %{"url" => url} = json_response(conn, 200)
assert url =~ "pleroma-fox-tan-smol"
# When a user sets their mascot, we should get that back
file = %Plug.Upload{
content_type: "image/jpg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
conn =
build_conn()
|> assign(:user, user)
|> put("/api/v1/pleroma/mascot", %{"file" => file})
assert json_response(conn, 200)
user = User.get_cached_by_id(user.id)
conn =
build_conn()
|> assign(:user, user)
|> get("/api/v1/pleroma/mascot")
assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
assert url =~ "an_image"
end
test "hashtag timeline", %{conn: conn} do test "hashtag timeline", %{conn: conn} do
following = insert(:user) following = insert(:user)
@ -2716,33 +2782,50 @@ test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
|> post("/api/v1/statuses/#{activity_two.id}/pin") |> post("/api/v1/statuses/#{activity_two.id}/pin")
|> json_response(400) |> json_response(400)
end end
end
test "Status rich-media Card", %{conn: conn, user: user} do describe "cards" do
setup do
Pleroma.Config.put([:rich_media, :enabled], true) Pleroma.Config.put([:rich_media, :enabled], true)
on_exit(fn ->
Pleroma.Config.put([:rich_media, :enabled], false)
end)
user = insert(:user)
%{user: user}
end
test "returns rich-media card", %{conn: conn, user: user} do
{:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"}) {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
card_data = %{
"image" => "http://ia.media-imdb.com/images/rock.jpg",
"provider_name" => "www.imdb.com",
"provider_url" => "http://www.imdb.com",
"title" => "The Rock",
"type" => "link",
"url" => "http://www.imdb.com/title/tt0117500/",
"description" =>
"Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
"pleroma" => %{
"opengraph" => %{
"image" => "http://ia.media-imdb.com/images/rock.jpg",
"title" => "The Rock",
"type" => "video.movie",
"url" => "http://www.imdb.com/title/tt0117500/",
"description" =>
"Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
}
}
}
response = response =
conn conn
|> get("/api/v1/statuses/#{activity.id}/card") |> get("/api/v1/statuses/#{activity.id}/card")
|> json_response(200) |> json_response(200)
assert response == %{ assert response == card_data
"image" => "http://ia.media-imdb.com/images/rock.jpg",
"provider_name" => "www.imdb.com",
"provider_url" => "http://www.imdb.com",
"title" => "The Rock",
"type" => "link",
"url" => "http://www.imdb.com/title/tt0117500/",
"description" => nil,
"pleroma" => %{
"opengraph" => %{
"image" => "http://ia.media-imdb.com/images/rock.jpg",
"title" => "The Rock",
"type" => "video.movie",
"url" => "http://www.imdb.com/title/tt0117500/"
}
}
}
# works with private posts # works with private posts
{:ok, activity} = {:ok, activity} =
@ -2754,9 +2837,33 @@ test "Status rich-media Card", %{conn: conn, user: user} do
|> get("/api/v1/statuses/#{activity.id}/card") |> get("/api/v1/statuses/#{activity.id}/card")
|> json_response(200) |> json_response(200)
assert response_two == response assert response_two == card_data
end
Pleroma.Config.put([:rich_media, :enabled], false) test "replaces missing description with an empty string", %{conn: conn, user: user} do
{:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp-missing-data"})
response =
conn
|> get("/api/v1/statuses/#{activity.id}/card")
|> json_response(:ok)
assert response == %{
"type" => "link",
"title" => "Pleroma",
"description" => "",
"image" => nil,
"provider_name" => "pleroma.social",
"provider_url" => "https://pleroma.social",
"url" => "https://pleroma.social/",
"pleroma" => %{
"opengraph" => %{
"title" => "Pleroma",
"type" => "website",
"url" => "https://pleroma.social/"
}
}
}
end end
end end

View file

@ -0,0 +1,59 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MongooseIMController do
use Pleroma.Web.ConnCase
import Pleroma.Factory
test "/user_exists", %{conn: conn} do
_user = insert(:user, nickname: "lain")
_remote_user = insert(:user, nickname: "alice", local: false)
res =
conn
|> get(mongoose_im_path(conn, :user_exists), user: "lain")
|> json_response(200)
assert res == true
res =
conn
|> get(mongoose_im_path(conn, :user_exists), user: "alice")
|> json_response(404)
assert res == false
res =
conn
|> get(mongoose_im_path(conn, :user_exists), user: "bob")
|> json_response(404)
assert res == false
end
test "/check_password", %{conn: conn} do
user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt("cool"))
res =
conn
|> get(mongoose_im_path(conn, :check_password), user: user.nickname, pass: "cool")
|> json_response(200)
assert res == true
res =
conn
|> get(mongoose_im_path(conn, :check_password), user: user.nickname, pass: "uncool")
|> json_response(403)
assert res == false
res =
conn
|> get(mongoose_im_path(conn, :check_password), user: "nobody", pass: "cool")
|> json_response(404)
assert res == false
end
end

View file

@ -7,6 +7,22 @@ defmodule Pleroma.Web.NodeInfoTest do
import Pleroma.Factory import Pleroma.Factory
test "GET /.well-known/nodeinfo", %{conn: conn} do
links =
conn
|> get("/.well-known/nodeinfo")
|> json_response(200)
|> Map.fetch!("links")
Enum.each(links, fn link ->
href = Map.fetch!(link, "href")
conn
|> get(href)
|> json_response(200)
end)
end
test "nodeinfo shows staff accounts", %{conn: conn} do test "nodeinfo shows staff accounts", %{conn: conn} do
moderator = insert(:user, %{local: true, info: %{is_moderator: true}}) moderator = insert(:user, %{local: true, info: %{is_moderator: true}})
admin = insert(:user, %{local: true, info: %{is_admin: true}}) admin = insert(:user, %{local: true, info: %{is_admin: true}})
@ -32,70 +48,6 @@ test "nodeinfo shows restricted nicknames", %{conn: conn} do
result["metadata"]["restrictedNicknames"] result["metadata"]["restrictedNicknames"]
end end
test "returns 404 when federation is disabled", %{conn: conn} do
instance =
Application.get_env(:pleroma, :instance)
|> Keyword.put(:federating, false)
Application.put_env(:pleroma, :instance, instance)
conn
|> get("/.well-known/nodeinfo")
|> json_response(404)
conn
|> get("/nodeinfo/2.1.json")
|> json_response(404)
instance =
Application.get_env(:pleroma, :instance)
|> Keyword.put(:federating, true)
Application.put_env(:pleroma, :instance, instance)
end
test "returns 200 when federation is enabled", %{conn: conn} do
conn
|> get("/.well-known/nodeinfo")
|> json_response(200)
conn
|> get("/nodeinfo/2.1.json")
|> json_response(200)
end
test "returns 404 when federation is disabled (nodeinfo 2.0)", %{conn: conn} do
instance =
Application.get_env(:pleroma, :instance)
|> Keyword.put(:federating, false)
Application.put_env(:pleroma, :instance, instance)
conn
|> get("/.well-known/nodeinfo")
|> json_response(404)
conn
|> get("/nodeinfo/2.0.json")
|> json_response(404)
instance =
Application.get_env(:pleroma, :instance)
|> Keyword.put(:federating, true)
Application.put_env(:pleroma, :instance, instance)
end
test "returns 200 when federation is enabled (nodeinfo 2.0)", %{conn: conn} do
conn
|> get("/.well-known/nodeinfo")
|> json_response(200)
conn
|> get("/nodeinfo/2.0.json")
|> json_response(200)
end
test "returns software.repository field in nodeinfo 2.1", %{conn: conn} do test "returns software.repository field in nodeinfo 2.1", %{conn: conn} do
conn conn
|> get("/.well-known/nodeinfo") |> get("/.well-known/nodeinfo")

View file

@ -69,4 +69,17 @@ test "deletes all tokens of a user" do
assert tokens == 2 assert tokens == 2
end end
test "deletes expired tokens" do
insert(:oauth_token, valid_until: Timex.shift(Timex.now(), days: -3))
insert(:oauth_token, valid_until: Timex.shift(Timex.now(), days: -3))
t3 = insert(:oauth_token)
t4 = insert(:oauth_token, valid_until: Timex.shift(Timex.now(), minutes: 10))
{tokens, _} = Token.delete_expired_tokens()
assert tokens == 2
available_tokens = Pleroma.Repo.all(Token)
token_ids = available_tokens |> Enum.map(& &1.id)
assert token_ids == [t3.id, t4.id]
end
end end

View file

@ -6,11 +6,7 @@ defmodule Pleroma.Web.FederatingPlugTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
test "returns and halt the conn when federating is disabled" do test "returns and halt the conn when federating is disabled" do
instance = Pleroma.Config.put([:instance, :federating], false)
Application.get_env(:pleroma, :instance)
|> Keyword.put(:federating, false)
Application.put_env(:pleroma, :instance, instance)
conn = conn =
build_conn() build_conn()
@ -19,11 +15,7 @@ test "returns and halt the conn when federating is disabled" do
assert conn.status == 404 assert conn.status == 404
assert conn.halted assert conn.halted
instance = Pleroma.Config.put([:instance, :federating], true)
Application.get_env(:pleroma, :instance)
|> Keyword.put(:federating, true)
Application.put_env(:pleroma, :instance, instance)
end end
test "does nothing when federating is enabled" do test "does nothing when federating is enabled" do

View file

@ -44,6 +44,8 @@ test "parses ogp" do
%{ %{
image: "http://ia.media-imdb.com/images/rock.jpg", image: "http://ia.media-imdb.com/images/rock.jpg",
title: "The Rock", title: "The Rock",
description:
"Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
type: "video.movie", type: "video.movie",
url: "http://www.imdb.com/title/tt0117500/" url: "http://www.imdb.com/title/tt0117500/"
}} }}

View file

@ -5,6 +5,7 @@
defmodule Pleroma.Web.Salmon.SalmonTest do defmodule Pleroma.Web.Salmon.SalmonTest do
use Pleroma.DataCase use Pleroma.DataCase
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Keys
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.Federator.Publisher
@ -34,12 +35,6 @@ test "errors on wrong magic key" do
assert Salmon.decode_and_validate(@wrong_magickey, salmon) == :error assert Salmon.decode_and_validate(@wrong_magickey, salmon) == :error
end end
test "generates an RSA private key pem" do
{:ok, key} = Salmon.generate_rsa_pem()
assert is_binary(key)
assert Regex.match?(~r/RSA/, key)
end
test "it encodes a magic key from a public key" do test "it encodes a magic key from a public key" do
key = Salmon.decode_key(@magickey) key = Salmon.decode_key(@magickey)
magic_key = Salmon.encode_key(key) magic_key = Salmon.encode_key(key)
@ -51,18 +46,10 @@ test "it decodes a friendica public key" do
_key = Salmon.decode_key(@magickey_friendica) _key = Salmon.decode_key(@magickey_friendica)
end end
test "returns a public and private key from a pem" do
pem = File.read!("test/fixtures/private_key.pem")
{:ok, private, public} = Salmon.keys_from_pem(pem)
assert elem(private, 0) == :RSAPrivateKey
assert elem(public, 0) == :RSAPublicKey
end
test "encodes an xml payload with a private key" do test "encodes an xml payload with a private key" do
doc = File.read!("test/fixtures/incoming_note_activity.xml") doc = File.read!("test/fixtures/incoming_note_activity.xml")
pem = File.read!("test/fixtures/private_key.pem") pem = File.read!("test/fixtures/private_key.pem")
{:ok, private, public} = Salmon.keys_from_pem(pem) {:ok, private, public} = Keys.keys_from_pem(pem)
# Let's try a roundtrip. # Let's try a roundtrip.
{:ok, salmon} = Salmon.encode(private, doc) {:ok, salmon} = Salmon.encode(private, doc)
@ -105,7 +92,7 @@ test "it gets a magic key" do
{:ok, activity} = Repo.insert(%Activity{data: activity_data, recipients: activity_data["to"]}) {:ok, activity} = Repo.insert(%Activity{data: activity_data, recipients: activity_data["to"]})
user = User.get_cached_by_ap_id(activity.data["actor"]) user = User.get_cached_by_ap_id(activity.data["actor"])
{:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) {:ok, user} = User.ensure_keys_present(user)
Salmon.publish(user, activity) Salmon.publish(user, activity)

View file

@ -144,41 +144,25 @@ test "returns statuses", %{conn: conn} do
end end
test "returns 403 to unauthenticated request when the instance is not public", %{conn: conn} do test "returns 403 to unauthenticated request when the instance is not public", %{conn: conn} do
instance = Pleroma.Config.put([:instance, :public], false)
Application.get_env(:pleroma, :instance)
|> Keyword.put(:public, false)
Application.put_env(:pleroma, :instance, instance)
conn conn
|> get("/api/statuses/public_timeline.json") |> get("/api/statuses/public_timeline.json")
|> json_response(403) |> json_response(403)
instance = Pleroma.Config.put([:instance, :public], true)
Application.get_env(:pleroma, :instance)
|> Keyword.put(:public, true)
Application.put_env(:pleroma, :instance, instance)
end end
test "returns 200 to authenticated request when the instance is not public", test "returns 200 to authenticated request when the instance is not public",
%{conn: conn, user: user} do %{conn: conn, user: user} do
instance = Pleroma.Config.put([:instance, :public], false)
Application.get_env(:pleroma, :instance)
|> Keyword.put(:public, false)
Application.put_env(:pleroma, :instance, instance)
conn conn
|> with_credentials(user.nickname, "test") |> with_credentials(user.nickname, "test")
|> get("/api/statuses/public_timeline.json") |> get("/api/statuses/public_timeline.json")
|> json_response(200) |> json_response(200)
instance = Pleroma.Config.put([:instance, :public], true)
Application.get_env(:pleroma, :instance)
|> Keyword.put(:public, true)
Application.put_env(:pleroma, :instance, instance)
end end
test "returns 200 to unauthenticated request when the instance is public", %{conn: conn} do test "returns 200 to unauthenticated request when the instance is public", %{conn: conn} do
@ -214,41 +198,25 @@ test "returns 200 to authenticated request when the instance is public",
setup [:valid_user] setup [:valid_user]
test "returns 403 to unauthenticated request when the instance is not public", %{conn: conn} do test "returns 403 to unauthenticated request when the instance is not public", %{conn: conn} do
instance = Pleroma.Config.put([:instance, :public], false)
Application.get_env(:pleroma, :instance)
|> Keyword.put(:public, false)
Application.put_env(:pleroma, :instance, instance)
conn conn
|> get("/api/statuses/public_and_external_timeline.json") |> get("/api/statuses/public_and_external_timeline.json")
|> json_response(403) |> json_response(403)
instance = Pleroma.Config.put([:instance, :public], true)
Application.get_env(:pleroma, :instance)
|> Keyword.put(:public, true)
Application.put_env(:pleroma, :instance, instance)
end end
test "returns 200 to authenticated request when the instance is not public", test "returns 200 to authenticated request when the instance is not public",
%{conn: conn, user: user} do %{conn: conn, user: user} do
instance = Pleroma.Config.put([:instance, :public], false)
Application.get_env(:pleroma, :instance)
|> Keyword.put(:public, false)
Application.put_env(:pleroma, :instance, instance)
conn conn
|> with_credentials(user.nickname, "test") |> with_credentials(user.nickname, "test")
|> get("/api/statuses/public_and_external_timeline.json") |> get("/api/statuses/public_and_external_timeline.json")
|> json_response(200) |> json_response(200)
instance = Pleroma.Config.put([:instance, :public], true)
Application.get_env(:pleroma, :instance)
|> Keyword.put(:public, true)
Application.put_env(:pleroma, :instance, instance)
end end
test "returns 200 to unauthenticated request when the instance is public", %{conn: conn} do test "returns 200 to unauthenticated request when the instance is public", %{conn: conn} do

View file

@ -105,19 +105,4 @@ test "it gets the xrd endpoint for statusnet" do
assert template == "http://status.alpicola.com/main/xrd?uri={uri}" assert template == "http://status.alpicola.com/main/xrd?uri={uri}"
end end
end end
describe "ensure_keys_present" do
test "it creates keys for a user and stores them in info" do
user = insert(:user)
refute is_binary(user.info.keys)
{:ok, user} = WebFinger.ensure_keys_present(user)
assert is_binary(user.info.keys)
end
test "it doesn't create keys if there already are some" do
user = insert(:user, %{info: %{keys: "xxx"}})
{:ok, user} = WebFinger.ensure_keys_present(user)
assert user.info.keys == "xxx"
end
end
end end

View file

@ -5,7 +5,6 @@
defmodule Pleroma.Web.Websub.WebsubControllerTest do defmodule Pleroma.Web.Websub.WebsubControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
import Pleroma.Factory import Pleroma.Factory
alias Pleroma.Activity
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.Web.Websub alias Pleroma.Web.Websub
alias Pleroma.Web.Websub.WebsubClientSubscription alias Pleroma.Web.Websub.WebsubClientSubscription
@ -52,7 +51,7 @@ test "websub subscription confirmation", %{conn: conn} do
end end
describe "websub_incoming" do describe "websub_incoming" do
test "handles incoming feed updates", %{conn: conn} do test "accepts incoming feed updates", %{conn: conn} do
websub = insert(:websub_client_subscription) websub = insert(:websub_client_subscription)
doc = "some stuff" doc = "some stuff"
signature = Websub.sign(websub.secret, doc) signature = Websub.sign(websub.secret, doc)
@ -64,8 +63,6 @@ test "handles incoming feed updates", %{conn: conn} do
|> post("/push/subscriptions/#{websub.id}", doc) |> post("/push/subscriptions/#{websub.id}", doc)
assert response(conn, 200) == "OK" assert response(conn, 200) == "OK"
assert length(Repo.all(Activity)) == 1
end end
test "rejects incoming feed updates with the wrong signature", %{conn: conn} do test "rejects incoming feed updates with the wrong signature", %{conn: conn} do
@ -80,8 +77,6 @@ test "rejects incoming feed updates with the wrong signature", %{conn: conn} do
|> post("/push/subscriptions/#{websub.id}", doc) |> post("/push/subscriptions/#{websub.id}", doc)
assert response(conn, 500) == "Error" assert response(conn, 500) == "Error"
assert Enum.empty?(Repo.all(Activity))
end end
end end
end end