fix merge conflict
This commit is contained in:
commit
0c53d91f3b
11
CHANGELOG.md
11
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`)
|
||||||
- Ability to reset avatar, profile banner and backgroud
|
- Ability to reset avatar, profile banner and backgroud
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -72,6 +79,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Deps: Updated Ecto to 3.0.7
|
- Deps: Updated Ecto to 3.0.7
|
||||||
- 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.
|
||||||
|
- 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,6 +113,7 @@ 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`]
|
||||||
|
- User-Agent is now sent correctly for all HTTP requests.
|
||||||
|
|
||||||
## 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`
|
||||||
|
|
|
@ -192,6 +192,7 @@
|
||||||
# Configures http settings, upstream proxy etc.
|
# Configures http settings, upstream proxy etc.
|
||||||
config :pleroma, :http,
|
config :pleroma, :http,
|
||||||
proxy_url: nil,
|
proxy_url: nil,
|
||||||
|
send_user_agent: true,
|
||||||
adapter: [
|
adapter: [
|
||||||
ssl_options: [
|
ssl_options: [
|
||||||
# We don't support TLS v1.3 yet
|
# We don't support TLS v1.3 yet
|
||||||
|
@ -238,7 +239,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
|
||||||
|
|
||||||
|
@ -275,6 +277,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,
|
||||||
|
@ -297,8 +312,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: [],
|
||||||
|
@ -369,6 +387,7 @@
|
||||||
"activities",
|
"activities",
|
||||||
"api",
|
"api",
|
||||||
"auth",
|
"auth",
|
||||||
|
"check_password",
|
||||||
"dev",
|
"dev",
|
||||||
"friend-requests",
|
"friend-requests",
|
||||||
"inbox",
|
"inbox",
|
||||||
|
@ -389,6 +408,7 @@
|
||||||
"status",
|
"status",
|
||||||
"tag",
|
"tag",
|
||||||
"user-search",
|
"user-search",
|
||||||
|
"user_exists",
|
||||||
"users",
|
"users",
|
||||||
"web"
|
"web"
|
||||||
]
|
]
|
||||||
|
@ -463,7 +483,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
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,8 @@
|
||||||
|
|
||||||
config :pleroma, :http_security, report_uri: "https://endpoint.com"
|
config :pleroma, :http_security, report_uri: "https://endpoint.com"
|
||||||
|
|
||||||
|
config :pleroma, :http, send_user_agent: false
|
||||||
|
|
||||||
rum_enabled = System.get_env("RUM_ENABLED") == "true"
|
rum_enabled = System.get_env("RUM_ENABLED") == "true"
|
||||||
config :pleroma, :database, rum_enabled: rum_enabled
|
config :pleroma, :database, rum_enabled: rum_enabled
|
||||||
IO.puts("RUM enabled: #{rum_enabled}")
|
IO.puts("RUM enabled: #{rum_enabled}")
|
||||||
|
|
|
@ -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`
|
||||||
|
|
|
@ -104,6 +104,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
|
||||||
|
@ -203,12 +204,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
|
||||||
|
@ -467,7 +481,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
|
||||||
|
|
||||||
|
@ -539,6 +553,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,6 +5,7 @@ Possible uses include:
|
||||||
|
|
||||||
* marking incoming messages with media from a given account or instance as sensitive
|
* marking incoming messages with media from a given account or instance as sensitive
|
||||||
* rejecting messages from a specific instance
|
* rejecting messages from a specific instance
|
||||||
|
* rejecting reports (flags) from a specific instance
|
||||||
* removing/unlisting messages from the public timelines
|
* removing/unlisting messages from the public timelines
|
||||||
* removing media from messages
|
* removing media from messages
|
||||||
* sending only public messages to a specific instance
|
* sending only public messages to a specific instance
|
||||||
|
@ -41,12 +42,13 @@ Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_si
|
||||||
* `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
|
* `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
|
||||||
* `reject`: Servers in this group will have their messages rejected.
|
* `reject`: Servers in this group will have their messages rejected.
|
||||||
* `federated_timeline_removal`: Servers in this group will have their messages unlisted from the public timelines by flipping the `to` and `cc` fields.
|
* `federated_timeline_removal`: Servers in this group will have their messages unlisted from the public timelines by flipping the `to` and `cc` fields.
|
||||||
|
* `report_removal`: Servers in this group will have their reports (flags) rejected.
|
||||||
|
|
||||||
Servers should be configured as lists.
|
Servers should be configured as lists.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com` and remove messages from `spam.university` from the federated timeline:
|
This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`, remove messages from `spam.university` from the federated timeline and block reports (flags) from `whiny.whiner`:
|
||||||
|
|
||||||
```
|
```
|
||||||
config :pleroma, :instance,
|
config :pleroma, :instance,
|
||||||
|
@ -56,7 +58,8 @@ config :pleroma, :mrf_simple,
|
||||||
media_removal: ["illegalporn.biz"],
|
media_removal: ["illegalporn.biz"],
|
||||||
media_nsfw: ["porn.biz", "porn.business"],
|
media_nsfw: ["porn.biz", "porn.business"],
|
||||||
reject: ["spam.com"],
|
reject: ["spam.com"],
|
||||||
federated_timeline_removal: ["spam.university"]
|
federated_timeline_removal: ["spam.university"],
|
||||||
|
report_removal: ["whiny.whiner"]
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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+)+)(?<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/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -45,8 +45,15 @@ def url(request, u) do
|
||||||
Add headers to the request
|
Add headers to the request
|
||||||
"""
|
"""
|
||||||
@spec headers(map(), list(tuple)) :: map()
|
@spec headers(map(), list(tuple)) :: map()
|
||||||
def headers(request, h) do
|
def headers(request, header_list) do
|
||||||
Map.put_new(request, :headers, h)
|
header_list =
|
||||||
|
if Pleroma.Config.get([:http, :send_user_agent]) do
|
||||||
|
header_list ++ [{"User-Agent", Pleroma.Application.user_agent()}]
|
||||||
|
else
|
||||||
|
header_list
|
||||||
|
end
|
||||||
|
|
||||||
|
Map.put_new(request, :headers, header_list)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
@ -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
|
|
@ -130,6 +130,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}
|
||||||
|
|
|
@ -8,6 +8,19 @@ defmodule Pleroma.Object.Fetcher do
|
||||||
|
|
||||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
@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.
|
||||||
def fetch_object_from_id(id) do
|
def fetch_object_from_id(id) do
|
||||||
|
@ -26,12 +39,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}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
@ -1402,4 +1403,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}
|
||||||
|
|
||||||
|
|
|
@ -834,6 +834,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)
|
||||||
|
@ -852,6 +859,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)
|
||||||
|
@ -901,7 +909,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"] &&
|
||||||
%{
|
%{
|
||||||
|
@ -948,9 +956,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
|
||||||
|
|
|
@ -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}))
|
||||||
|
|
|
@ -48,10 +48,9 @@ defp check_media_nsfw(
|
||||||
%{host: actor_host} = _actor_info,
|
%{host: actor_host} = _actor_info,
|
||||||
%{
|
%{
|
||||||
"type" => "Create",
|
"type" => "Create",
|
||||||
"object" => %{"attachment" => child_attachment} = child_object
|
"object" => child_object
|
||||||
} = object
|
} = object
|
||||||
)
|
) do
|
||||||
when length(child_attachment) > 0 do
|
|
||||||
object =
|
object =
|
||||||
if Enum.member?(Pleroma.Config.get([:mrf_simple, :media_nsfw]), actor_host) do
|
if Enum.member?(Pleroma.Config.get([:mrf_simple, :media_nsfw]), actor_host) do
|
||||||
tags = (child_object["tag"] || []) ++ ["nsfw"]
|
tags = (child_object["tag"] || []) ++ ["nsfw"]
|
||||||
|
@ -95,18 +94,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.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])
|
||||||
|
|
||||||
|
|
|
@ -157,6 +157,7 @@ def post(user, %{"status" => status} = data) do
|
||||||
{to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility),
|
{to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility),
|
||||||
context <- make_context(in_reply_to),
|
context <- make_context(in_reply_to),
|
||||||
cw <- data["spoiler_text"] || "",
|
cw <- data["spoiler_text"] || "",
|
||||||
|
sensitive <- data["sensitive"] || Enum.member?(tags, {"#nsfw", "nsfw"}),
|
||||||
full_payload <- String.trim(status <> cw),
|
full_payload <- String.trim(status <> cw),
|
||||||
length when length in 1..limit <- String.length(full_payload),
|
length when length in 1..limit <- String.length(full_payload),
|
||||||
object <-
|
object <-
|
||||||
|
@ -169,7 +170,8 @@ def post(user, %{"status" => status} = data) do
|
||||||
in_reply_to,
|
in_reply_to,
|
||||||
tags,
|
tags,
|
||||||
cw,
|
cw,
|
||||||
cc
|
cc,
|
||||||
|
sensitive
|
||||||
),
|
),
|
||||||
object <-
|
object <-
|
||||||
Map.put(
|
Map.put(
|
||||||
|
|
|
@ -223,7 +223,8 @@ def make_note_data(
|
||||||
in_reply_to,
|
in_reply_to,
|
||||||
tags,
|
tags,
|
||||||
cw \\ nil,
|
cw \\ nil,
|
||||||
cc \\ []
|
cc \\ [],
|
||||||
|
sensitive \\ false
|
||||||
) do
|
) do
|
||||||
object = %{
|
object = %{
|
||||||
"type" => "Note",
|
"type" => "Note",
|
||||||
|
@ -231,6 +232,7 @@ def make_note_data(
|
||||||
"cc" => cc,
|
"cc" => cc,
|
||||||
"content" => content_html,
|
"content" => content_html,
|
||||||
"summary" => cw,
|
"summary" => cw,
|
||||||
|
"sensitive" => !Enum.member?(["false", "False", "0", false], sensitive),
|
||||||
"context" => context,
|
"context" => context,
|
||||||
"attachment" => attachments,
|
"attachment" => attachments,
|
||||||
"actor" => actor,
|
"actor" => actor,
|
||||||
|
|
|
@ -11,7 +11,6 @@ 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.Websub
|
alias Pleroma.Web.Websub
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
@ -77,9 +76,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
|
||||||
|
|
|
@ -707,6 +707,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
|
||||||
|
@ -1329,7 +1364,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"]
|
||||||
},
|
},
|
||||||
rights: %{
|
rights: %{
|
||||||
delete_others_notice: present?(user.info.is_moderator),
|
delete_others_notice: present?(user.info.is_moderator),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -24,6 +24,7 @@ defp validate_page_url(_), do: :error
|
||||||
def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) do
|
def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) do
|
||||||
with true <- Pleroma.Config.get([:rich_media, :enabled]),
|
with true <- Pleroma.Config.get([:rich_media, :enabled]),
|
||||||
%Object{} = object <- Object.normalize(activity),
|
%Object{} = object <- Object.normalize(activity),
|
||||||
|
false <- object.data["sensitive"] || false,
|
||||||
{:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]),
|
{:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]),
|
||||||
:ok <- validate_page_url(page_url),
|
:ok <- validate_page_url(page_url),
|
||||||
{:ok, rich_media} <- Parser.parse(page_url) do
|
{:ok, rich_media} <- Parser.parse(page_url) do
|
||||||
|
|
|
@ -352,6 +352,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
|
||||||
|
|
||||||
|
@ -704,9 +707,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)
|
||||||
|
@ -718,6 +727,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")
|
||||||
|
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Web.Salmon do
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
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 +90,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"
|
||||||
|
@ -227,7 +189,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 +215,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)
|
||||||
|
|
||||||
[
|
[
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.WebFinger do
|
||||||
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 +60,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 +70,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 +87,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}
|
||||||
|
|
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)
|
||||||
|
|
Binary file not shown.
|
@ -206,6 +206,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
|
||||||
|
|
|
@ -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} =
|
||||||
|
|
|
@ -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], [])
|
||||||
|
@ -178,6 +208,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 +273,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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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: %{}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1455,6 +1455,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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule Pleroma.Web.RichMedia.HelpersTest do
|
defmodule Pleroma.Web.RichMedia.HelpersTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
alias Pleroma.Object
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
@ -59,4 +60,43 @@ test "crawls valid, complete URLs" do
|
||||||
|
|
||||||
Pleroma.Config.put([:rich_media, :enabled], false)
|
Pleroma.Config.put([:rich_media, :enabled], false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "refuses to crawl URLs from posts marked sensitive" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
"status" => "http://example.com/ogp",
|
||||||
|
"sensitive" => true
|
||||||
|
})
|
||||||
|
|
||||||
|
%Object{} = object = Object.normalize(activity)
|
||||||
|
|
||||||
|
assert object.data["sensitive"]
|
||||||
|
|
||||||
|
Pleroma.Config.put([:rich_media, :enabled], true)
|
||||||
|
|
||||||
|
assert %{} = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||||
|
|
||||||
|
Pleroma.Config.put([:rich_media, :enabled], false)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "refuses to crawl URLs from posts tagged NSFW" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
"status" => "http://example.com/ogp #nsfw"
|
||||||
|
})
|
||||||
|
|
||||||
|
%Object{} = object = Object.normalize(activity)
|
||||||
|
|
||||||
|
assert object.data["sensitive"]
|
||||||
|
|
||||||
|
Pleroma.Config.put([:rich_media, :enabled], true)
|
||||||
|
|
||||||
|
assert %{} = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||||
|
|
||||||
|
Pleroma.Config.put([:rich_media, :enabled], false)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue