forked from AkkomaGang/akkoma
Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
This commit is contained in:
commit
df500529e2
65 changed files with 1020 additions and 444 deletions
|
@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
|
||||
## [unreleased]
|
||||
### Added
|
||||
- Optional SSH access mode. (Needs `erlang-ssh` package on some distributions).
|
||||
- [MongooseIM](https://github.com/esl/MongooseIM) http authentication support.
|
||||
- LDAP authentication
|
||||
- External OAuth provider authentication
|
||||
- A [job queue](https://git.pleroma.social/pleroma/pleroma_job_queue) for federation, emails, web push, etc.
|
||||
|
@ -39,6 +41,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Metadata: RelMe provider
|
||||
- OAuth: added support for refresh tokens
|
||||
- Emoji packs and emoji pack manager
|
||||
- Object pruning (`mix pleroma.database prune_objects`)
|
||||
- OAuth: added job to clean expired access tokens
|
||||
- MRF: Support for rejecting reports from specific instances (`mrf_simple`)
|
||||
- MRF: Support for stripping avatars and banner images from specific instances (`mrf_simple`)
|
||||
|
||||
### Changed
|
||||
- **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer
|
||||
|
@ -73,6 +79,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Don't ship finmoji by default, they can be installed as an emoji pack
|
||||
- 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
|
||||
- Added an FTS index on objects. Running `vacuum analyze` and setting a larger `work_mem` is recommended.
|
||||
|
@ -105,6 +113,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Mastodon API: Exposing default scope of the user to anyone
|
||||
- Mastodon API: Make `irreversible` field default to `false` [`POST /api/v1/filters`]
|
||||
- User-Agent is now sent correctly for all HTTP requests.
|
||||
- MRF: Simple policy now properly delists imported or relayed statuses
|
||||
|
||||
## Removed
|
||||
- Configuration: `config :pleroma, :fe` in favor of the more flexible `config :pleroma, :frontend_configurations`
|
||||
|
|
|
@ -184,9 +184,6 @@
|
|||
"application/ld+json" => ["activity+json"]
|
||||
}
|
||||
|
||||
config :pleroma, :websub, Pleroma.Web.Websub
|
||||
config :pleroma, :ostatus, Pleroma.Web.OStatus
|
||||
config :pleroma, :httpoison, Pleroma.HTTP
|
||||
config :tesla, adapter: Tesla.Adapter.Hackney
|
||||
|
||||
# Configures http settings, upstream proxy etc.
|
||||
|
@ -239,7 +236,8 @@
|
|||
welcome_message: nil,
|
||||
max_report_comment_size: 1000,
|
||||
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
|
||||
|
||||
|
@ -313,7 +311,9 @@
|
|||
federated_timeline_removal: [],
|
||||
report_removal: [],
|
||||
reject: [],
|
||||
accept: []
|
||||
accept: [],
|
||||
avatar_removal: [],
|
||||
banner_removal: []
|
||||
|
||||
config :pleroma, :mrf_keyword,
|
||||
reject: [],
|
||||
|
@ -384,6 +384,7 @@
|
|||
"activities",
|
||||
"api",
|
||||
"auth",
|
||||
"check_password",
|
||||
"dev",
|
||||
"friend-requests",
|
||||
"inbox",
|
||||
|
@ -404,6 +405,7 @@
|
|||
"status",
|
||||
"tag",
|
||||
"user-search",
|
||||
"user_exists",
|
||||
"users",
|
||||
"web"
|
||||
]
|
||||
|
@ -478,7 +480,9 @@
|
|||
|
||||
config :pleroma, :oauth2,
|
||||
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
|
||||
|
||||
|
|
|
@ -39,8 +39,6 @@
|
|||
# Reduce hash rounds for testing
|
||||
config :pbkdf2_elixir, rounds: 1
|
||||
|
||||
config :pleroma, :websub, Pleroma.Web.WebsubMock
|
||||
config :pleroma, :ostatus, Pleroma.Web.OStatusMock
|
||||
config :tesla, adapter: Tesla.Mock
|
||||
config :pleroma, :rich_media, enabled: false
|
||||
|
||||
|
|
|
@ -104,6 +104,7 @@ config :pleroma, Pleroma.Emails.Mailer,
|
|||
* `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`)
|
||||
* `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
|
||||
REST API for creating an account settings
|
||||
|
@ -219,6 +220,9 @@ relates to mascots on the mastodon frontend
|
|||
* `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
|
||||
* `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
|
||||
* `allow_followersonly`: whether to allow followers-only posts
|
||||
|
@ -477,7 +481,7 @@ config :esshd,
|
|||
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
|
||||
|
||||
|
@ -549,6 +553,8 @@ Configure OAuth 2 provider capabilities:
|
|||
|
||||
* `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.
|
||||
* `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
|
||||
* `shortcode_globs`: Location of custom emoji files. `*` can be used as a wildcard. Example `["/emoji/custom/**/*.png"]`
|
||||
|
|
10
docs/config/howto_mongooseim.md
Normal file
10
docs/config/howto_mongooseim.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Configuring MongooseIM (XMPP Server) to use Pleroma for authentication
|
||||
|
||||
If you want to give your Pleroma users an XMPP (chat) account, you can configure [MongooseIM](https://github.com/esl/MongooseIM) to use your Pleroma server for user authentication, automatically giving every local user an XMPP account.
|
||||
|
||||
In general, you just have to follow the configuration described at [https://mongooseim.readthedocs.io/en/latest/authentication-backends/HTTP-authentication-module/](https://mongooseim.readthedocs.io/en/latest/authentication-backends/HTTP-authentication-module/) and do these changes to your mongooseim.cfg.
|
||||
|
||||
1. Set the auth_method to `{auth_method, http}`.
|
||||
2. Add the http auth pool like this: `{http, global, auth, [{workers, 50}], [{server, "https://yourpleromainstance.com"}]}`
|
||||
|
||||
Restart your MongooseIM server, your users should now be able to connect with their Pleroma credentials.
|
|
@ -10,7 +10,9 @@ example.tld {
|
|||
|
||||
gzip
|
||||
|
||||
proxy / localhost:4000 {
|
||||
# this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
|
||||
# and `localhost.` resolves to [::0] on some systems: see issue #930
|
||||
proxy / 127.0.0.1:4000 {
|
||||
websocket
|
||||
transparent
|
||||
}
|
||||
|
|
|
@ -58,8 +58,10 @@ CustomLog ${APACHE_LOG_DIR}/access.log combined
|
|||
RewriteRule /(.*) ws://localhost:4000/$1 [P,L]
|
||||
|
||||
ProxyRequests off
|
||||
ProxyPass / http://localhost:4000/
|
||||
ProxyPassReverse / http://localhost:4000/
|
||||
# this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
|
||||
# and `localhost.` resolves to [::0] on some systems: see issue #930
|
||||
ProxyPass / http://127.0.0.1:4000/
|
||||
ProxyPassReverse / http://127.0.0.1:4000/
|
||||
|
||||
RequestHeader set Host ${servername}
|
||||
ProxyPreserveHost On
|
||||
|
|
|
@ -69,7 +69,9 @@ server {
|
|||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $http_host;
|
||||
|
||||
proxy_pass http://localhost:4000;
|
||||
# this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
|
||||
# and `localhost.` resolves to [::0] on some systems: see issue #930
|
||||
proxy_pass http://127.0.0.1:4000;
|
||||
|
||||
client_max_body_size 16m;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
vcl 4.0;
|
||||
vcl 4.1;
|
||||
import std;
|
||||
|
||||
backend default {
|
||||
|
@ -35,24 +35,6 @@ sub vcl_recv {
|
|||
}
|
||||
return(purge);
|
||||
}
|
||||
|
||||
# Pleroma MediaProxy - strip headers that will affect caching
|
||||
if (req.url ~ "^/proxy/") {
|
||||
unset req.http.Cookie;
|
||||
unset req.http.Authorization;
|
||||
unset req.http.Accept;
|
||||
return (hash);
|
||||
}
|
||||
|
||||
# Strip headers that will affect caching from all other static content
|
||||
# This also permits caching of individual toots and AP Activities
|
||||
if ((req.url ~ "^/(media|static)/") ||
|
||||
(req.url ~ "(?i)\.(html|js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|mp4|ogg|webm|svg|swf|ttf|pdf|woff|woff2)$"))
|
||||
{
|
||||
unset req.http.Cookie;
|
||||
unset req.http.Authorization;
|
||||
return (hash);
|
||||
}
|
||||
}
|
||||
|
||||
sub vcl_backend_response {
|
||||
|
@ -61,6 +43,12 @@ sub vcl_backend_response {
|
|||
set beresp.do_gzip = true;
|
||||
}
|
||||
|
||||
# Retry broken backend responses.
|
||||
if (beresp.status == 503) {
|
||||
set bereq.http.X-Varnish-Backend-503 = "1";
|
||||
return (retry);
|
||||
}
|
||||
|
||||
# CHUNKED SUPPORT
|
||||
if (bereq.http.x-range ~ "bytes=" && beresp.status == 206) {
|
||||
set beresp.ttl = 10m;
|
||||
|
@ -73,8 +61,6 @@ sub vcl_backend_response {
|
|||
return (deliver);
|
||||
}
|
||||
|
||||
# Default object caching of 86400s;
|
||||
set beresp.ttl = 86400s;
|
||||
# Allow serving cached content for 6h in case backend goes down
|
||||
set beresp.grace = 6h;
|
||||
|
||||
|
@ -90,20 +76,6 @@ sub vcl_backend_response {
|
|||
set beresp.ttl = 30s;
|
||||
return (deliver);
|
||||
}
|
||||
|
||||
# Pleroma MediaProxy internally sets headers properly
|
||||
if (bereq.url ~ "^/proxy/") {
|
||||
return (deliver);
|
||||
}
|
||||
|
||||
# Strip cache-restricting headers from Pleroma on static content that we want to cache
|
||||
if (bereq.url ~ "(?i)\.(js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|mp4|ogg|webm|svg|swf|ttf|pdf|woff|woff2)$")
|
||||
{
|
||||
unset beresp.http.set-cookie;
|
||||
unset beresp.http.Cache-Control;
|
||||
unset beresp.http.x-request-id;
|
||||
set beresp.http.Cache-Control = "public, max-age=86400";
|
||||
}
|
||||
}
|
||||
|
||||
# The synthetic response for 301 redirects
|
||||
|
@ -132,10 +104,32 @@ sub vcl_hash {
|
|||
}
|
||||
|
||||
sub vcl_backend_fetch {
|
||||
# Be more lenient for slow servers on the fediverse
|
||||
if bereq.url ~ "^/proxy/" {
|
||||
set bereq.first_byte_timeout = 300s;
|
||||
}
|
||||
|
||||
# CHUNKED SUPPORT
|
||||
if (bereq.http.x-range) {
|
||||
set bereq.http.Range = bereq.http.x-range;
|
||||
}
|
||||
|
||||
if (bereq.retries == 0) {
|
||||
# Clean up the X-Varnish-Backend-503 flag that is used internally
|
||||
# to mark broken backend responses that should be retried.
|
||||
unset bereq.http.X-Varnish-Backend-503;
|
||||
} else {
|
||||
if (bereq.http.X-Varnish-Backend-503) {
|
||||
if (bereq.method != "POST" &&
|
||||
std.healthy(bereq.backend) &&
|
||||
bereq.retries <= 4) {
|
||||
# Flush broken backend response flag & try again.
|
||||
unset bereq.http.X-Varnish-Backend-503;
|
||||
} else {
|
||||
return (abandon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub vcl_deliver {
|
||||
|
@ -145,3 +139,9 @@ sub vcl_deliver {
|
|||
unset resp.http.CR;
|
||||
}
|
||||
}
|
||||
|
||||
sub vcl_backend_error {
|
||||
# Retry broken backend responses.
|
||||
set bereq.http.X-Varnish-Backend-503 = "1";
|
||||
return (retry);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Mix.Tasks.Pleroma.Database do
|
||||
alias Mix.Tasks.Pleroma.Common
|
||||
alias Pleroma.Conversation
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
require Logger
|
||||
|
@ -23,6 +24,10 @@ defmodule Mix.Tasks.Pleroma.Database do
|
|||
Options:
|
||||
- `--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.
|
||||
|
||||
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.update_follower_count/1)
|
||||
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
|
||||
|
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.Activity do
|
|||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.ThreadMute
|
||||
alias Pleroma.User
|
||||
|
||||
import Ecto.Changeset
|
||||
|
@ -37,6 +38,7 @@ defmodule Pleroma.Activity do
|
|||
field(:local, :boolean, default: true)
|
||||
field(:actor, :string)
|
||||
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
|
||||
has_one(:bookmark, Bookmark)
|
||||
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_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
|
||||
Repo.one(
|
||||
from(
|
||||
|
|
|
@ -110,6 +110,7 @@ def start(_type, _args) do
|
|||
hackney_pool_children() ++
|
||||
[
|
||||
worker(Pleroma.Web.Federator.RetryQueue, []),
|
||||
worker(Pleroma.Web.OAuth.Token.CleanWorker, []),
|
||||
worker(Pleroma.Stats, []),
|
||||
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)
|
||||
|
@ -131,6 +132,7 @@ def start(_type, _args) do
|
|||
defp setup_instrumenters do
|
||||
require Prometheus.Registry
|
||||
|
||||
if Application.get_env(:prometheus, Pleroma.Repo.Instrumenter) do
|
||||
:ok =
|
||||
:telemetry.attach(
|
||||
"prometheus-ecto",
|
||||
|
@ -139,11 +141,13 @@ defp setup_instrumenters do
|
|||
%{}
|
||||
)
|
||||
|
||||
Pleroma.Repo.Instrumenter.setup()
|
||||
end
|
||||
|
||||
Prometheus.Registry.register_collector(:prometheus_process_collector)
|
||||
Pleroma.Web.Endpoint.MetricsExporter.setup()
|
||||
Pleroma.Web.Endpoint.PipelineInstrumenter.setup()
|
||||
Pleroma.Web.Endpoint.Instrumenter.setup()
|
||||
Pleroma.Repo.Instrumenter.setup()
|
||||
end
|
||||
|
||||
def enabled_hackney_pools do
|
||||
|
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Formatter do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
@safe_mention_regex ~r/^(\s*(?<mentions>@.+?\s+)+)(?<rest>.*)/
|
||||
@safe_mention_regex ~r/^(\s*(?<mentions>(@.+?\s+){1,})+)(?<rest>.*)/s
|
||||
@link_regex ~r"((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+"ui
|
||||
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.HTTP.Connection do
|
|||
"""
|
||||
|
||||
@hackney_options [
|
||||
connect_timeout: 2_000,
|
||||
connect_timeout: 10_000,
|
||||
recv_timeout: 20_000,
|
||||
follow_redirect: true,
|
||||
pool: :federation
|
||||
|
@ -32,9 +32,11 @@ def new(opts \\ []) do
|
|||
defp hackney_options(opts) do
|
||||
options = Keyword.get(opts, :adapter, [])
|
||||
adapter_options = Pleroma.Config.get([:http, :adapter], [])
|
||||
proxy_url = Pleroma.Config.get([:http, :proxy_url], nil)
|
||||
|
||||
@hackney_options
|
||||
|> Keyword.merge(adapter_options)
|
||||
|> Keyword.merge(options)
|
||||
|> Keyword.merge(proxy: proxy_url)
|
||||
end
|
||||
end
|
||||
|
|
44
lib/pleroma/keys.ex
Normal file
44
lib/pleroma/keys.ex
Normal file
|
@ -0,0 +1,44 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Keys do
|
||||
# Native generation of RSA keys is only available since OTP 20+ and in default build conditions
|
||||
# We try at compile time to generate natively an RSA key otherwise we fallback on the old way.
|
||||
try do
|
||||
_ = :public_key.generate_key({:rsa, 2048, 65_537})
|
||||
|
||||
def generate_rsa_pem do
|
||||
key = :public_key.generate_key({:rsa, 2048, 65_537})
|
||||
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
|
||||
pem = :public_key.pem_encode([entry]) |> String.trim_trailing()
|
||||
{:ok, pem}
|
||||
end
|
||||
rescue
|
||||
_ ->
|
||||
def generate_rsa_pem do
|
||||
port = Port.open({:spawn, "openssl genrsa"}, [:binary])
|
||||
|
||||
{:ok, pem} =
|
||||
receive do
|
||||
{^port, {:data, pem}} -> {:ok, pem}
|
||||
end
|
||||
|
||||
Port.close(port)
|
||||
|
||||
if Regex.match?(~r/RSA PRIVATE KEY/, pem) do
|
||||
{:ok, pem}
|
||||
else
|
||||
:error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def keys_from_pem(pem) do
|
||||
[private_key_code] = :public_key.pem_decode(pem)
|
||||
private_key = :public_key.pem_entry_decode(private_key_code)
|
||||
{:RSAPrivateKey, _, modulus, exponent, _, _, _, _, _, _, _} = private_key
|
||||
public_key = {:RSAPublicKey, modulus, exponent}
|
||||
{:ok, private_key, public_key}
|
||||
end
|
||||
end
|
|
@ -130,6 +130,13 @@ def delete(%Object{data: %{"id" => id}} = object) do
|
|||
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
|
||||
Cachex.put(:object_cache, "object:#{ap_id}", object)
|
||||
{:ok, object}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
defmodule Pleroma.Object.Fetcher do
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Object.Containment
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
|
@ -6,7 +7,18 @@ defmodule Pleroma.Object.Fetcher do
|
|||
|
||||
require Logger
|
||||
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
defp reinject_object(data) do
|
||||
Logger.debug("Reinjecting object #{data["id"]}")
|
||||
|
||||
with data <- Transmogrifier.fix_object(data),
|
||||
{:ok, object} <- Object.create(data) do
|
||||
{:ok, object}
|
||||
else
|
||||
e ->
|
||||
Logger.error("Error while processing object: #{inspect(e)}")
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
# TODO:
|
||||
# This will create a Create activity, which we need internally at the moment.
|
||||
|
@ -26,12 +38,17 @@ def fetch_object_from_id(id) do
|
|||
"object" => data
|
||||
},
|
||||
:ok <- Containment.contain_origin(id, params),
|
||||
{:ok, activity} <- Transmogrifier.handle_incoming(params) do
|
||||
{:ok, Object.normalize(activity, false)}
|
||||
{:ok, activity} <- Transmogrifier.handle_incoming(params),
|
||||
{:object, _data, %Object{} = object} <-
|
||||
{:object, data, Object.normalize(activity, false)} do
|
||||
{:ok, object}
|
||||
else
|
||||
{:error, {:reject, nil}} ->
|
||||
{:reject, nil}
|
||||
|
||||
{:object, data, nil} ->
|
||||
reinject_object(data)
|
||||
|
||||
object = %Object{} ->
|
||||
{:ok, object}
|
||||
|
||||
|
@ -60,7 +77,7 @@ def fetch_and_contain_remote_object_from_id(id) do
|
|||
|
||||
with true <- String.starts_with?(id, "http"),
|
||||
{:ok, %{body: body, status: code}} when code in 200..299 <-
|
||||
@httpoison.get(
|
||||
HTTP.get(
|
||||
id,
|
||||
[{:Accept, "application/activity+json"}]
|
||||
),
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ReverseProxy do
|
||||
alias Pleroma.HTTP
|
||||
|
||||
@keep_req_headers ~w(accept user-agent accept-encoding cache-control if-modified-since) ++
|
||||
~w(if-unmodified-since if-none-match if-range range)
|
||||
@resp_cache_headers ~w(etag date last-modified cache-control)
|
||||
|
@ -60,7 +62,6 @@ defmodule Pleroma.ReverseProxy do
|
|||
|
||||
"""
|
||||
@hackney Application.get_env(:pleroma, :hackney, :hackney)
|
||||
@httpoison Application.get_env(:pleroma, :httpoison, HTTPoison)
|
||||
|
||||
@default_hackney_options []
|
||||
|
||||
|
@ -97,7 +98,7 @@ def call(conn = %{method: method}, url, opts) when method in @methods do
|
|||
hackney_opts =
|
||||
@default_hackney_options
|
||||
|> Keyword.merge(Keyword.get(opts, :http, []))
|
||||
|> @httpoison.process_request_options()
|
||||
|> HTTP.process_request_options()
|
||||
|
||||
req_headers = build_req_headers(conn.req_headers, opts)
|
||||
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
defmodule Pleroma.Signature do
|
||||
@behaviour HTTPSignatures.Adapter
|
||||
|
||||
alias Pleroma.Keys
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.Salmon
|
||||
alias Pleroma.Web.WebFinger
|
||||
|
||||
def fetch_public_key(conn) do
|
||||
with actor_id <- Utils.get_ap_id(conn.params["actor"]),
|
||||
|
@ -33,8 +32,8 @@ def refetch_public_key(conn) do
|
|||
end
|
||||
|
||||
def sign(%User{} = user, headers) do
|
||||
with {:ok, %{info: %{keys: keys}}} <- WebFinger.ensure_keys_present(user),
|
||||
{:ok, private_key, _} <- Salmon.keys_from_pem(keys) do
|
||||
with {:ok, %{info: %{keys: keys}}} <- User.ensure_keys_present(user),
|
||||
{:ok, private_key, _} <- Keys.keys_from_pem(keys) do
|
||||
HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,11 +4,10 @@
|
|||
|
||||
defmodule Pleroma.Uploaders.MDII do
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.HTTP
|
||||
|
||||
@behaviour Pleroma.Uploaders.Uploader
|
||||
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
|
||||
# MDII-hosted images are never passed through the MediaPlug; only local media.
|
||||
# Delegate to Pleroma.Uploaders.Local
|
||||
def get_file(file) do
|
||||
|
@ -25,7 +24,7 @@ def put_file(upload) do
|
|||
query = "#{cgi}?#{extension}"
|
||||
|
||||
with {:ok, %{status: 200, body: body}} <-
|
||||
@httpoison.post(query, file_data, [], adapter: [pool: :default]) do
|
||||
HTTP.post(query, file_data, [], adapter: [pool: :default]) do
|
||||
remote_file_name = String.split(body) |> List.first()
|
||||
public_url = "#{files}/#{remote_file_name}.#{extension}"
|
||||
{:ok, {:url, public_url}}
|
||||
|
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.User do
|
|||
|
||||
alias Comeonin.Pbkdf2
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Keys
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Registration
|
||||
|
@ -1422,4 +1423,24 @@ def get_mascot(%{info: %{mascot: mascot}}) when is_nil(mascot) do
|
|||
}
|
||||
}
|
||||
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
|
||||
|
|
|
@ -834,6 +834,13 @@ defp maybe_preload_bookmarks(query, opts) do
|
|||
|> Activity.with_preloaded_bookmark(opts["user"])
|
||||
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
|
||||
query
|
||||
|> order_by(desc: :id)
|
||||
|
@ -852,6 +859,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
|||
base_query
|
||||
|> maybe_preload_objects(opts)
|
||||
|> maybe_preload_bookmarks(opts)
|
||||
|> maybe_set_thread_muted_field(opts)
|
||||
|> maybe_order(opts)
|
||||
|> restrict_recipients(recipients, opts["user"])
|
||||
|> restrict_tag(opts)
|
||||
|
@ -901,7 +909,7 @@ def upload(file, opts \\ []) do
|
|||
end
|
||||
end
|
||||
|
||||
def user_data_from_user_object(data) do
|
||||
defp object_to_user_data(data) do
|
||||
avatar =
|
||||
data["icon"]["url"] &&
|
||||
%{
|
||||
|
@ -948,9 +956,19 @@ def user_data_from_user_object(data) do
|
|||
{:ok, user_data}
|
||||
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
|
||||
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do
|
||||
user_data_from_user_object(data)
|
||||
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
|
||||
{:ok, data} <- user_data_from_user_object(data) do
|
||||
{:ok, data}
|
||||
else
|
||||
e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
|
||||
end
|
||||
|
|
|
@ -39,7 +39,7 @@ def relay_active?(conn, _) do
|
|||
|
||||
def user(conn, %{"nickname" => nickname}) do
|
||||
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
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> 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
|
||||
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)
|
||||
|
||||
conn
|
||||
|
@ -117,7 +117,7 @@ def following(conn, %{"nickname" => nickname, "page" => page}) do
|
|||
|
||||
def following(conn, %{"nickname" => nickname}) do
|
||||
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
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> 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
|
||||
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)
|
||||
|
||||
conn
|
||||
|
@ -137,7 +137,7 @@ def followers(conn, %{"nickname" => nickname, "page" => page}) do
|
|||
|
||||
def followers(conn, %{"nickname" => nickname}) do
|
||||
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
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> json(UserView.render("followers.json", %{user: user}))
|
||||
|
@ -146,7 +146,7 @@ def followers(conn, %{"nickname" => nickname}) do
|
|||
|
||||
def outbox(conn, %{"nickname" => nickname} = params) do
|
||||
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
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> 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
|
||||
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
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> json(UserView.render("user.json", %{user: user}))
|
||||
|
|
|
@ -74,8 +74,7 @@ defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do
|
|||
actor_host
|
||||
),
|
||||
user <- User.get_cached_by_ap_id(object["actor"]),
|
||||
true <- "https://www.w3.org/ns/activitystreams#Public" in object["to"],
|
||||
true <- user.follower_address in object["cc"] do
|
||||
true <- "https://www.w3.org/ns/activitystreams#Public" in object["to"] do
|
||||
to =
|
||||
List.delete(object["to"], "https://www.w3.org/ns/activitystreams#Public") ++
|
||||
[user.follower_address]
|
||||
|
@ -104,9 +103,29 @@ defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"}
|
|||
|
||||
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
|
||||
def filter(object) do
|
||||
actor_info = URI.parse(object["actor"])
|
||||
def filter(%{"actor" => actor} = object) do
|
||||
actor_info = URI.parse(actor)
|
||||
|
||||
with {:ok, object} <- check_accept(actor_info, object),
|
||||
{:ok, object} <- check_reject(actor_info, object),
|
||||
|
@ -119,4 +138,18 @@ def filter(object) do
|
|||
_e -> {:reject, nil}
|
||||
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
|
||||
|
|
|
@ -19,10 +19,12 @@ defp filter_by_list(%{"actor" => actor} = object, allow_list) do
|
|||
end
|
||||
|
||||
@impl true
|
||||
def filter(object) do
|
||||
actor_info = URI.parse(object["actor"])
|
||||
def filter(%{"actor" => actor} = object) do
|
||||
actor_info = URI.parse(actor)
|
||||
allow_list = Config.get([:mrf_user_allowlist, String.to_atom(actor_info.host)], [])
|
||||
|
||||
filter_by_list(object, allow_list)
|
||||
end
|
||||
|
||||
def filter(object), do: {:ok, object}
|
||||
end
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Web.ActivityPub.Publisher do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Relay
|
||||
|
@ -16,8 +17,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
|
||||
require Logger
|
||||
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
|
||||
@moduledoc """
|
||||
ActivityPub outgoing federation module.
|
||||
"""
|
||||
|
@ -63,7 +62,7 @@ def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = pa
|
|||
|
||||
with {:ok, %{status: code}} when code in 200..299 <-
|
||||
result =
|
||||
@httpoison.post(
|
||||
HTTP.post(
|
||||
inbox,
|
||||
json,
|
||||
[
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Web.ActivityPub.UserView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
alias Pleroma.Keys
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
@ -12,8 +13,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.Router.Helpers
|
||||
alias Pleroma.Web.Salmon
|
||||
alias Pleroma.Web.WebFinger
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
|
@ -34,8 +33,8 @@ def render("endpoints.json", _), do: %{}
|
|||
|
||||
# the instance itself is not a Person, but instead an Application
|
||||
def render("user.json", %{user: %{nickname: nil} = user}) do
|
||||
{:ok, user} = WebFinger.ensure_keys_present(user)
|
||||
{:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
|
||||
{:ok, user} = User.ensure_keys_present(user)
|
||||
{: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_encode([public_key])
|
||||
|
||||
|
@ -62,8 +61,8 @@ def render("user.json", %{user: %{nickname: nil} = user}) do
|
|||
end
|
||||
|
||||
def render("user.json", %{user: user}) do
|
||||
{:ok, user} = WebFinger.ensure_keys_present(user)
|
||||
{:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
|
||||
{:ok, user} = User.ensure_keys_present(user)
|
||||
{: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_encode([public_key])
|
||||
|
||||
|
|
|
@ -16,17 +16,32 @@ defmodule Pleroma.Web.Endpoint do
|
|||
|
||||
plug(Pleroma.Plugs.UploadedMedia)
|
||||
|
||||
@static_cache_control "public, no-cache"
|
||||
|
||||
# InstanceStatic needs to be before Plug.Static to be able to override shipped-static files
|
||||
# If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well
|
||||
plug(Pleroma.Plugs.InstanceStatic, at: "/")
|
||||
# Cache-control headers are duplicated in case we turn off etags in the future
|
||||
plug(Pleroma.Plugs.InstanceStatic,
|
||||
at: "/",
|
||||
gzip: true,
|
||||
cache_control_for_etags: @static_cache_control,
|
||||
headers: %{
|
||||
"cache-control" => @static_cache_control
|
||||
}
|
||||
)
|
||||
|
||||
plug(
|
||||
Plug.Static,
|
||||
at: "/",
|
||||
from: :pleroma,
|
||||
only:
|
||||
~w(index.html robots.txt static finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc)
|
||||
~w(index.html robots.txt static finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc),
|
||||
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
|
||||
gzip: true,
|
||||
cache_control_for_etags: @static_cache_control,
|
||||
headers: %{
|
||||
"cache-control" => @static_cache_control
|
||||
}
|
||||
)
|
||||
|
||||
plug(Plug.Static.IndexHtml, at: "/pleroma/admin/")
|
||||
|
|
|
@ -11,14 +11,11 @@ defmodule Pleroma.Web.Federator do
|
|||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
alias Pleroma.Web.Federator.RetryQueue
|
||||
alias Pleroma.Web.WebFinger
|
||||
alias Pleroma.Web.OStatus
|
||||
alias Pleroma.Web.Websub
|
||||
|
||||
require Logger
|
||||
|
||||
@websub Application.get_env(:pleroma, :websub)
|
||||
@ostatus Application.get_env(:pleroma, :ostatus)
|
||||
|
||||
def init do
|
||||
# 1 minute
|
||||
Process.sleep(1000 * 60)
|
||||
|
@ -77,9 +74,8 @@ def perform(:request_subscription, websub) do
|
|||
def perform(:publish, activity) do
|
||||
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
|
||||
{:ok, actor} = WebFinger.ensure_keys_present(actor)
|
||||
|
||||
with %User{} = actor <- User.get_cached_by_ap_id(activity.data["actor"]),
|
||||
{:ok, actor} <- User.ensure_keys_present(actor) do
|
||||
Publisher.publish(actor, activity)
|
||||
end
|
||||
end
|
||||
|
@ -89,12 +85,12 @@ def perform(:verify_websub, websub) do
|
|||
"Running WebSub verification for #{websub.id} (#{websub.topic}, #{websub.callback})"
|
||||
end)
|
||||
|
||||
@websub.verify(websub)
|
||||
Websub.verify(websub)
|
||||
end
|
||||
|
||||
def perform(:incoming_doc, doc) do
|
||||
Logger.info("Got document, trying to parse")
|
||||
@ostatus.handle_incoming(doc)
|
||||
OStatus.handle_incoming(doc)
|
||||
end
|
||||
|
||||
def perform(:incoming_ap_doc, params) do
|
||||
|
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.Filter
|
||||
alias Pleroma.Formatter
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Object.Fetcher
|
||||
|
@ -55,7 +56,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
when action in [:account_register]
|
||||
)
|
||||
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
@local_mastodon_name "Mastodon-Local"
|
||||
|
||||
action_fallback(:errors)
|
||||
|
@ -1084,7 +1084,7 @@ def status_search(user, query) do
|
|||
from([a, o] in Activity.with_preloaded_object(Activity),
|
||||
where: fragment("?->>'type' = 'Create'", a.data),
|
||||
where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients,
|
||||
limit: 20
|
||||
limit: 40
|
||||
)
|
||||
|
||||
q =
|
||||
|
@ -1691,7 +1691,7 @@ def suggestions(%{assigns: %{user: user}} = conn, _) do
|
|||
|> String.replace("{{user}}", user)
|
||||
|
||||
with {:ok, %{status: 200, body: body}} <-
|
||||
@httpoison.get(
|
||||
HTTP.get(
|
||||
url,
|
||||
[],
|
||||
adapter: [
|
||||
|
|
|
@ -112,7 +112,7 @@ defp do_render("account.json", %{user: user} = opts) do
|
|||
fields: fields,
|
||||
bot: bot,
|
||||
source: %{
|
||||
note: "",
|
||||
note: HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
|
||||
sensitive: false,
|
||||
pleroma: %{}
|
||||
},
|
||||
|
|
|
@ -157,6 +157,12 @@ def render("status.json", %{activity: %{data: %{"object" => _object}} = activity
|
|||
|
||||
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"] || []
|
||||
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]),
|
||||
favourited: present?(favorited),
|
||||
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),
|
||||
sensitive: sensitive,
|
||||
spoiler_text: summary_html,
|
||||
|
|
41
lib/pleroma/web/mongooseim/mongoose_im_controller.ex
Normal file
41
lib/pleroma/web/mongooseim/mongoose_im_controller.ex
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MongooseIM.MongooseIMController do
|
||||
use Pleroma.Web, :controller
|
||||
alias Comeonin.Pbkdf2
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
||||
def user_exists(conn, %{"user" => username}) do
|
||||
with %User{} <- Repo.get_by(User, nickname: username, local: true) do
|
||||
conn
|
||||
|> json(true)
|
||||
else
|
||||
_ ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(false)
|
||||
end
|
||||
end
|
||||
|
||||
def check_password(conn, %{"user" => username, "pass" => password}) do
|
||||
with %User{password_hash: password_hash} <-
|
||||
Repo.get_by(User, nickname: username, local: true),
|
||||
true <- Pbkdf2.checkpw(password, password_hash) do
|
||||
conn
|
||||
|> json(true)
|
||||
else
|
||||
false ->
|
||||
conn
|
||||
|> put_status(403)
|
||||
|> json(false)
|
||||
|
||||
_ ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(false)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -12,8 +12,6 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
|
|||
alias Pleroma.Web.ActivityPub.MRF
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
|
||||
plug(Pleroma.Web.FederatingPlug)
|
||||
|
||||
def schemas(conn, _params) do
|
||||
response = %{
|
||||
links: [
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
defmodule Pleroma.Web.OAuth.Token do
|
||||
use Ecto.Schema
|
||||
|
||||
import Ecto.Query
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Pleroma.Repo
|
||||
|
@ -13,6 +12,7 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
alias Pleroma.Web.OAuth.App
|
||||
alias Pleroma.Web.OAuth.Authorization
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.OAuth.Token.Query
|
||||
|
||||
@expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600)
|
||||
@type t :: %__MODULE__{}
|
||||
|
@ -31,17 +31,17 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
@doc "Gets token for app by access token"
|
||||
@spec get_by_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found}
|
||||
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()
|
||||
end
|
||||
|
||||
@doc "Gets token for app by refresh token"
|
||||
@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
|
||||
from(t in __MODULE__,
|
||||
where: t.app_id == ^app_id and t.refresh_token == ^token,
|
||||
preload: [:user]
|
||||
)
|
||||
Query.get_by_app(app_id)
|
||||
|> Query.get_by_refresh_token(token)
|
||||
|> Query.preload([:user])
|
||||
|> Repo.find_resource()
|
||||
end
|
||||
|
||||
|
@ -97,29 +97,25 @@ def create_token(%App{} = app, %User{} = user, attrs \\ %{}) do
|
|||
end
|
||||
|
||||
def delete_user_tokens(%User{id: user_id}) do
|
||||
from(
|
||||
t in Token,
|
||||
where: t.user_id == ^user_id
|
||||
)
|
||||
Query.get_by_user(user_id)
|
||||
|> Repo.delete_all()
|
||||
end
|
||||
|
||||
def delete_user_token(%User{id: user_id}, token_id) do
|
||||
from(
|
||||
t in Token,
|
||||
where: t.user_id == ^user_id,
|
||||
where: t.id == ^token_id
|
||||
)
|
||||
Query.get_by_user(user_id)
|
||||
|> Query.get_by_id(token_id)
|
||||
|> Repo.delete_all()
|
||||
end
|
||||
|
||||
def delete_expired_tokens do
|
||||
Query.get_expired_tokens()
|
||||
|> Repo.delete_all()
|
||||
end
|
||||
|
||||
def get_user_tokens(%User{id: user_id}) do
|
||||
from(
|
||||
t in Token,
|
||||
where: t.user_id == ^user_id
|
||||
)
|
||||
Query.get_by_user(user_id)
|
||||
|> Query.preload([:app])
|
||||
|> Repo.all()
|
||||
|> Repo.preload(:app)
|
||||
end
|
||||
|
||||
def is_expired?(%__MODULE__{valid_until: valid_until}) do
|
||||
|
|
41
lib/pleroma/web/oauth/token/clean_worker.ex
Normal file
41
lib/pleroma/web/oauth/token/clean_worker.ex
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.OAuth.Token.CleanWorker do
|
||||
@moduledoc """
|
||||
The module represents functions to clean an expired oauth tokens.
|
||||
"""
|
||||
|
||||
# 10 seconds
|
||||
@start_interval 10_000
|
||||
@interval Pleroma.Config.get(
|
||||
# 24 hours
|
||||
[:oauth2, :clean_expired_tokens_interval],
|
||||
86_400_000
|
||||
)
|
||||
@queue :background
|
||||
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
||||
def start_link, do: GenServer.start_link(__MODULE__, nil)
|
||||
|
||||
def init(_) do
|
||||
if Pleroma.Config.get([:oauth2, :clean_expired_tokens], false) do
|
||||
Process.send_after(self(), :perform, @start_interval)
|
||||
{:ok, nil}
|
||||
else
|
||||
:ignore
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def handle_info(:perform, state) do
|
||||
Process.send_after(self(), :perform, @interval)
|
||||
PleromaJobQueue.enqueue(@queue, __MODULE__, [:clean])
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
# Job Worker Callbacks
|
||||
def perform(:clean), do: Token.delete_expired_tokens()
|
||||
end
|
55
lib/pleroma/web/oauth/token/query.ex
Normal file
55
lib/pleroma/web/oauth/token/query.ex
Normal file
|
@ -0,0 +1,55 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.OAuth.Token.Query do
|
||||
@moduledoc """
|
||||
Contains queries for OAuth Token.
|
||||
"""
|
||||
|
||||
import Ecto.Query, only: [from: 2]
|
||||
|
||||
@type query :: Ecto.Queryable.t() | Token.t()
|
||||
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
||||
@spec get_by_refresh_token(query, String.t()) :: query
|
||||
def get_by_refresh_token(query \\ Token, refresh_token) do
|
||||
from(q in query, where: q.refresh_token == ^refresh_token)
|
||||
end
|
||||
|
||||
@spec get_by_token(query, String.t()) :: query
|
||||
def get_by_token(query \\ Token, token) do
|
||||
from(q in query, where: q.token == ^token)
|
||||
end
|
||||
|
||||
@spec get_by_app(query, String.t()) :: query
|
||||
def get_by_app(query \\ Token, app_id) do
|
||||
from(q in query, where: q.app_id == ^app_id)
|
||||
end
|
||||
|
||||
@spec get_by_id(query, String.t()) :: query
|
||||
def get_by_id(query \\ Token, id) do
|
||||
from(q in query, where: q.id == ^id)
|
||||
end
|
||||
|
||||
@spec get_expired_tokens(query, DateTime.t() | nil) :: query
|
||||
def get_expired_tokens(query \\ Token, date \\ nil) do
|
||||
expired_date = date || Timex.now()
|
||||
from(q in query, where: fragment("?", q.valid_until) < ^expired_date)
|
||||
end
|
||||
|
||||
@spec get_by_user(query, String.t()) :: query
|
||||
def get_by_user(query \\ Token, user_id) do
|
||||
from(q in query, where: q.user_id == ^user_id)
|
||||
end
|
||||
|
||||
@spec preload(query, any) :: query
|
||||
def preload(query \\ Token, assoc_preload \\ [])
|
||||
|
||||
def preload(query, assoc_preload) when is_list(assoc_preload) do
|
||||
from(q in query, preload: ^assoc_preload)
|
||||
end
|
||||
|
||||
def preload(query, _assoc_preload), do: query
|
||||
end
|
|
@ -3,13 +3,12 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.OStatus do
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
|
||||
import Ecto.Query
|
||||
import Pleroma.Web.XML
|
||||
require Logger
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
@ -363,7 +362,7 @@ def get_atom_url(body) do
|
|||
def fetch_activity_from_atom_url(url) do
|
||||
with true <- String.starts_with?(url, "http"),
|
||||
{:ok, %{body: body, status: code}} when code in 200..299 <-
|
||||
@httpoison.get(
|
||||
HTTP.get(
|
||||
url,
|
||||
[{:Accept, "application/atom+xml"}]
|
||||
) do
|
||||
|
@ -380,7 +379,7 @@ def fetch_activity_from_html_url(url) do
|
|||
Logger.debug("Trying to fetch #{url}")
|
||||
|
||||
with true <- String.starts_with?(url, "http"),
|
||||
{:ok, %{body: body}} <- @httpoison.get(url, []),
|
||||
{:ok, %{body: body}} <- HTTP.get(url, []),
|
||||
{:ok, atom_url} <- get_atom_url(body) do
|
||||
fetch_activity_from_atom_url(atom_url)
|
||||
else
|
||||
|
|
|
@ -707,9 +707,15 @@ defmodule Pleroma.Web.Router do
|
|||
end
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web.MongooseIM do
|
||||
get("/user_exists", MongooseIMController, :user_exists)
|
||||
get("/check_password", MongooseIMController, :check_password)
|
||||
end
|
||||
|
||||
scope "/", Fallback do
|
||||
get("/registration/:token", RedirectController, :registration_page)
|
||||
get("/:maybe_nickname_or_id", RedirectController, :redirector_with_meta)
|
||||
get("/api*path", RedirectController, :api_not_implemented)
|
||||
get("/*path", RedirectController, :redirector)
|
||||
|
||||
options("/*path", RedirectController, :empty)
|
||||
|
@ -721,6 +727,12 @@ defmodule Fallback.RedirectController do
|
|||
alias Pleroma.User
|
||||
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
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
defmodule Pleroma.Web.Salmon do
|
||||
@behaviour Pleroma.Web.Federator.Publisher
|
||||
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
|
||||
use Bitwise
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Keys
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
|
@ -89,45 +89,6 @@ def encode_key({:RSAPublicKey, modulus, exponent}) do
|
|||
"RSA.#{modulus_enc}.#{exponent_enc}"
|
||||
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
|
||||
type = "application/atom+xml"
|
||||
encoding = "base64url"
|
||||
|
@ -176,7 +137,7 @@ def publish_one(%{recipient: %{info: %{salmon: salmon}}} = params),
|
|||
|
||||
def publish_one(%{recipient: url, feed: feed} = params) when is_binary(url) do
|
||||
with {:ok, %{status: code}} when code in 200..299 <-
|
||||
@httpoison.post(
|
||||
HTTP.post(
|
||||
url,
|
||||
feed,
|
||||
[{"Content-Type", "application/magic-envelope+xml"}]
|
||||
|
@ -227,7 +188,7 @@ def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity
|
|||
|> :xmerl.export_simple(:xmerl_xml)
|
||||
|> to_string
|
||||
|
||||
{:ok, private, _} = keys_from_pem(keys)
|
||||
{:ok, private, _} = Keys.keys_from_pem(keys)
|
||||
{:ok, feed} = encode(private, feed)
|
||||
|
||||
remote_users = remote_users(activity)
|
||||
|
@ -253,7 +214,7 @@ def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity
|
|||
def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end)
|
||||
|
||||
def 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)
|
||||
|
||||
[
|
||||
|
|
|
@ -284,6 +284,12 @@ def render(
|
|||
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,
|
||||
"uri" => object.data["id"],
|
||||
|
@ -314,7 +320,7 @@ def render(
|
|||
"summary" => summary,
|
||||
"summary_html" => summary |> Formatter.emojify(object.data["emoji"]),
|
||||
"card" => card,
|
||||
"muted" => CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user)
|
||||
"muted" => thread_muted? || User.mutes?(opts[:for], user)
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -3,12 +3,10 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.WebFinger do
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
alias Pleroma.Web.Salmon
|
||||
alias Pleroma.Web.XML
|
||||
alias Pleroma.XmlBuilder
|
||||
require Jason
|
||||
|
@ -61,7 +59,7 @@ defp gather_links(%User{} = user) do
|
|||
end
|
||||
|
||||
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()}",
|
||||
|
@ -71,7 +69,7 @@ def represent_user(user, "JSON") do
|
|||
end
|
||||
|
||||
def represent_user(user, "XML") do
|
||||
{:ok, user} = ensure_keys_present(user)
|
||||
{:ok, user} = User.ensure_keys_present(user)
|
||||
|
||||
links =
|
||||
gather_links(user)
|
||||
|
@ -88,27 +86,6 @@ def represent_user(user, "XML") do
|
|||
|> XmlBuilder.to_doc()
|
||||
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
|
||||
"data:application/magic-public-key," <> magic_key = magic_key
|
||||
{:ok, magic_key}
|
||||
|
@ -198,11 +175,11 @@ def get_template_from_xml(body) do
|
|||
|
||||
def find_lrdd_template(domain) do
|
||||
with {:ok, %{status: status, body: body}} when status in 200..299 <-
|
||||
@httpoison.get("http://#{domain}/.well-known/host-meta", []) do
|
||||
HTTP.get("http://#{domain}/.well-known/host-meta", []) do
|
||||
get_template_from_xml(body)
|
||||
else
|
||||
_ ->
|
||||
with {:ok, %{body: body}} <- @httpoison.get("https://#{domain}/.well-known/host-meta", []) do
|
||||
with {:ok, %{body: body}} <- HTTP.get("https://#{domain}/.well-known/host-meta", []) do
|
||||
get_template_from_xml(body)
|
||||
else
|
||||
e -> {:error, "Can't find LRDD template: #{inspect(e)}"}
|
||||
|
@ -231,7 +208,7 @@ def finger(account) do
|
|||
end
|
||||
|
||||
with response <-
|
||||
@httpoison.get(
|
||||
HTTP.get(
|
||||
address,
|
||||
Accept: "application/xrd+xml,application/jrd+json"
|
||||
),
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Web.Websub do
|
||||
alias Ecto.Changeset
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
@ -24,9 +25,7 @@ defmodule Pleroma.Web.Websub do
|
|||
|
||||
@behaviour Pleroma.Web.Federator.Publisher
|
||||
|
||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||
|
||||
def verify(subscription, getter \\ &@httpoison.get/3) do
|
||||
def verify(subscription, getter \\ &HTTP.get/3) do
|
||||
challenge = Base.encode16(:crypto.strong_rand_bytes(8))
|
||||
lease_seconds = NaiveDateTime.diff(subscription.valid_until, subscription.updated_at)
|
||||
lease_seconds = lease_seconds |> to_string
|
||||
|
@ -207,7 +206,7 @@ def subscribe(subscriber, subscribed, requester \\ &request_subscription/1) do
|
|||
requester.(subscription)
|
||||
end
|
||||
|
||||
def gather_feed_data(topic, getter \\ &@httpoison.get/1) do
|
||||
def gather_feed_data(topic, getter \\ &HTTP.get/1) do
|
||||
with {:ok, response} <- getter.(topic),
|
||||
status when status in 200..299 <- response.status,
|
||||
body <- response.body,
|
||||
|
@ -236,7 +235,7 @@ def gather_feed_data(topic, getter \\ &@httpoison.get/1) do
|
|||
end
|
||||
end
|
||||
|
||||
def request_subscription(websub, poster \\ &@httpoison.post/3, timeout \\ 10_000) do
|
||||
def request_subscription(websub, poster \\ &HTTP.post/3, timeout \\ 10_000) do
|
||||
data = [
|
||||
"hub.mode": "subscribe",
|
||||
"hub.topic": websub.topic,
|
||||
|
@ -294,7 +293,7 @@ def publish_one(%{xml: xml, topic: topic, callback: callback, secret: secret} =
|
|||
Logger.info(fn -> "Pushing #{topic} to #{callback}" end)
|
||||
|
||||
with {:ok, %{status: code}} when code in 200..299 <-
|
||||
@httpoison.post(
|
||||
HTTP.post(
|
||||
callback,
|
||||
xml,
|
||||
[
|
||||
|
|
9
mix.exs
9
mix.exs
|
@ -42,7 +42,7 @@ def project do
|
|||
def application do
|
||||
[
|
||||
mod: {Pleroma.Application, []},
|
||||
extra_applications: [:logger, :runtime_tools, :comeonin, :esshd, :quack],
|
||||
extra_applications: [:logger, :runtime_tools, :comeonin, :quack],
|
||||
included_applications: [:ex_syslogger]
|
||||
]
|
||||
end
|
||||
|
@ -66,10 +66,7 @@ defp deps do
|
|||
{:plug_cowboy, "~> 2.0"},
|
||||
{:phoenix_pubsub, "~> 1.1"},
|
||||
{:phoenix_ecto, "~> 4.0"},
|
||||
{:ecto_sql,
|
||||
git: "https://github.com/elixir-ecto/ecto_sql",
|
||||
ref: "14cb065a74c488d737d973f7a91bc036c6245f78",
|
||||
override: true},
|
||||
{:ecto_sql, "~> 3.1"},
|
||||
{:postgrex, ">= 0.13.5"},
|
||||
{:gettext, "~> 0.15"},
|
||||
{:comeonin, "~> 4.1.1"},
|
||||
|
@ -120,7 +117,7 @@ defp deps do
|
|||
{:recon, github: "ferd/recon", tag: "2.4.0"},
|
||||
{:quack, "~> 0.1.1"},
|
||||
{:benchee, "~> 1.0"},
|
||||
{:esshd, "~> 0.1.0"},
|
||||
{:esshd, "~> 0.1.0", runtime: Application.get_env(:esshd, :enabled, false)},
|
||||
{:ex_rated, "~> 1.2"},
|
||||
{:plug_static_index_html, "~> 1.0.0"},
|
||||
{: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"},
|
||||
"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_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"},
|
||||
"eternal": {:hex, :eternal, "1.2.0", "e2a6b6ce3b8c248f7dc31451aefca57e3bdf0e48d73ae5043229380a67614c41", [:mix], [], "hexpm"},
|
||||
"ex2ms": {:hex, :ex2ms, "1.5.0", "19e27f9212be9a96093fed8cdfbef0a2b56c21237196d26760f11dfcfae58e97", [:mix], [], "hexpm"},
|
||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.ActivityTest do
|
|||
use Pleroma.DataCase
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Bookmark
|
||||
alias Pleroma.ThreadMute
|
||||
import Pleroma.Factory
|
||||
|
||||
test "returns an activity by it's AP id" do
|
||||
|
@ -47,6 +48,31 @@ test "preloading a bookmark" do
|
|||
assert queried_activity.bookmark == bookmark3
|
||||
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
|
||||
test "when association is loaded" do
|
||||
user = insert(:user)
|
||||
|
|
|
@ -184,17 +184,19 @@ test "does not give a replacement for single-character local nicknames who don't
|
|||
|
||||
test "given the 'safe_mention' option, it will only mention people in the beginning" do
|
||||
user = insert(:user)
|
||||
_other_user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
third_user = insert(:user)
|
||||
text = " @#{user.nickname} hey dude i hate @#{third_user.nickname}"
|
||||
text = " @#{user.nickname} @#{other_user.nickname} hey dudes i hate @#{third_user.nickname}"
|
||||
{expected_text, mentions, [] = _tags} = Formatter.linkify(text, safe_mention: true)
|
||||
|
||||
assert mentions == [{"@#{user.nickname}", user}]
|
||||
assert mentions == [{"@#{user.nickname}", user}, {"@#{other_user.nickname}", other_user}]
|
||||
|
||||
assert expected_text ==
|
||||
"<span class='h-card'><a data-user='#{user.id}' class='u-url mention' href='#{
|
||||
user.ap_id
|
||||
}'>@<span>#{user.nickname}</span></a></span> hey dude i hate <span class='h-card'><a data-user='#{
|
||||
}'>@<span>#{user.nickname}</span></a></span> <span class='h-card'><a data-user='#{
|
||||
other_user.id
|
||||
}' class='u-url mention' href='#{other_user.ap_id}'>@<span>#{other_user.nickname}</span></a></span> hey dudes i hate <span class='h-card'><a data-user='#{
|
||||
third_user.id
|
||||
}' class='u-url mention' href='#{third_user.ap_id}'>@<span>#{third_user.nickname}</span></a></span>"
|
||||
end
|
||||
|
@ -206,6 +208,15 @@ test "given the 'safe_mention' option, it will still work without any mention" d
|
|||
assert mentions == []
|
||||
assert expected_text == text
|
||||
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
|
||||
|
||||
describe ".parse_tags" do
|
||||
|
|
20
test/keys_test.exs
Normal file
20
test/keys_test.exs
Normal file
|
@ -0,0 +1,20 @@
|
|||
defmodule Pleroma.KeysTest do
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.Keys
|
||||
|
||||
test "generates an RSA private key pem" do
|
||||
{:ok, key} = Keys.generate_rsa_pem()
|
||||
|
||||
assert is_binary(key)
|
||||
assert Regex.match?(~r/RSA/, key)
|
||||
end
|
||||
|
||||
test "returns a public and private key from a pem" do
|
||||
pem = File.read!("test/fixtures/private_key.pem")
|
||||
{:ok, private, public} = Keys.keys_from_pem(pem)
|
||||
|
||||
assert elem(private, 0) == :RSAPrivateKey
|
||||
assert elem(public, 0) == :RSAPublicKey
|
||||
end
|
||||
end
|
|
@ -87,4 +87,23 @@ test "all objects with fake directions are rejected by the object fetcher" do
|
|||
)
|
||||
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
|
||||
|
|
20
test/plugs/cache_control_test.exs
Normal file
20
test/plugs/cache_control_test.exs
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.CacheControlTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
alias Plug.Conn
|
||||
|
||||
test "Verify Cache-Control header on static assets", %{conn: conn} do
|
||||
conn = get(conn, "/index.html")
|
||||
|
||||
assert Conn.get_resp_header(conn, "cache-control") == ["public, no-cache"]
|
||||
end
|
||||
|
||||
test "Verify Cache-Control header on the API", %{conn: conn} do
|
||||
conn = get(conn, "/api/v1/instance")
|
||||
|
||||
assert Conn.get_resp_header(conn, "cache-control") == ["max-age=0, private, must-revalidate"]
|
||||
end
|
||||
end
|
|
@ -1,11 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.OStatusMock do
|
||||
import Pleroma.Factory
|
||||
|
||||
def handle_incoming(_doc) do
|
||||
insert(:note_activity)
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.WebsubMock do
|
||||
def verify(sub) do
|
||||
{:ok, sub}
|
||||
end
|
||||
end
|
|
@ -902,7 +902,7 @@ test "hide a user's statuses from timelines and notifications" do
|
|||
|
||||
assert [activity] == ActivityPub.fetch_public_activities(%{}) |> Repo.preload(:bookmark)
|
||||
|
||||
assert [activity] ==
|
||||
assert [%{activity | thread_muted?: CommonAPI.thread_muted?(user2, activity)}] ==
|
||||
ActivityPub.fetch_activities([user2.ap_id | user2.following], %{"user" => user2})
|
||||
|
||||
{:ok, _user} = User.deactivate(user)
|
||||
|
@ -1251,4 +1251,19 @@ test "if user is unconfirmed" do
|
|||
refute user.info.confirmation_token
|
||||
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
|
||||
|
|
|
@ -1005,7 +1005,7 @@ test "it filters broken threads" do
|
|||
describe "update" do
|
||||
test "it creates an update activity with the new user data" do
|
||||
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})
|
||||
|
||||
{:ok, update} =
|
||||
|
|
|
@ -17,7 +17,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
federated_timeline_removal: [],
|
||||
report_removal: [],
|
||||
reject: [],
|
||||
accept: []
|
||||
accept: [],
|
||||
avatar_removal: [],
|
||||
banner_removal: []
|
||||
)
|
||||
|
||||
on_exit(fn ->
|
||||
|
@ -143,6 +145,24 @@ test "has a matching host" do
|
|||
|
||||
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||
end
|
||||
|
||||
test "has a matching host but only as:Public in to" do
|
||||
{_actor, ftl_message} = build_ftl_actor_and_message()
|
||||
|
||||
ftl_message_actor_host =
|
||||
ftl_message
|
||||
|> Map.fetch!("actor")
|
||||
|> URI.parse()
|
||||
|> Map.fetch!(:host)
|
||||
|
||||
ftl_message = Map.put(ftl_message, "cc", [])
|
||||
|
||||
Config.put([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host])
|
||||
|
||||
assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message)
|
||||
refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"]
|
||||
assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"]
|
||||
end
|
||||
end
|
||||
|
||||
defp build_ftl_actor_and_message do
|
||||
|
@ -206,6 +226,60 @@ test "has a matching host" do
|
|||
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
|
||||
%{
|
||||
"actor" => "#{Pleroma.Web.base_url()}/users/alice",
|
||||
|
@ -217,4 +291,19 @@ defp build_local_message do
|
|||
defp build_remote_message do
|
||||
%{"actor" => "https://remote.instance/users/bob"}
|
||||
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
|
||||
|
|
|
@ -2,11 +2,12 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
|
|||
use Pleroma.DataCase
|
||||
import Pleroma.Factory
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.UserView
|
||||
|
||||
test "Renders a user, including the public key" do
|
||||
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})
|
||||
|
||||
|
@ -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
|
||||
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})
|
||||
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})
|
||||
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
|
||||
test "local users have a usable endpoints structure" do
|
||||
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})
|
||||
|
||||
|
@ -58,7 +59,7 @@ test "local users have a usable endpoints structure" do
|
|||
|
||||
test "remote users have an empty endpoints structure" do
|
||||
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})
|
||||
|
||||
|
@ -68,7 +69,7 @@ test "remote users have an empty endpoints structure" do
|
|||
|
||||
test "instance users do not expose oAuth endpoints" do
|
||||
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})
|
||||
|
||||
|
|
|
@ -397,14 +397,14 @@ test "it returns 500 if `registrations_open` is enabled", %{conn: conn, user: us
|
|||
end
|
||||
end
|
||||
|
||||
test "/api/pleroma/admin/invite_token" do
|
||||
test "/api/pleroma/admin/users/invite_token" do
|
||||
admin = insert(:user, info: %{is_admin: true})
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, admin)
|
||||
|> put_req_header("accept", "application/json")
|
||||
|> get("/api/pleroma/admin/invite_token")
|
||||
|> get("/api/pleroma/admin/users/invite_token")
|
||||
|
||||
assert conn.status == 200
|
||||
end
|
||||
|
@ -437,10 +437,8 @@ test "renders users array for the first page", %{conn: conn, admin: admin} do
|
|||
user = insert(:user, local: false, tags: ["foo", "bar"])
|
||||
conn = get(conn, "/api/pleroma/admin/users?page=1")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 2,
|
||||
"page_size" => 50,
|
||||
"users" => [
|
||||
users =
|
||||
[
|
||||
%{
|
||||
"deactivated" => admin.info.deactivated,
|
||||
"id" => admin.id,
|
||||
|
@ -458,6 +456,12 @@ test "renders users array for the first page", %{conn: conn, admin: admin} do
|
|||
"tags" => ["foo", "bar"]
|
||||
}
|
||||
]
|
||||
|> Enum.sort_by(& &1["nickname"])
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 2,
|
||||
"page_size" => 50,
|
||||
"users" => users
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -659,10 +663,8 @@ test "only local users with no query", %{admin: old_admin} do
|
|||
|> assign(:user, admin)
|
||||
|> get("/api/pleroma/admin/users?filters=local")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 3,
|
||||
"page_size" => 50,
|
||||
"users" => [
|
||||
users =
|
||||
[
|
||||
%{
|
||||
"deactivated" => user.info.deactivated,
|
||||
"id" => user.id,
|
||||
|
@ -688,6 +690,12 @@ test "only local users with no query", %{admin: old_admin} do
|
|||
"tags" => []
|
||||
}
|
||||
]
|
||||
|> Enum.sort_by(& &1["nickname"])
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 3,
|
||||
"page_size" => 50,
|
||||
"users" => users
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -698,10 +706,8 @@ test "load only admins", %{conn: conn, admin: admin} do
|
|||
|
||||
conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 2,
|
||||
"page_size" => 50,
|
||||
"users" => [
|
||||
users =
|
||||
[
|
||||
%{
|
||||
"deactivated" => false,
|
||||
"id" => admin.id,
|
||||
|
@ -719,6 +725,12 @@ test "load only admins", %{conn: conn, admin: admin} do
|
|||
"tags" => []
|
||||
}
|
||||
]
|
||||
|> Enum.sort_by(& &1["nickname"])
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 2,
|
||||
"page_size" => 50,
|
||||
"users" => users
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -753,10 +765,8 @@ test "load users with tags list", %{conn: conn} do
|
|||
|
||||
conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 2,
|
||||
"page_size" => 50,
|
||||
"users" => [
|
||||
users =
|
||||
[
|
||||
%{
|
||||
"deactivated" => false,
|
||||
"id" => user1.id,
|
||||
|
@ -774,6 +784,12 @@ test "load users with tags list", %{conn: conn} do
|
|||
"tags" => ["second"]
|
||||
}
|
||||
]
|
||||
|> Enum.sort_by(& &1["nickname"])
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"count" => 2,
|
||||
"page_size" => 50,
|
||||
"users" => users
|
||||
}
|
||||
end
|
||||
|
||||
|
|
52
test/web/fallback_test.exs
Normal file
52
test/web/fallback_test.exs
Normal file
|
@ -0,0 +1,52 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.FallbackTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
import Pleroma.Factory
|
||||
|
||||
test "GET /registration/:token", %{conn: conn} do
|
||||
assert conn
|
||||
|> get("/registration/foo")
|
||||
|> html_response(200) =~ "<!--server-generated-meta-->"
|
||||
end
|
||||
|
||||
test "GET /:maybe_nickname_or_id", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
assert conn
|
||||
|> get("/foo")
|
||||
|> html_response(200) =~ "<!--server-generated-meta-->"
|
||||
|
||||
refute conn
|
||||
|> get("/" <> user.nickname)
|
||||
|> html_response(200) =~ "<!--server-generated-meta-->"
|
||||
end
|
||||
|
||||
test "GET /api*path", %{conn: conn} do
|
||||
assert conn
|
||||
|> get("/api/foo")
|
||||
|> json_response(404) == %{"error" => "Not implemented"}
|
||||
end
|
||||
|
||||
test "GET /*path", %{conn: conn} do
|
||||
assert conn
|
||||
|> get("/foo")
|
||||
|> html_response(200) =~ "<!--server-generated-meta-->"
|
||||
|
||||
assert conn
|
||||
|> get("/foo/bar")
|
||||
|> html_response(200) =~ "<!--server-generated-meta-->"
|
||||
end
|
||||
|
||||
test "OPTIONS /*path", %{conn: conn} do
|
||||
assert conn
|
||||
|> options("/foo")
|
||||
|> response(204) == ""
|
||||
|
||||
assert conn
|
||||
|> options("/foo/bar")
|
||||
|> response(204) == ""
|
||||
end
|
||||
end
|
|
@ -55,7 +55,7 @@ test "Represent a user account" do
|
|||
fields: [],
|
||||
bot: false,
|
||||
source: %{
|
||||
note: "",
|
||||
note: "valid html",
|
||||
sensitive: false,
|
||||
pleroma: %{}
|
||||
},
|
||||
|
@ -120,7 +120,7 @@ test "Represent a Service(bot) account" do
|
|||
fields: [],
|
||||
bot: true,
|
||||
source: %{
|
||||
note: "",
|
||||
note: user.bio,
|
||||
sensitive: false,
|
||||
pleroma: %{}
|
||||
},
|
||||
|
@ -209,7 +209,7 @@ test "represent an embedded relationship" do
|
|||
fields: [],
|
||||
bot: true,
|
||||
source: %{
|
||||
note: "",
|
||||
note: user.bio,
|
||||
sensitive: false,
|
||||
pleroma: %{}
|
||||
},
|
||||
|
|
59
test/web/mongooseim/mongoose_im_controller_test.exs
Normal file
59
test/web/mongooseim/mongoose_im_controller_test.exs
Normal file
|
@ -0,0 +1,59 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MongooseIMController do
|
||||
use Pleroma.Web.ConnCase
|
||||
import Pleroma.Factory
|
||||
|
||||
test "/user_exists", %{conn: conn} do
|
||||
_user = insert(:user, nickname: "lain")
|
||||
_remote_user = insert(:user, nickname: "alice", local: false)
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get(mongoose_im_path(conn, :user_exists), user: "lain")
|
||||
|> json_response(200)
|
||||
|
||||
assert res == true
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get(mongoose_im_path(conn, :user_exists), user: "alice")
|
||||
|> json_response(404)
|
||||
|
||||
assert res == false
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get(mongoose_im_path(conn, :user_exists), user: "bob")
|
||||
|> json_response(404)
|
||||
|
||||
assert res == false
|
||||
end
|
||||
|
||||
test "/check_password", %{conn: conn} do
|
||||
user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt("cool"))
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get(mongoose_im_path(conn, :check_password), user: user.nickname, pass: "cool")
|
||||
|> json_response(200)
|
||||
|
||||
assert res == true
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get(mongoose_im_path(conn, :check_password), user: user.nickname, pass: "uncool")
|
||||
|> json_response(403)
|
||||
|
||||
assert res == false
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get(mongoose_im_path(conn, :check_password), user: "nobody", pass: "cool")
|
||||
|> json_response(404)
|
||||
|
||||
assert res == false
|
||||
end
|
||||
end
|
|
@ -7,6 +7,22 @@ defmodule Pleroma.Web.NodeInfoTest do
|
|||
|
||||
import Pleroma.Factory
|
||||
|
||||
test "GET /.well-known/nodeinfo", %{conn: conn} do
|
||||
links =
|
||||
conn
|
||||
|> get("/.well-known/nodeinfo")
|
||||
|> json_response(200)
|
||||
|> Map.fetch!("links")
|
||||
|
||||
Enum.each(links, fn link ->
|
||||
href = Map.fetch!(link, "href")
|
||||
|
||||
conn
|
||||
|> get(href)
|
||||
|> json_response(200)
|
||||
end)
|
||||
end
|
||||
|
||||
test "nodeinfo shows staff accounts", %{conn: conn} do
|
||||
moderator = insert(:user, %{local: true, info: %{is_moderator: true}})
|
||||
admin = insert(:user, %{local: true, info: %{is_admin: true}})
|
||||
|
@ -32,70 +48,6 @@ test "nodeinfo shows restricted nicknames", %{conn: conn} do
|
|||
result["metadata"]["restrictedNicknames"]
|
||||
end
|
||||
|
||||
test "returns 404 when federation is disabled", %{conn: conn} do
|
||||
instance =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|> Keyword.put(:federating, false)
|
||||
|
||||
Application.put_env(:pleroma, :instance, instance)
|
||||
|
||||
conn
|
||||
|> get("/.well-known/nodeinfo")
|
||||
|> json_response(404)
|
||||
|
||||
conn
|
||||
|> get("/nodeinfo/2.1.json")
|
||||
|> json_response(404)
|
||||
|
||||
instance =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|> Keyword.put(:federating, true)
|
||||
|
||||
Application.put_env(:pleroma, :instance, instance)
|
||||
end
|
||||
|
||||
test "returns 200 when federation is enabled", %{conn: conn} do
|
||||
conn
|
||||
|> get("/.well-known/nodeinfo")
|
||||
|> json_response(200)
|
||||
|
||||
conn
|
||||
|> get("/nodeinfo/2.1.json")
|
||||
|> json_response(200)
|
||||
end
|
||||
|
||||
test "returns 404 when federation is disabled (nodeinfo 2.0)", %{conn: conn} do
|
||||
instance =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|> Keyword.put(:federating, false)
|
||||
|
||||
Application.put_env(:pleroma, :instance, instance)
|
||||
|
||||
conn
|
||||
|> get("/.well-known/nodeinfo")
|
||||
|> json_response(404)
|
||||
|
||||
conn
|
||||
|> get("/nodeinfo/2.0.json")
|
||||
|> json_response(404)
|
||||
|
||||
instance =
|
||||
Application.get_env(:pleroma, :instance)
|
||||
|> Keyword.put(:federating, true)
|
||||
|
||||
Application.put_env(:pleroma, :instance, instance)
|
||||
end
|
||||
|
||||
test "returns 200 when federation is enabled (nodeinfo 2.0)", %{conn: conn} do
|
||||
conn
|
||||
|> get("/.well-known/nodeinfo")
|
||||
|> json_response(200)
|
||||
|
||||
conn
|
||||
|> get("/nodeinfo/2.0.json")
|
||||
|> json_response(200)
|
||||
end
|
||||
|
||||
test "returns software.repository field in nodeinfo 2.1", %{conn: conn} do
|
||||
conn
|
||||
|> get("/.well-known/nodeinfo")
|
||||
|
|
|
@ -69,4 +69,17 @@ test "deletes all tokens of a user" do
|
|||
|
||||
assert tokens == 2
|
||||
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
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Web.Salmon.SalmonTest do
|
||||
use Pleroma.DataCase
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Keys
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
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
|
||||
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
|
||||
key = Salmon.decode_key(@magickey)
|
||||
magic_key = Salmon.encode_key(key)
|
||||
|
@ -51,18 +46,10 @@ test "it decodes a friendica public key" do
|
|||
_key = Salmon.decode_key(@magickey_friendica)
|
||||
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
|
||||
doc = File.read!("test/fixtures/incoming_note_activity.xml")
|
||||
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.
|
||||
{: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"]})
|
||||
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)
|
||||
|
||||
|
|
|
@ -105,19 +105,4 @@ test "it gets the xrd endpoint for statusnet" do
|
|||
assert template == "http://status.alpicola.com/main/xrd?uri={uri}"
|
||||
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
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
defmodule Pleroma.Web.Websub.WebsubControllerTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
import Pleroma.Factory
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Web.Websub
|
||||
alias Pleroma.Web.Websub.WebsubClientSubscription
|
||||
|
@ -52,7 +51,7 @@ test "websub subscription confirmation", %{conn: conn} do
|
|||
end
|
||||
|
||||
describe "websub_incoming" do
|
||||
test "handles incoming feed updates", %{conn: conn} do
|
||||
test "accepts incoming feed updates", %{conn: conn} do
|
||||
websub = insert(:websub_client_subscription)
|
||||
doc = "some stuff"
|
||||
signature = Websub.sign(websub.secret, doc)
|
||||
|
@ -64,8 +63,6 @@ test "handles incoming feed updates", %{conn: conn} do
|
|||
|> post("/push/subscriptions/#{websub.id}", doc)
|
||||
|
||||
assert response(conn, 200) == "OK"
|
||||
|
||||
assert length(Repo.all(Activity)) == 1
|
||||
end
|
||||
|
||||
test "rejects incoming feed updates with the wrong signature", %{conn: conn} do
|
||||
|
@ -80,8 +77,6 @@ test "rejects incoming feed updates with the wrong signature", %{conn: conn} do
|
|||
|> post("/push/subscriptions/#{websub.id}", doc)
|
||||
|
||||
assert response(conn, 500) == "Error"
|
||||
|
||||
assert Enum.empty?(Repo.all(Activity))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue