Resolve merge conflicts
This commit is contained in:
commit
65db5e9f52
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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`
|
||||||
|
|
|
@ -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"]`
|
||||||
|
|
|
@ -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.
|
|
@ -5,11 +5,12 @@ 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
|
||||||
|
|
||||||
The MRF provides user-configurable policies. The default policy is `NoOpPolicy`, which disables the MRF functionality. Pleroma also includes an easy to use policy called `SimplePolicy` which maps messages matching certain pre-defined criterion to actions built into the policy module.
|
The MRF provides user-configurable policies. The default policy is `NoOpPolicy`, which disables the MRF functionality. Pleroma also includes an easy to use policy called `SimplePolicy` which maps messages matching certain pre-defined criterion to actions built into the policy module.
|
||||||
It is possible to use multiple, active MRF policies at the same time.
|
It is possible to use multiple, active MRF policies at the same time.
|
||||||
|
|
||||||
## Quarantine Instances
|
## Quarantine Instances
|
||||||
|
@ -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"]
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
* 新しいディレクトリに移動します。
|
* 新しいディレクトリに移動します。
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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`
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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, "dGhlcmUgYXJlIG5vIGVhc3RlciBlZ2dzIGhlcmVf"}
|
||||||
|
% ]}
|
||||||
|
% ]},
|
||||||
|
|
||||||
|
{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, "c3RvcCBsb29raW5nIGZvciBlYXN0ZXIgZWdncyxr"},
|
||||||
|
% {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:
|
||||||
|
%%%.
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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 ->
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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") ++
|
||||||
|
|
|
@ -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/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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}
|
||||||
|
|
|
@ -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"}]
|
||||||
),
|
),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}))
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
[
|
[
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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])
|
||||||
|
|
||||||
|
|
|
@ -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, []}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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: [
|
||||||
|
|
|
@ -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: %{}
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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)}"}
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
[
|
[
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
),
|
),
|
||||||
|
|
|
@ -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,
|
||||||
[
|
[
|
||||||
|
|
9
mix.exs
9
mix.exs
|
@ -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}
|
||||||
|
|
2
mix.lock
2
mix.lock
|
@ -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"},
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
Binary file not shown.
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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")}}
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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})
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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: %{}
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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")
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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/"
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue