The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+## Unreleased
+
+## Added
+- Config: HTTP timeout options, :pool\_timeout and :receive\_timeout
+
+## Changed
+- MastoAPI: Accept BooleanLike input on `/api/v1/accounts/:id/follow` (fixes follows with mastodon.py)
+
## 2022.11
## Added
- Scraping of nodeinfo from remote instances to display instance info
- `requested_by` in relationships when the user has requested to follow you
-## Changes
+## Changed
- Follows no longer override domain blocks, a domain block is final
- Deletes are now the lowest priority to publish and will be handled after creates
- Domain blocks are now subdomain-matches by default
-Unless otherwise stated this repository is copyright © 2017-2021
-Pleroma Authors <https://pleroma.social/>, and is distributed under
-The GNU Affero General Public License Version 3, you should have received a
-copy of the license file as AGPL-3.
+Unless otherwise stated this repository is
+Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+Copyright © 2022 Akkoma Authors <https://akkoma.social/>
+and is distributed under The GNU Affero General Public License Version 3, you
+should have received a copy of the license file as AGPL-3.
---
-Files inside docs directory are copyright © 2021 Pleroma Authors
-<https://pleroma.social/>, and are distributed under the Creative Commons
+Files inside docs directory are
+Copyright © 2021-2022 Pleroma Authors <https://pleroma.social/>
+Copyright © 2022 Akkoma Authors <https://akkoma.social/>
+and are distributed under the Creative Commons
Attribution 4.0 International license, you should have received
a copy of the license file as CC-BY-4.0.
under the Creative Commons Attribution-ShareAlike 4.0 International license,
you should have received a copy of the license file as CC-BY-SA-4.0.
-priv/static/images/pleroma-fox-tan.png
priv/static/images/pleroma-fox-tan-smol.png
-priv/static/images/pleroma-tan.png
-
----
-
-The following files are copyright © 2019 shitposter.club, and are distributed
-under the Creative Commons Attribution 4.0 International license, you should
-have received a copy of the license file as CC-BY-4.0.
-
-priv/static/images/pleroma-fox-tan-shy.png
---
Attribution-ShareAlike 4.0 International license, you should have received
a copy of the license file as CC-BY-SA-4.0.
-priv/static/images/avi.png
-priv/static/images/banner.png
priv/static/instance/thumbnail.jpeg
---
# Configures http settings, upstream proxy etc.
config :pleroma, :http,
+ pool_timeout: :timer.seconds(5),
+ receive_timeout: :timer.seconds(15),
proxy_url: nil,
user_agent: :default,
adapter: []
logo: "/static/logo.svg",
logoMargin: ".1em",
logoMask: true,
- minimalScopesMode: false,
noAttachmentLinks: false,
nsfwCensorImage: "",
postContentType: "text/plain",
redirectRootLogin: "/main/friends",
- redirectRootNoLogin: "/main/all",
+ redirectRootNoLogin: "/main/public",
scopeCopy: true,
sidebarRight: false,
showFeaturesPanel: true,
showInstanceSpecificPanel: false,
subjectLineBehavior: "email",
theme: "pleroma-dark",
- webPushNotifications: false
+ webPushNotifications: false,
+ conversationDisplay: "linear"
},
masto_fe: %{
showInstanceSpecificPanel: true
config :pleroma, :http_security,
enabled: true,
sts: false,
- sts_max_age: 31_536_000,
- ct_max_age: 2_592_000,
+ sts_max_age: 63_072_000,
referrer_policy: "same-origin"
config :cors_plug,
federator_incoming: 5,
federator_outgoing: 5,
search_indexing: 2
+ ],
+ timeout: [
+ activity_expiration: :timer.seconds(5),
+ token_expiration: :timer.seconds(5),
+ filter_expiration: :timer.seconds(5),
+ backup: :timer.seconds(900),
+ federator_incoming: :timer.seconds(10),
+ federator_outgoing: :timer.seconds(10),
+ ingestion_queue: :timer.seconds(5),
+ web_push: :timer.seconds(5),
+ mailer: :timer.seconds(5),
+ transmogrifier: :timer.seconds(5),
+ scheduled_activities: :timer.seconds(5),
+ poll_notifications: :timer.seconds(5),
+ background: :timer.seconds(5),
+ remote_fetcher: :timer.seconds(10),
+ attachments_cleanup: :timer.seconds(900),
+ new_users_digest: :timer.seconds(10),
+ mute_expire: :timer.seconds(5),
+ search_indexing: :timer.seconds(5),
+ nodeinfo_fetcher: :timer.seconds(10)
]
config :pleroma, Pleroma.Formatter,
type: :boolean,
description: "Enables green text on lines prefixed with the > character"
},
+ %{
+ key: :conversationDisplay,
+ label: "Conversation display style",
+ type: :string,
+ description: "How to display conversations (linear or tree)",
+ suggestions: ["linear", "tree"]
+ },
%{
key: :hideFilteredStatuses,
label: "Hide Filtered Statuses",
"By default it assumes logo used will be monochrome with alpha channel to be compatible with both light and dark themes. " <>
"If you want a colorful logo you must disable logoMask."
},
- %{
- key: :minimalScopesMode,
- label: "Minimal scopes mode",
- type: :boolean,
- description:
- "Limit scope selection to Direct, User default, and Scope of post replying to. " <>
- "Also prevents replying to a DM with a public post from PleromaFE."
- },
%{
key: :nsfwCensorImage,
label: "NSFW Censor Image",
label: "STS max age",
type: :integer,
description: "The maximum age for the Strict-Transport-Security header if sent",
- suggestions: [31_536_000]
- },
- %{
- key: :ct_max_age,
- label: "CT max age",
- type: :integer,
- description: "The maximum age for the Expect-CT header if sent",
- suggestions: [2_592_000]
+ suggestions: [63_072_000]
},
%{
key: :referrer_policy,
federator_incoming: 5,
federator_outgoing: 5
]
+ },
+ %{
+ key: :timeout,
+ type: {:keyword, :integer},
+ description: "Timeout for jobs, per `Oban` queue, in ms",
+ suggestions: [
+ activity_expiration: :timer.seconds(5),
+ token_expiration: :timer.seconds(5),
+ filter_expiration: :timer.seconds(5),
+ backup: :timer.seconds(900),
+ federator_incoming: :timer.seconds(10),
+ federator_outgoing: :timer.seconds(10),
+ ingestion_queue: :timer.seconds(5),
+ web_push: :timer.seconds(5),
+ mailer: :timer.seconds(5),
+ transmogrifier: :timer.seconds(5),
+ scheduled_activities: :timer.seconds(5),
+ poll_notifications: :timer.seconds(5),
+ background: :timer.seconds(5),
+ remote_fetcher: :timer.seconds(10),
+ attachments_cleanup: :timer.seconds(900),
+ new_users_digest: :timer.seconds(10),
+ mute_expire: :timer.seconds(5),
+ search_indexing: :timer.seconds(5),
+ nodeinfo_fetcher: :timer.seconds(10)
+ ]
}
]
},
type: :group,
description: "HTTP settings",
children: [
+ %{
+ key: :pool_timeout,
+ label: "HTTP Pool Request Timeout",
+ type: :integer,
+ description: "Timeout for initiating HTTP requests (in ms, default 5000)",
+ suggestions: [5000]
+ },
+ %{
+ key: :receive_timeout,
+ label: "HTTP Receive Timeout",
+ type: :integer,
+ description:
+ "Timeout for waiting on remote servers to respond to HTTP requests (in ms, default 15000)",
+ suggestions: [15000]
+ },
%{
key: :proxy_url,
label: "Proxy URL",
-# Pleroma Clients
-Note: Additional clients may be working but theses are officially supporting Pleroma.
-Feel free to contact us to be added to this list!
+# Akkoma Clients
+Note: Additional clients may work, but these are known to work with Akkoma.
+Apps listed here might not support all of Akkoma's features.
## Desktop
-### Roma for Desktop
-- Homepage: <https://www.pleroma.com/#desktopApp>
-- Source Code: <https://github.com/roma-apps/roma-desktop>
-- Platforms: Windows, Mac, Linux
-- Features: MastoAPI, Streaming Ready
-
-### Social
-- Source Code: <https://gitlab.gnome.org/World/Social>
-- Contact: [@brainblasted@social.libre.fi](https://social.libre.fi/users/brainblasted)
-- Platforms: Linux (GNOME)
-- Note(2019-01-28): Not at a pre-alpha stage yet
-- Features: MastoAPI
-
### Whalebird
- Homepage: <https://whalebird.social/>
- Source Code: <https://github.com/h3poteto/whalebird-desktop>
- Platforms: Android
- Features: MastoAPI, ActivityPub (Client-to-Server)
-### Amaroq
-- Homepage: <https://itunes.apple.com/us/app/amaroq-for-mastodon/id1214116200>
-- Source Code: <https://github.com/ReticentJohn/Amaroq>
-- Contact: [@eurasierboy@mastodon.social](https://mastodon.social/users/eurasierboy)
-- Platforms: iOS
-- Features: MastoAPI, No Streaming
-
### Fedilab
- Homepage: <https://fedilab.app/>
-- Source Code: <https://framagit.org/tom79/fedilab/>
-- Contact: [@fedilab@framapiaf.org](https://framapiaf.org/users/fedilab)
+- Source Code: <https://codeberg.org/tom79/Fedilab>
+- Contact: [@apps@toot.felilab.app](https://toot.fedilab.app/@apps)
- Platforms: Android
- Features: MastoAPI, Streaming Ready, Moderation, Text Formatting
-### Kyclos
-- Source Code: <https://git.pleroma.social/pleroma/harbour-kyclos>
-- Platforms: SailfishOS
-- Features: MastoAPI, No Streaming
-
### Husky
-- Source code: <https://git.mentality.rip/FWGS/Husky>
-- Contact: [@Husky@enigmatic.observer](https://enigmatic.observer/users/Husky)
+- Source code: <https://git.sr.ht/~captainepoch/husky>
+- Contact: [@captainepoch@stereophonic.space](https://stereophonic.space/captainepoch)
- Platforms: Android
- Features: MastoAPI, No Streaming, Emoji Reactions, Text Formatting, FE Stickers
- Platforms: Android
- Features: MastoAPI, No Streaming
-### Twidere
-- Homepage: <https://twidere.mariotaku.org/>
-- Source Code: <https://github.com/TwidereProject/Twidere-Android/>
-- Contact: <me@mariotaku.org>
-- Platform: Android
-- Features: MastoAPI, No Streaming
-
-### Indigenous
-- Homepage: <https://indigenous.realize.be/>
-- Source Code: <https://github.com/swentel/indigenous-android/>
-- Contact: [@swentel@realize.be](https://realize.be)
-- Platforms: Android
-- Features: MastoAPI, No Streaming
-
## Alternative Web Interfaces
-### Brutaldon
-- Homepage: <https://jfm.carcosa.net/projects/software/brutaldon/>
-- Source Code: <https://git.carcosa.net/jmcbray/brutaldon>
-- Contact: [@gcupc@glitch.social](https://glitch.social/users/gcupc)
-- Features: MastoAPI, No Streaming
-
-### Halcyon
-- Source Code: <https://notabug.org/halcyon-suite/halcyon>
-- Contact: [@halcyon@social.csswg.org](https://social.csswg.org/users/halcyon)
-- Features: MastoAPI, Streaming Ready
-
### Pinafore
- Homepage: <https://pinafore.social/>
- Source Code: <https://github.com/nolanlawson/pinafore>
* `cleanup_attachments`: Remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances.
* `show_reactions`: Let favourites and emoji reactions be viewed through the API (default: `true`).
* `password_reset_token_validity`: The time after which reset tokens aren't accepted anymore, in seconds (default: one day).
-* `local_bubble`: Array of domains representing instances closely related to yours. Used to populate the `bubble` timeline. e.g `['example.com']`, (default: `[]`)
+* `local_bubble`: Array of domains representing instances closely related to yours. Used to populate the `bubble` timeline. e.g `["example.com"]`, (default: `[]`)
+* `languages`: List of Language Codes used by the instance. This is used to try and set a default language from the frontend. It will try and find the first match between the languages set here and the user's browser languages. It will default to the first language in this setting if there is no match.. (default `["en"]`)
## :database
* `improved_hashtag_timeline`: Setting to force toggle / force disable improved hashtags timeline. `:enabled` forces hashtags to be fetched from `hashtags` table for hashtags timeline. `:disabled` forces object-embedded hashtags to be used (slower). Keep it `:auto` for automatic behaviour (it is auto-set to `:enabled` [unless overridden] when HashtagsTableMigrator completes).
* ``enabled``: Whether the managed content security policy is enabled.
* ``sts``: Whether to additionally send a `Strict-Transport-Security` header.
* ``sts_max_age``: The maximum age for the `Strict-Transport-Security` header if sent.
-* ``ct_max_age``: The maximum age for the `Expect-CT` header if sent.
* ``referrer_policy``: The referrer policy to use, either `"same-origin"` or `"no-referrer"`.
* ``report_uri``: Adds the specified url to `report-uri` and `report-to` group in CSP header.
### :http
+* `receive_timeout`: the amount of time, in ms, to wait for a remote server to respond to a request. (default: `15000`)
+* `pool_timeout`: the amount of time, in ms, to wait to check out an HTTP connection from the pool. This likely does not need changing unless your instance is _very_ busy with outbound requests. (default `5000`)
* `proxy_url`: an upstream proxy to fetch posts and/or media with, (default: `nil`); for example `http://127.0.0.1:3192`. Does not support SOCKS5 proxy, only http(s).
* `send_user_agent`: should we include a user agent with HTTP requests? (default: `true`)
* `user_agent`: what user agent should we use? (default: `:default`), must be string or `:default`
This will send additional HTTP security headers to the clients, including:
-* `X-XSS-Protection: "1; mode=block"`
+* `X-XSS-Protection: "0"`
* `X-Permitted-Cross-Domain-Policies: "none"`
* `X-Frame-Options: "DENY"`
* `X-Content-Type-Options: "nosniff"`
-* `X-Download-Options: "noopen"`
A content security policy (CSP) will also be set:
```csp
content-security-policy:
default-src 'none';
- base-uri 'self';
+ base-uri 'none';
frame-ancestors 'none';
img-src 'self' data: blob: https:;
media-src 'self' https:;
An additional “Strict transport security” header will be sent with the configured `sts_max_age` parameter. This tells the browser, that the domain should only be accessed over a secure HTTPs connection.
-#### `ct_max_age`
-
-An additional “Expect-CT” header will be sent with the configured `ct_max_age` parameter. This enforces the use of TLS certificates that are published in the certificate transparency log. (see [Expect-CT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT))
-
#### `referrer_policy`
> Recommended value: `same-origin`
}
```
-If you added it in the back-end configuration file, you'll need to restart your instance for the changes to take effect. If you don't see the changes, it's probably because the browser has cached the previous theme. In that case you'll want to clear browser caches. Alternatively you can use a private/incognito window just to see the changes.
-
+If you added it in the back-end configuration file, you'll need to restart your instance for the changes to take effect. If you don't see the changes, it's probably because the browser has cached the previous theme. In that case you'll want to clear browser caches. Alternatively you can use a private/incognito window just to see the changes.
\ No newline at end of file
location / {
- add_header X-XSS-Protection "1; mode=block";
+ add_header X-XSS-Protection "0";
add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin;
- add_header X-Download-Options noopen;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
location / {
- add_header X-XSS-Protection "1; mode=block";
+ add_header X-XSS-Protection "0";
add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin;
- add_header X-Download-Options noopen;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
Terms of Service will be shown to all users on the registration page. It's the best place where to write down the rules for your instance. You can modify the rules by adding and changing `$static_dir/static/terms-of-service.html`.
+## Favicon
+
+The favicon will display on the frontend, and in the browser tab.
+
+Place a PNG file at `$static_dir/favicon.png` to change the favicon. Not that this
+is _one level above_ where the logo is placed, it should be on the same level as
+the `frontends` directory.
## Styling rendered pages
- `preview`: boolean, if set to `true` the post won't be actually posted, but the status entity would still be rendered back. This could be useful for previewing rich text/custom emoji, for example.
- `content_type`: string, contain the MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint.
-- `to`: A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for post visibility are not affected by this and will still apply.
+- `to`: A list of nicknames (like `admin@otp.akkoma.dev` or `admin` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for post visibility are not affected by this and will still apply.
- `visibility`: string, besides standard MastoAPI values (`direct`, `private`, `unlisted`, `local` or `public`) it can be used to address a List by setting it to `list:LIST_ID`.
- `expires_in`: The number of seconds the posted activity should expire in. When a posted activity expires it will be deleted from the server, and a delete request for it will be federated. This needs to be longer than an hour.
- `in_reply_to_conversation_id`: Will reply to a given conversation, addressing only the people who are part of the recipient set of that conversation. Sets the visibility to `direct`.
metrics_path: /api/pleroma/app_metrics
scheme: https
static_configs:
- - targets: ['pleroma.soykaf.com']
+ - targets: ['otp.akkoma.dev']
```
## How can I use it?
-Akkoma instances are already widely deployed, a list can be found at <https://the-federation.info/pleroma> and <https://fediverse.network/pleroma>.
+Akkoma instances are already widely deployed, a list can be found at <https://the-federation.info/akkoma> and <https://akkoma.fediverse.observer/list>.
If you don't feel like joining an existing instance, but instead prefer to deploy your own instance, that's easy too!
Installation instructions can be found in the installation section of these docs.
## I got an account, now what?
-Great! Now you can explore the fediverse! Open the login page for your Akkoma instance (e.g. <https://pleroma.soykaf.com>) and login with your username and password. (If you don't have an account yet, click on Register)
+Great! Now you can explore the fediverse! Open the login page for your Akkoma instance (e.g. <https://otp.akkoma.dev>) and login with your username and password. (If you don't have an account yet, click on Register)
### Pleroma-FE
The default front-end used by Akkoma is Pleroma-FE. You can find more information on what it is and how to use it in the [Introduction to Pleroma-FE](https://docs-fe.akkoma.dev/stable/).
### Mastodon interface
If the Pleroma-FE interface isn't your thing, or you're just trying something new but you want to keep using the familiar Mastodon interface, we got that too!
-Just add a "/web" after your instance url (e.g. <https://pleroma.soykaf.com/web>) and you'll end on the Mastodon web interface, but with a Akkoma backend! MAGIC!
+Just add a "/web" after your instance url (e.g. <https://otp.akkoma.dev/web>) and you'll end on the Mastodon web interface, but with a Akkoma backend! MAGIC!
The Mastodon interface is from the Glitch-soc fork. For more information on the Mastodon interface you can check the [Mastodon](https://docs.joinmastodon.org/) and [Glitch-soc](https://glitch-soc.github.io/docs/) documentation.
Remember, what you see is only the frontend part of Mastodon, the backend is still Akkoma.
[forked pleroma-fe repository](https://akkoma.dev/AkkomaGang/pleroma-fe),
and either merge or cherry-pick from there depending on how you've got
things.
+
+## Common issues
+
+### The frontend doesn't show after installing it
+
+This may occur if you are using database configuration.
+
+Sometimes the config in your database will cause akkoma to still report
+that there's no frontend, even when you've run the install.
+
+To fix this, run:
+
+=== "OTP"
+ ```sh
+ ./bin/pleroma_ctl config delete pleroma frontends
+ ```
+
+=== "From Source"
+ ```sh
+ mix pleroma.config delete pleroma frontends
+ ```
+
+which will remove the config from the database. Things should work now.
\ No newline at end of file
match request header append "X-Forwarded-For" value "$REMOTE_ADDR" # This two header and the next one are not strictly required by akkoma but adding them won't hurt
match request header append "X-Forwarded-By" value "$SERVER_ADDR:$SERVER_PORT"
- match response header append "X-XSS-Protection" value "1; mode=block"
+ match response header append "X-XSS-Protection" value "0"
match response header append "X-Permitted-Cross-Domain-Policies" value "none"
match response header append "X-Frame-Options" value "DENY"
match response header append "X-Content-Type-Options" value "nosniff"
match response header append "Referrer-Policy" value "same-origin"
- match response header append "X-Download-Options" value "noopen"
- match response header append "Content-Security-Policy" value "default-src 'none'; base-uri 'self'; form-action 'self'; img-src 'self' data: https:; media-src 'self' https:; style-src 'self' 'unsafe-inline'; font-src 'self'; script-src 'self'; connect-src 'self' wss://CHANGEME.tld; upgrade-insecure-requests;" # Modify "CHANGEME.tld" and set your instance's domain here
+ match response header append "Content-Security-Policy" value "default-src 'none'; base-uri 'none'; form-action 'self'; img-src 'self' data: https:; media-src 'self' https:; style-src 'self' 'unsafe-inline'; font-src 'self'; script-src 'self'; connect-src 'self' wss://CHANGEME.tld; upgrade-insecure-requests;" # Modify "CHANGEME.tld" and set your instance's domain here
match request header append "Connection" value "upgrade"
- #match response header append "Strict-Transport-Security" value "max-age=31536000; includeSubDomains" # Uncomment this only after you get HTTPS working.
+ #match response header append "Strict-Transport-Security" value "max-age=63072000; includeSubDomains; preload" # Uncomment this only after you get HTTPS working.
# If you do not want remote frontends to be able to access your Akkoma backend server, comment these lines
match response header append "Access-Control-Allow-Origin" value "*"
| {:error, atom()}
| nil
def parse_proxy(nil), do: nil
+ def parse_proxy(""), do: nil
def parse_proxy(proxy) when is_binary(proxy) do
with %URI{} = uri <- URI.parse(proxy),
@spec options(keyword(), URI.t()) :: keyword()
def options(opts, _uri) do
proxy = Pleroma.Config.get([:http, :proxy_url])
- AdapterHelper.maybe_add_proxy(opts, AdapterHelper.format_proxy(proxy))
+ pool_timeout = Pleroma.Config.get([:http, :pool_timeout], 5000)
+ receive_timeout = Pleroma.Config.get([:http, :receive_timeout], 15_000)
+
+ opts
+ |> AdapterHelper.maybe_add_proxy(AdapterHelper.format_proxy(proxy))
+ |> Keyword.put(:pool_timeout, pool_timeout)
+ |> Keyword.put(:receive_timeout, receive_timeout)
end
@spec get_conn(URI.t(), keyword()) :: {:ok, keyword()}
defmodule Pleroma.Object.Fetcher do
alias Pleroma.HTTP
+ alias Pleroma.Instances
alias Pleroma.Maps
alias Pleroma.Object
alias Pleroma.Object.Containment
{:ok, body} <- get_object(id),
{:ok, data} <- safe_json_decode(body),
:ok <- Containment.contain_origin_from_id(id, data) do
+ unless Instances.reachable?(id) do
+ Instances.set_reachable(id)
+ end
+
{:ok, data}
else
{:scheme, _} ->
{%User{} = user, _} ->
{:ok, user}
- _ ->
+ e ->
+ Logger.error("Could not fetch user, #{inspect(e)}")
{:error, :not_found}
end
end
end
end
+ defp exclude_invisible_actors(query, %{type: "Flag"}), do: query
defp exclude_invisible_actors(query, %{invisible_actors: true}), do: query
defp exclude_invisible_actors(query, _opts) do
- invisible_ap_ids =
- User.Query.build(%{invisible: true, select: [:ap_id]})
- |> Repo.all()
- |> Enum.map(fn %{ap_id: ap_id} -> ap_id end)
-
- from([activity] in query, where: activity.actor not in ^invisible_ap_ids)
+ query
+ |> join(:inner, [activity], u in User,
+ as: :u,
+ on: activity.actor == u.ap_id and u.invisible == false
+ )
end
defp exclude_id(query, %{exclude_id: id}) when is_binary(id) do
|> restrict_instance(opts)
|> restrict_announce_object_actor(opts)
|> restrict_filtered(opts)
- |> Activity.restrict_deactivated_users()
+ |> maybe_restrict_deactivated_users(opts)
|> exclude_poll_votes(opts)
|> exclude_invisible_actors(opts)
|> exclude_visibility(opts)
# we request WebFinger here
nickname = additional[:nickname_from_acct] || generate_nickname(data)
+ # also_known_as must be a URL
+ also_known_as =
+ data
+ |> Map.get("alsoKnownAs", [])
+ |> Enum.filter(fn url ->
+ case URI.parse(url) do
+ %URI{scheme: "http"} -> true
+ %URI{scheme: "https"} -> true
+ _ -> false
+ end
+ end)
+
%{
ap_id: data["id"],
uri: get_actor_url(data["url"]),
featured_address: featured_address,
bio: data["summary"] || "",
actor_type: actor_type,
- also_known_as: Map.get(data, "alsoKnownAs", []),
+ also_known_as: also_known_as,
public_key: public_key,
inbox: data["inbox"],
shared_inbox: shared_inbox,
|> restrict_visibility(%{visibility: "direct"})
|> order_by([activity], asc: activity.id)
end
+
+ defp maybe_restrict_deactivated_users(activity, %{type: "Flag"}), do: activity
+
+ defp maybe_restrict_deactivated_users(activity, _opts),
+ do: Activity.restrict_deactivated_users(activity)
end
type: :object,
properties: %{
reblogs: %Schema{
- type: :boolean,
+ allOf: [BooleanLike],
description: "Receive this account's reblogs in home timeline? Defaults to true.",
default: true
},
notify: %Schema{
- type: :boolean,
+ allOf: [BooleanLike],
description:
"Receive notifications for all statuses posted by the account? Defaults to false.",
default: false
@impl true
def publish(%{data: %{"object" => object}} = activity) when is_binary(object) do
- PublisherWorker.enqueue("publish", %{"activity_id" => activity.id, "object_data" => nil})
+ PublisherWorker.enqueue("publish", %{"activity_id" => activity.id, "object_data" => nil},
+ priority: publish_priority(activity)
+ )
end
@impl true
)
end
- defp publish_priority(%{type: "Delete"}), do: 3
+ defp publish_priority(%{data: %{"type" => "Delete"}}), do: 3
defp publish_priority(_), do: 0
# Job Worker Callbacks
custom_http_frontend_headers = custom_http_frontend_headers()
headers = [
- {"x-xss-protection", "1; mode=block"},
+ {"x-xss-protection", "0"},
{"x-permitted-cross-domain-policies", "none"},
{"x-frame-options", "DENY"},
{"x-content-type-options", "nosniff"},
{"referrer-policy", referrer_policy},
- {"x-download-options", "noopen"},
{"content-security-policy", csp_string()},
{"permissions-policy", "interest-cohort=()"}
]
static_csp_rules = [
"default-src 'none'",
- "base-uri 'self'",
+ "base-uri 'none'",
"frame-ancestors 'none'",
"style-src 'self' 'unsafe-inline'",
"font-src 'self'",
defp maybe_send_sts_header(conn, true) do
max_age_sts = Config.get([:http_security, :sts_max_age])
- max_age_ct = Config.get([:http_security, :ct_max_age])
merge_resp_headers(conn, [
- {"strict-transport-security", "max-age=#{max_age_sts}; includeSubDomains"},
- {"expect-ct", "enforce, max-age=#{max_age_ct}"}
+ {"strict-transport-security", "max-age=#{max_age_sts}; includeSubDomains; preload"}
])
end
|> Oban.insert()
end
+ @impl Oban.Worker
+ def timeout(_job) do
+ Pleroma.Config.get([:workers, :timeout, :backup], :timer.minutes(1))
+ end
+
def schedule_deletion(backup) do
days = Pleroma.Config.get([Backup, :purge_after_days])
time = 60 * 60 * 24 * days
|> Oban.insert()
end
+ @impl true
def perform(%Job{
args: %{"op" => "process", "backup_id" => backup_id, "admin_user_id" => admin_user_id}
}) do
end
end
+ @impl Oban.Worker
+ def timeout(_job) do
+ Pleroma.Config.get([:workers, :timeout, :activity_expiration], :timer.minutes(1))
+ end
+
@impl true
def perform(%Oban.Job{args: %{"activity_id" => id}}) do
with %Activity{} = activity <- find_activity(id),
|> Oban.insert()
end
+ @impl Oban.Worker
+ def timeout(_job) do
+ Pleroma.Config.get([:workers, :timeout, :filter_expiration], :timer.minutes(1))
+ end
+
@impl true
def perform(%Job{args: %{"filter_id" => id}}) do
Pleroma.Filter
|> Oban.insert()
end
+ @impl Oban.Worker
+ def timeout(_job) do
+ Pleroma.Config.get([:workers, :timeout, :token_expiration], :timer.minutes(1))
+ end
+
@impl true
def perform(%Oban.Job{args: %{"token_id" => id, "mod" => module}}) do
module
|> apply(:new, [params, worker_args])
|> Oban.insert()
end
+
+ @impl Oban.Worker
+ def timeout(_job) do
+ queue_atom = String.to_atom(unquote(queue))
+ Config.get([:workers, :timeout, queue_atom], :timer.minutes(1))
+ end
end
end
end
"What user agent to use. Must be a string or an atom `:default`. Default "
"value is `:default`."
-#: lib/pleroma/docs/translator.ex:5
-#, fuzzy
-msgctxt "config description at :pleroma-:http_security > :ct_max_age"
-msgid "The maximum age for the Expect-CT header if sent"
-msgstr "The maximum age for the Expect-CT header if sent"
-
#: lib/pleroma/docs/translator.ex:5
#, fuzzy
msgctxt "config description at :pleroma-:http_security > :enabled"
msgid "User agent"
msgstr "User agent"
-#: lib/pleroma/docs/translator.ex:5
-#, fuzzy
-msgctxt "config label at :pleroma-:http_security > :ct_max_age"
-msgid "CT max age"
-msgstr "CT max age"
-
#: lib/pleroma/docs/translator.ex:5
#, fuzzy
msgctxt "config label at :pleroma-:http_security > :enabled"
msgid "What user agent to use. Must be a string or an atom `:default`. Default value is `:default`."
msgstr ""
-#, elixir-autogen, elixir-format
-#: lib/pleroma/docs/translator.ex:5
-msgctxt "config description at :pleroma-:http_security > :ct_max_age"
-msgid "The maximum age for the Expect-CT header if sent"
-msgstr ""
-
#, elixir-autogen, elixir-format
#: lib/pleroma/docs/translator.ex:5
msgctxt "config description at :pleroma-:http_security > :enabled"
msgid "User agent"
msgstr ""
-#, elixir-autogen, elixir-format
-#: lib/pleroma/docs/translator.ex:5
-msgctxt "config label at :pleroma-:http_security > :ct_max_age"
-msgid "CT max age"
-msgstr ""
-
#, elixir-autogen, elixir-format
#: lib/pleroma/docs/translator.ex:5
msgctxt "config label at :pleroma-:http_security > :enabled"
"What user agent to use. Must be a string or an atom `:default`. Default "
"value is `:default`."
-#: lib/pleroma/docs/translator.ex:5
-#, elixir-autogen, elixir-format, fuzzy
-msgctxt "config description at :pleroma-:http_security > :ct_max_age"
-msgid "The maximum age for the Expect-CT header if sent"
-msgstr "The maximum age for the Expect-CT header if sent"
-
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config description at :pleroma-:http_security > :enabled"
msgid "User agent"
msgstr "User agent"
-#: lib/pleroma/docs/translator.ex:5
-#, elixir-autogen, elixir-format, fuzzy
-msgctxt "config label at :pleroma-:http_security > :ct_max_age"
-msgid "CT max age"
-msgstr "CT max age"
-
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config label at :pleroma-:http_security > :enabled"
"What user agent to use. Must be a string or an atom `:default`. Default "
"value is `:default`."
-#: lib/pleroma/docs/translator.ex:5
-#, elixir-autogen, elixir-format, fuzzy
-msgctxt "config description at :pleroma-:http_security > :ct_max_age"
-msgid "The maximum age for the Expect-CT header if sent"
-msgstr "The maximum age for the Expect-CT header if sent"
-
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config description at :pleroma-:http_security > :enabled"
msgid "User agent"
msgstr "User agent"
-#: lib/pleroma/docs/translator.ex:5
-#, elixir-autogen, elixir-format, fuzzy
-msgctxt "config label at :pleroma-:http_security > :ct_max_age"
-msgid "CT max age"
-msgstr "CT max age"
-
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config label at :pleroma-:http_security > :enabled"
--- /dev/null
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {
+ "Hashtag": "as:Hashtag",
+ "sensitive": "as:sensitive",
+ "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+ "alsoKnownAs": {
+ "@id": "as:alsoKnownAs",
+ "@type": "@id"
+ },
+ "movedTo": {
+ "@id": "as:movedTo",
+ "@type": "@id"
+ },
+ "toot": "http://joinmastodon.org/ns#",
+ "featured": {
+ "@id": "toot:featured",
+ "@type": "@id"
+ },
+ "Emoji": "toot:Emoji",
+ "blurhash": "toot:blurhash",
+ "votersCount": "toot:votersCount",
+ "schema": "http://schema.org#",
+ "PropertyValue": "schema:PropertyValue",
+ "value": "schema:value",
+ "ostatus": "http://ostatus.org#",
+ "conversation": "ostatus:conversation"
+ }
+ ],
+ "type": "Person",
+ "id": "https://mbp.example.com",
+ "following": "https://mbp.example.com/following",
+ "followers": "https://mbp.example.com/followers",
+ "featured": "https://mbp.example.com/featured",
+ "inbox": "https://mbp.example.com/inbox",
+ "outbox": "https://mbp.example.com/outbox",
+ "preferredUsername": "MBP",
+ "name": "MBP",
+ "summary": "wowee",
+ "endpoints": {
+ "sharedInbox": "https://mbp.example.com/inbox"
+ },
+ "url": "https://mbp.example.com/",
+ "manuallyApprovesFollowers": false,
+ "attachment": [],
+ "icon": {
+ "mediaType": "image/jpeg",
+ "type": "Image",
+ "url": "https://beta.4201337.xyz/static/denise.jpg"
+ },
+ "tag": [],
+ "alsoKnownAs": [
+ "example@elsewhere.com"
+ ]
+}
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.AdapterHelperTest do
- use ExUnit.Case, async: true
-
+ use Pleroma.DataCase, async: true
alias Pleroma.HTTP.AdapterHelper
describe "format_proxy/1" do
]
end
end
+
+ describe "timeout settings" do
+ test "should default to 5000/15000" do
+ options = AdapterHelper.options(%URI{host: 'somewhere'})
+ assert options[:pool_timeout] == 5000
+ assert options[:receive_timeout] == 15_000
+ end
+
+ test "pool_timeout should be overridden by :http, :pool_timeout" do
+ clear_config([:http, :pool_timeout], 10_000)
+ options = AdapterHelper.options(%URI{host: 'somewhere'})
+ assert options[:pool_timeout] == 10_000
+ end
+
+ test "receive_timeout should be overridden by :http, :receive_timeout" do
+ clear_config([:http, :receive_timeout], 20_000)
+ options = AdapterHelper.options(%URI{host: 'somewhere'})
+ assert options[:receive_timeout] == 20_000
+ end
+ end
end
use Pleroma.DataCase
alias Pleroma.Activity
+ alias Pleroma.Instances
alias Pleroma.Object
alias Pleroma.Object.Fetcher
"https://patch.cx/media/03ca3c8b4ac3ddd08bf0f84be7885f2f88de0f709112131a22d83650819e36c2.json"
)
end
+
+ test "it resets instance reachability on successful fetch" do
+ id = "http://mastodon.example.org/@admin/99541947525187367"
+ Instances.set_consistently_unreachable(id)
+ refute Instances.reachable?(id)
+
+ {:ok, _object} =
+ Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367")
+
+ assert Instances.reachable?(id)
+ end
end
describe "implementation quirks" do
assert user.last_refreshed_at == orig_user.last_refreshed_at
end
+
+ test "it doesn't fail on invalid alsoKnownAs entries" do
+ Tesla.Mock.mock(fn
+ %{url: "https://mbp.example.com/"} ->
+ %Tesla.Env{
+ status: 200,
+ body:
+ "test/fixtures/microblogpub/user_with_invalid_also_known_as.json"
+ |> File.read!(),
+ headers: [{"content-type", "application/activity+json"}]
+ }
+
+ _ ->
+ %Tesla.Env{status: 404}
+ end)
+
+ assert {:ok, %User{also_known_as: []}} =
+ User.get_or_fetch_by_ap_id("https://mbp.example.com/")
+ end
end
test "returns an ap_id for a user" do
|> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: true})
|> json_response_and_validate_schema(200)
+ assert %{"showing_reblogs" => true} =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: "1"})
+ |> json_response_and_validate_schema(200)
+
assert [%{"id" => ^reblog_id}] =
conn
|> get("/api/v1/timelines/home")
|> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: false})
|> json_response_and_validate_schema(200)
+ assert %{"showing_reblogs" => false} =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: "0"})
+ |> json_response_and_validate_schema(200)
+
assert [] ==
conn
|> get("/api/v1/timelines/home")
%{conn: conn} = oauth_access(["follow"])
followed = insert(:user)
- ret_conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: true})
-
- assert %{"id" => _id, "subscribing" => true} =
- json_response_and_validate_schema(ret_conn, 200)
+ assert %{"subscribing" => true} =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: true})
+ |> json_response_and_validate_schema(200)
- ret_conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: false})
+ assert %{"subscribing" => true} =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: "1"})
+ |> json_response_and_validate_schema(200)
- assert %{"id" => _id, "subscribing" => false} =
- json_response_and_validate_schema(ret_conn, 200)
+ assert %{"subscribing" => false} =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: false})
+ |> json_response_and_validate_schema(200)
end
test "following / unfollowing errors", %{user: user, conn: conn} do
refute Conn.get_resp_header(conn, "x-permitted-cross-domain-policies") == []
refute Conn.get_resp_header(conn, "x-frame-options") == []
refute Conn.get_resp_header(conn, "x-content-type-options") == []
- refute Conn.get_resp_header(conn, "x-download-options") == []
refute Conn.get_resp_header(conn, "referrer-policy") == []
refute Conn.get_resp_header(conn, "content-security-policy") == []
end
conn = get(conn, "/api/v1/instance")
refute Conn.get_resp_header(conn, "strict-transport-security") == []
- refute Conn.get_resp_header(conn, "expect-ct") == []
end
test "it does not send STS headers when disabled", %{conn: conn} do
conn = get(conn, "/api/v1/instance")
assert Conn.get_resp_header(conn, "strict-transport-security") == []
- assert Conn.get_resp_header(conn, "expect-ct") == []
end
test "referrer-policy header reflects configured value", %{conn: conn} do
assert Conn.get_resp_header(conn, "x-permitted-cross-domain-policies") == []
assert Conn.get_resp_header(conn, "x-frame-options") == []
assert Conn.get_resp_header(conn, "x-content-type-options") == []
- assert Conn.get_resp_header(conn, "x-download-options") == []
assert Conn.get_resp_header(conn, "referrer-policy") == []
assert Conn.get_resp_header(conn, "content-security-policy") == []
end
--- /dev/null
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Workers.PublisherWorkerTest do
+ use Pleroma.DataCase, async: true
+ use Oban.Testing, repo: Pleroma.Repo
+
+ import Pleroma.Factory
+
+ alias Pleroma.Object
+ alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.ActivityPub.Builder
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.Federator
+
+ describe "Oban job priority:" do
+ setup do
+ user = insert(:user)
+
+ {:ok, post} = CommonAPI.post(user, %{status: "Regrettable post"})
+ object = Object.normalize(post, fetch: false)
+ {:ok, delete_data, _meta} = Builder.delete(user, object.data["id"])
+ {:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true)
+
+ %{
+ post: post,
+ delete: delete
+ }
+ end
+
+ test "Deletions are lower priority", %{delete: delete} do
+ assert {:ok, %Oban.Job{priority: 3}} = Federator.publish(delete)
+ end
+
+ test "Creates are normal priority", %{post: post} do
+ assert {:ok, %Oban.Job{priority: 0}} = Federator.publish(post)
+ end
+ end
+
+ describe "Oban job timeout" do
+ test "should have a timeout" do
+ clear_config([:workers, :timeout, :federator_outgoing], :timer.minutes(2))
+ assert Pleroma.Workers.PublisherWorker.timeout(nil) == :timer.minutes(2)
+ end
+
+ test "should use a default timeout if none specified" do
+ clear_config([:workers, :timeout, :federator_outgoing])
+ assert Pleroma.Workers.PublisherWorker.timeout(nil) == :timer.seconds(10)
+ end
+ end
+end
assert {:error, :activity_not_found} =
perform_job(Pleroma.Workers.PurgeExpiredActivity, %{activity_id: "some_if"})
end
+
+ test "has a timeout" do
+ clear_config([:workers, :timeout, :activity_expiration], 50)
+ assert Pleroma.Workers.PurgeExpiredActivity.timeout(%Oban.Job{}) == 50
+ end
end
ScheduledActivityWorker.perform(%Oban.Job{args: %{"activity_id" => 42}})
end) =~ "Couldn't find scheduled activity: 42"
end
+
+ test "has a timeout" do
+ clear_config([:workers, :timeout, :scheduled_activities], :timer.minutes(5))
+ assert ScheduledActivityWorker.timeout(nil) == :timer.minutes(5)
+ end
end