Compare commits

...

151 commits

Author SHA1 Message Date
Henry Jameson
8b249431da Merge remote-tracking branch 'origin/develop' into shigusegubu 2024-10-08 18:01:04 +03:00
marcin mikołajczak
07cfbe4ae8 Merge branch 'profile-image-descriptions' into 'develop'
Federate avatar/header descriptions

See merge request pleroma/pleroma!4264
2024-10-06 20:05:36 +00:00
Haelwenn
d2eb4992e2 Merge branch 'elixir-1.14-docker' into 'develop'
Dockerfile: Elixir 1.14

See merge request pleroma/pleroma!4274
2024-09-21 13:44:17 +00:00
Haelwenn (lanodan) Monnier
a1e3fb506b
Dockerfile: Elixir 1.14 2024-09-21 15:40:04 +02:00
Haelwenn
bc1b4f0be7 Merge branch 'features/mrf-id_filter' into 'develop'
Add id_filter to MRFs

See merge request pleroma/pleroma!3858
2024-09-21 12:27:35 +00:00
Haelwenn (lanodan) Monnier
0fa13c5535
MRF.SimplePolicy: Add id_filter/1 2024-09-21 14:07:26 +02:00
Haelwenn (lanodan) Monnier
30063c5914
MRF.DropPolicy: Add id_filter/1 2024-09-21 14:06:26 +02:00
Haelwenn (lanodan) Monnier
3dd6f65859
Object.Fetcher: Hook to MRF.id_filter 2024-09-21 14:06:26 +02:00
Haelwenn (lanodan) Monnier
03e14e759d
MRF: Add filtering against AP id 2024-09-21 14:06:24 +02:00
feld
196f108823 Merge branch 'oban-bump' into 'develop'
Oban: update to 2.18.3

See merge request pleroma/pleroma!4271
2024-09-19 21:42:49 +00:00
Mark Felder
d6a364dad4 Merge remote-tracking branch 'origin/develop' into oban-bump 2024-09-19 16:23:38 -04:00
feld
7c37fc6a71 Merge branch 'elixir-1.14' into 'develop'
Elixir 1.14

See merge request pleroma/pleroma!4272
2024-09-19 20:23:17 +00:00
Mark Felder
1bd28e7d59 CI script to build and publish an image for Elixir 1.14 2024-09-18 17:28:53 +00:00
Mark Felder
7e303600fb Remove old elixir 1.12 build image generation script 2024-09-18 17:15:55 +00:00
Mark Felder
f00545d85b Elixir 1.14 and Erlang/OTP 23 is now the minimum supported release 2024-09-18 13:14:17 -04:00
feld
6358294943 Merge branch 'ldap-call' into 'develop'
LDAP: fix compile warning

See merge request pleroma/pleroma!4270
2024-09-18 16:20:45 +00:00
Mark Felder
ecd1b8393b Oban: update to 2.18.3
This release includes the fix which should prevent the scenario where Postgrex crashes can cause Oban to get into a state where it will stop processing jobs.
2024-09-18 12:09:27 -04:00
Mark Felder
73204c1bca LDAP: fix compile warning
Sometimes the compile will emit the following warning, so we'll just avoid it by making it call a function in the LDAP module which will never have this problem.

warning: :GenServer.call/2 is undefined (module :GenServer is not available or is yet to be defined)
2024-09-18 11:58:52 -04:00
feld
8776d31794 Merge branch 'swoosh-mua' into 'develop'
Add dependencies for Swoosh's Mua mail adapter

See merge request pleroma/pleroma!4263
2024-09-17 19:45:29 +00:00
Mint
1de5208a9e Cheatsheet: add Mua mail adapter config 2024-09-17 22:03:43 +03:00
feld
f423534ef0 Merge branch 'ldap-tls' into 'develop'
LDAP: refactor to a GenServer

See merge request pleroma/pleroma!4268
2024-09-17 18:15:35 +00:00
Mark Felder
35ddb1d2c8 LDAP genserver changelog 2024-09-17 13:57:10 -04:00
Mark Felder
2b482e34eb Improve matching on bind errors 2024-09-17 13:54:57 -04:00
Mark Felder
ea63533cf2 Change :connection to :handle to match upstream nomenclature 2024-09-17 13:46:56 -04:00
Mark Felder
1d123832da Formatting 2024-09-17 13:46:49 -04:00
Mark Felder
21bf229731 Reduce LDAP timeouts
10 seconds is way too long for any login attempt or search result. LDAP should always be fast.
2024-09-17 13:43:22 -04:00
Mark Felder
363b462c54 Make the email attribute configurable
While here, fix the System.get_env usage to use the normal fallback value method and improve the UID label description
2024-09-17 13:40:15 -04:00
Mark Felder
14a9663f1a Remove cacertfile as child of SSL and TLS options
We need to pass the cacerts (list of charlist encoded certs) not cacertfile, so our new cacertfile setting handles this for us.
2024-09-17 13:40:15 -04:00
Mark Felder
a1972d57e3 Link the eldap connection process
Ensure if LDAP GenServer crashes it gets cleaned up, and we should crash and restart if somehow the eldap connection process crashes unexpectedly as we can't seem to receive any DOWN messages from it, etc.
2024-09-17 13:40:15 -04:00
Mark Felder
164ffbcab8 Fix return value when not doing STARTTLS 2024-09-17 13:40:15 -04:00
Mark Felder
d0ee899ab9 Only close connection if it is not nil 2024-09-17 13:40:15 -04:00
Mark Felder
123093a186 Ensure :ssl is started before we attempt to make the LDAP connection 2024-09-17 13:40:15 -04:00
Mark Felder
65a7b387c3 Require a reboot if LDAP configuration changes 2024-09-17 13:40:15 -04:00
Mark Felder
d82abf925d Ensure :cacertfile is configurable in ConfigDB 2024-09-17 13:40:15 -04:00
Mark Felder
44b836c94c Fix tests
We do not need to mock and verify connections are closed as the new Pleroma.LDAP GenServer will handle managing the connection lifetime
2024-09-17 13:40:15 -04:00
Mark Felder
7c04098dde Catchall for when LDAP is not enabled 2024-09-17 13:40:15 -04:00
Mark Felder
ead287d623 Credo 2024-09-17 13:40:08 -04:00
Mark Felder
9264b21907 Pleroma.LDAP
This adds a GenServer which will keep an LDAP connection open and auto reconnect on failure with a 5 second wait between retries. Another benefit is this prevents parsing the Root CAs for every login attempt as we only need to do it once per connection.
2024-09-17 13:40:08 -04:00
feld
e7176bb998 Merge branch 'retry-tests' into 'develop'
Reapply "Custom mix task to retry failed tests once in CI pipeline"

See merge request pleroma/pleroma!4267
2024-09-16 20:13:10 +00:00
Mark Felder
e24e107f3d Merge remote-tracking branch 'origin/develop' into retry-tests 2024-09-16 11:51:22 -04:00
feld
3a0d4e9837 Merge branch 'ldap-tls' into 'develop'
LDAP: permit overriding the CA root, improve SSL/TLS

See merge request pleroma/pleroma!4265
2024-09-16 15:50:58 +00:00
Mark Felder
e59706c201 Reapply "Custom mix task to retry failed tests once in CI pipeline"
This reverts commit b281ad06de.
2024-09-16 11:42:55 -04:00
Mark Felder
91d1d7260b Retain the try do so an LDAP failure can fall back to local database.
This fixes tests but the automatic fallback may not be well documented behavior.
2024-09-15 23:18:18 -04:00
Mark Felder
af3bf8a462 Support implicit TLS connections
Update docs to clarify that the :ssl option is also for modern TLS, but the :tls option is only for STARTTLS

These options may benefit from being renamed but they match upstream terminology.
2024-09-15 14:00:19 -04:00
marcin mikołajczak
8250a9764e Merge branch 'notifications-group-key' into 'develop'
Add `group_key` to notifications

See merge request pleroma/pleroma!4262
2024-09-15 10:07:52 +00:00
Mark Felder
5539fea3bb LDAP: permit overriding the CA root 2024-09-14 20:36:24 -04:00
marcin mikołajczak
1a120d0130 Federate avatar/header descriptions
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-09-14 21:32:32 +02:00
marcin mikołajczak
c1a1150888 Merge branch 'profile-image-descriptions' into 'develop'
Allow providing avatar/header descriptions

See merge request pleroma/pleroma!4227
2024-09-14 17:46:33 +00:00
marcin mikołajczak
0111659a1f Merge branch 'list-id-visibility' into 'develop'
Include list id in StatusView

See merge request pleroma/pleroma!4246
2024-09-14 17:46:09 +00:00
marcin mikołajczak
abf38b405d Merge branch 'manifest-icon-size' into 'develop'
Manifest: declare /static/logo.svg as 512x512 to match one bundled with pleroma

See merge request pleroma/pleroma!4260
2024-09-14 17:27:49 +00:00
Mint
e10db52e0a Add dependencies for Swoosh's Mua mail adapter 2024-09-13 02:58:59 +03:00
marcin mikołajczak
17b69c43d5 Add group_key to notifications
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-09-12 14:37:37 +02:00
marcin mikołajczak
affdcdb68d Manifest: declare /static/logo.svg as 512x512 to match one provided by pleroma-fe
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-09-12 11:27:29 +02:00
feld
360dd34f19 Merge branch 'ldap-tls' into 'develop'
LDAP Auth: fix TLS certificate verification

See merge request pleroma/pleroma!4259
2024-09-11 18:00:22 +00:00
Mark Felder
7def11d7c3 LDAP Auth: fix TLS certificate verification
Currently we only support STARTTLS and it was not verifying certificate and hostname correctly. We must pass a custom fqdn_fun/1 function so it knows what value to compare against.
2024-09-11 12:47:46 -04:00
Haelwenn
20e82c7456 Merge branch 'argon2' into 'develop'
Support and convert argon2 passwords

See merge request pleroma/pleroma!4257
2024-09-08 20:48:21 +00:00
Mint
7e91c3a306 Changelog 2024-09-08 05:41:48 +03:00
Mint
9de522ce50 Authentication: convert argon2 passwords, add tests 2024-09-08 05:32:40 +03:00
marcin mikołajczak
c9b28eaf9a Argon2 password support 2024-09-08 05:23:46 +03:00
feld
bb0cb06d8a Merge branch 'well-known' into 'develop'
NodeInfo: Accept application/activity+json requests

See merge request pleroma/pleroma!4242
2024-09-06 16:30:07 +00:00
feld
80f3e507d4 Merge branch 'dialyzer' into 'develop'
Dialyzer fixes

See merge request pleroma/pleroma!4254
2024-09-06 16:12:28 +00:00
feld
b871551d97 Merge branch 'oban-uniques' into 'develop'
Oban: more unique job constraints

See merge request pleroma/pleroma!4253
2024-09-06 16:02:06 +00:00
feld
7f3600fddd Merge branch 'scrubber-mention-hashtag' into 'develop'
Add tests for Mastodon mention hashtag class

See merge request pleroma/pleroma!4255
2024-09-06 15:52:06 +00:00
Mark Felder
1afcfd4845 Add tests for Mastodon mention hashtag class 2024-09-06 11:51:16 -04:00
Mark Felder
5b26c56624 Changelog 2024-09-06 11:34:06 -04:00
Mark Felder
06ce5e3b43 Dialyzer: pattern_match The pattern can never match the type {:diff, false}. 2024-09-06 11:27:07 -04:00
Mark Felder
1d0e3b1355 Dialyzer: The pattern variable _ can never match the type, because it is covered by previous clauses. 2024-09-06 11:24:37 -04:00
Mark Felder
06d6febff9 Dialyzer: The pattern variable _e@1 can never match the type, because it is covered by previous clauses. 2024-09-06 11:19:24 -04:00
Mark Felder
7eb579c191 Dialyzer: invalid contract 2024-09-06 11:18:12 -04:00
Mark Felder
bc16f09d7b Dialyzer: the pattern can never match the type
The original error was for the chat controller:

lib/pleroma/web/pleroma_api/controllers/chat_controller.ex:104:pattern_match The pattern can never match the type {:error, :content_too_long | :forbidden | :no_content | :not_found} | {:user, nil}.

Improve typespecs for the Pipeline and apply them where it could be encountered
2024-09-06 11:12:15 -04:00
Mark Felder
fc3ea94a1c Dialyzer: the pattern can never match the type 2024-09-06 09:58:03 -04:00
Mark Felder
a887188890 Oban: more unique job constraints 2024-09-06 09:42:53 -04:00
feld
2c916ccd89 Merge branch 'following-state-bug' into 'develop'
Fix Following status bug

See merge request pleroma/pleroma!4251
2024-09-06 13:27:06 +00:00
feld
25db1a5d67 Merge branch 'oauth-app-spam2' into 'develop'
OAuth App Spam, revisited

See merge request pleroma/pleroma!4250
2024-09-05 21:19:09 +00:00
Mark Felder
5f573b4095 Credo: comment line length 2024-09-05 17:11:02 -04:00
Mark Felder
e51cd31a57 Bump credo to prevent it from crashing 2024-09-05 17:06:53 -04:00
Mark Felder
1797f5958a App orphans should only be removed if they are older than 15 mins 2024-09-05 20:55:28 +00:00
Mark Felder
4d76692db3 Fix Following status bug 2024-09-05 16:14:13 -04:00
Mark Felder
fb376ce005 Test Account View does not indicate following if a FollowingRelationship is missing 2024-09-05 16:14:06 -04:00
Mark Felder
53744bf146 Limit the number of orphaned to delete at 100 every 10 mins due to the cascading queries that have to check oauth_authorizations and oauth_tokens tables.
This should keep ahead of most app registration spam and not overwhelm lower powered servers.
2024-09-04 11:45:05 -04:00
Mark Felder
a1951f3af7 Add Cron worker to clean up orphaned apps hourly 2024-09-04 10:59:58 -04:00
Mark Felder
7bd0750787 Ensure apps are assigned to users 2024-09-04 10:40:37 -04:00
Mark Felder
427da7a99a Rate Limit the OAuth App spam 2024-09-04 09:22:58 -04:00
feld
fbcfbde833 Merge branch 'revert-9077d092' into 'develop'
Revert "Merge branch 'oauth-app-spam' into 'develop'"

See merge request pleroma/pleroma!4249
2024-09-04 02:41:31 +00:00
feld
92d5f0ac14 Revert "Merge branch 'oauth-app-spam' into 'develop'"
This reverts merge request !4244
2024-09-04 02:22:25 +00:00
marcin mikołajczak
6d5ae4d2e9 Include list id in StatusView
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-09-03 15:17:45 +02:00
marcin mikołajczak
fecfe8bf89 Merge branch 'scrubbers-allow-mention-hashtag' into 'develop'
scrubbers/default: Allow "mention hashtag" classes used by Mastodon

See merge request pleroma/pleroma!4245
2024-09-02 11:08:33 +00:00
marcin mikołajczak
37397a43be scrubbers/default: Allow "mention hashtag" classes used by Mastodon
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-09-02 12:39:29 +02:00
feld
9077d0925b Merge branch 'oauth-app-spam' into 'develop'
Fix OAuth app spam

See merge request pleroma/pleroma!4244
2024-09-01 18:24:06 +00:00
feld
61e4be396f Merge branch 'drop-unknown-deletes' into 'develop'
Drop unwanted activities from unknown actors

See merge request pleroma/pleroma!4236
2024-09-01 18:08:07 +00:00
Mark Felder
751d63d4bb Support OAuth App updating the website URL 2024-09-01 13:55:45 -04:00
Mark Felder
e3a7c1d906 Test that app scopes can be updated 2024-09-01 12:37:59 -04:00
Mark Felder
5a1144208d Prevent OAuth App flow from creating duplicate entries 2024-09-01 12:27:16 -04:00
Mark Felder
4ae17c6294 NodeInfo: Accept application/activity+json requests 2024-08-30 15:25:21 -04:00
Mark Felder
bb235f913f Update changelog 2024-08-30 10:05:12 -04:00
Mark Felder
11ee94ae17 InboxGuardPlug: Add early rejection of unknown activity types 2024-08-30 10:05:09 -04:00
Mark Felder
e38f5f1a81 Add recognized activity types to a constant and use it in the test 2024-08-30 09:47:45 -04:00
feld
5205e846eb Update allowed activity types from strangers
Move is emitted from the old account
EmojiReact is ~ Like
Announced TBD
2024-08-30 09:30:33 -04:00
Mark Felder
094da5d634 Update changelog 2024-08-29 16:05:40 -04:00
Mark Felder
012132303f Test more types we do not want to receive from strangers 2024-08-29 16:05:40 -04:00
Mark Felder
2b39956acb Fix test title to be more specific as it has a broader but incorrect meaning 2024-08-29 16:05:40 -04:00
Mark Felder
990b2058df Remove unnecessary error match in ReceiverWorker 2024-08-29 16:05:40 -04:00
Mark Felder
e2cdae2c88 Change relay inbox response when not federating to a 403 for consistency 2024-08-29 16:05:40 -04:00
Mark Felder
16a9b34876 Convert to an Plug called InboxGuard 2024-08-29 16:05:36 -04:00
Mark Felder
06deacd58e Formatting 2024-08-29 11:59:42 -04:00
Mark Felder
7bcc21ad6f Switch test to the inbox 2024-08-29 11:59:42 -04:00
feld
27fcc42171 Use Pleroma.Object.Containment.get_actor/1 to reliably find the actor of an incoming activity or object 2024-08-29 11:59:42 -04:00
Mark Felder
1c394dd18c Move the check to the inbox 2024-08-29 11:59:42 -04:00
Mark Felder
4bc6f334f4 Revert unintentional change 2024-08-29 11:59:42 -04:00
Mark Felder
ceffb8a891 Drop incoming Delete activities from unknown actors 2024-08-29 11:59:42 -04:00
feld
62856ab18f Merge branch 'todo-fixes' into 'develop'
Clean up Elixir 1.13 TODOs

See merge request pleroma/pleroma!4233
2024-08-29 15:27:53 +00:00
Mark Felder
b5814dc9b3 Merge remote-tracking branch 'origin/develop' into todo-fixes 2024-08-29 11:01:02 -04:00
feld
8d07034608 Merge branch 'pleroma-http-stream' into 'develop'
Pleroma.HTTP: support streaming response bodies

See merge request pleroma/pleroma!4239
2024-08-29 14:54:01 +00:00
Mark Felder
c17a78c55a Rich Media: add stream byte counting as an extra protection against malicious URLs 2024-08-29 09:37:11 -04:00
Mark Felder
d01569822e Changelog 2024-08-28 19:57:18 -04:00
Mark Felder
8ab4dd20df Update comments, remove solved TODO 2024-08-28 19:52:29 -04:00
Mark Felder
0bf82a1745 Add an AdapterHelper for Finch so we can support streaming request bodies 2024-08-28 19:50:51 -04:00
feld
7910b235c7 Merge branch 'user-refresh-oban-tests' into 'develop'
ReceiverWorker: tests, improvements

See merge request pleroma/pleroma!4241
2024-08-28 23:24:33 +00:00
Mark Felder
1821ef4f15 Move user active check into Federator.perform/1 2024-08-28 18:35:09 -04:00
marcin mikołajczak
1e8b79956e Merge branch 'docs-fix' into 'develop'
Correct response in AdminAPI docs

See merge request pleroma/pleroma!4240
2024-08-28 22:04:18 +00:00
Mark Felder
e498d252e4 Changelog update 2024-08-28 18:03:33 -04:00
Mark Felder
8a3efa7152 More error annotations 2024-08-28 18:02:35 -04:00
Mark Felder
c5ca806aa0 Add back one of the duplicate checks to fix a test, document where it comes from 2024-08-28 17:57:34 -04:00
Mark Felder
380a6a6df3 :validate_object is not a real error returned from anywhere 2024-08-28 17:45:31 -04:00
Mark Felder
2346807ac9 Annotate error cases 2024-08-28 17:44:33 -04:00
Mark Felder
2e9515578a ReceiverWorker job canceled due to deleted object 2024-08-28 17:38:13 -04:00
Mark Felder
6ae629cfe0 Cancel ReceiverWorker jobs if the user account has been disabled / deactivated 2024-08-28 17:24:59 -04:00
Mark Felder
bb2f4a76b3 Add test for origin containment failures 2024-08-28 17:01:30 -04:00
Mark Felder
3dadb9ed08 Changelog 2024-08-28 16:37:46 -04:00
Mark Felder
48a4661885 Simplify test, move data into a json fixture
By removing the inReplyTo, tags, and cc we can simplify the test and it still passes signature validation
2024-08-28 16:31:59 -04:00
Mark Felder
66e1b40895 Cancel if the User fetch resulted in a 410 2024-08-28 16:04:12 -04:00
Mark Felder
60101e240d Add test confirming cancellation for activity by a deleted user 2024-08-28 15:54:49 -04:00
Mark Felder
fc450fdefc ReceiverWorker: cancel job if user fetch is forbidden
An instance block with authenticated fetch being required can cause this as we couldn't get the user to find their public key to verify the signature. Commonly observed if someone boosts/Announces a post from an instance that blocked you.
2024-08-28 15:45:16 -04:00
marcin mikołajczak
3419e2cbdd Correct response in AdminAPI docs
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-08-28 18:28:22 +02:00
Mark Felder
0804b73c0a This error is not returned by Tesla
Upstream has a bug filed for this as they aren't handling this error internally, so it was raising
2024-08-27 22:08:31 -04:00
Mark Felder
44901502ff Fix incorrect identifier for the with statement 2024-08-27 21:56:02 -04:00
Mark Felder
116fe77b77 Tesla.Middleware.Timeout breaks streaming bodies
These are executed by Oban now and Oban can enforce the timeout if the regular HTTP timeout is not sufficient.
2024-08-27 21:55:07 -04:00
Mark Felder
0a86d2b3ac Handle streaming response errors 2024-08-27 21:22:59 -04:00
Mark Felder
ec8db9d4ee RichMedia: skip the HTTP HEAD request for adapters that support streaming the response body 2024-08-27 21:09:15 -04:00
Mark Felder
bb279c2802 Pleroma.HTTP add AdapterHelper.can_stream? to assist with discovering if the current adapter supports returning a Stream body 2024-08-27 21:08:25 -04:00
Mark Felder
5f6506d864 Pleroma.HTTP: option stream: true will return a stream as the body for Gun adapter 2024-08-27 20:39:32 -04:00
Mark Felder
5138a4984b Skip changelog 2024-08-21 15:24:33 -04:00
Mark Felder
e65555e8c5 Remove workaround for URI.merge bug on nil fields before Elixir 1.13
https://github.com/elixir-lang/elixir/issues/10771
2024-08-21 15:11:42 -04:00
Mark Felder
010edcbcb5 Use Map.filter now that minimum Elixir version is 1.13 2024-08-21 14:50:19 -04:00
marcin mikołajczak
917ac89b4f Update tests
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-08-17 20:01:25 +02:00
marcin mikołajczak
3498662712 Move new fields to pleroma object
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-08-17 19:59:39 +02:00
marcin mikołajczak
c802f3b7f6 Validate media description length
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-08-17 19:58:32 +02:00
marcin mikołajczak
855c5a234f Update docs
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-08-17 17:06:39 +02:00
marcin mikołajczak
071452a5d5 Update changelog
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-08-17 17:03:12 +02:00
marcin mikołajczak
681765669c Add test for avatar description
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-08-17 17:02:44 +02:00
marcin mikołajczak
71ef9f9519 Allow providing avatar/header descriptions
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-08-17 16:36:27 +02:00
107 changed files with 1536 additions and 619 deletions

View file

@ -1,8 +1,8 @@
image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.13.4-otp-25
image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.14.5-otp-25
variables: &global_variables
# Only used for the release
ELIXIR_VER: 1.13.4
ELIXIR_VER: 1.14.5
POSTGRES_DB: pleroma_test
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
@ -71,7 +71,7 @@ check-changelog:
tags:
- amd64
build-1.13.4-otp-25:
build-1.14.5-otp-25:
extends:
- .build_changes_policy
- .using-ci-base
@ -119,7 +119,7 @@ benchmark:
- mix ecto.migrate
- mix pleroma.load_testing
unit-testing-1.13.4-otp-25:
unit-testing-1.14.5-otp-25:
extends:
- .build_changes_policy
- .using-ci-base
@ -134,7 +134,7 @@ unit-testing-1.13.4-otp-25:
script: &testing_script
- mix ecto.create
- mix ecto.migrate
- mix test --cover --preload-modules
- mix pleroma.test_runner --cover --preload-modules
coverage: '/^Line total: ([^ ]*%)$/'
artifacts:
reports:

View file

@ -1,7 +1,8 @@
# https://hub.docker.com/r/hexpm/elixir/tags
ARG ELIXIR_IMG=hexpm/elixir
ARG ELIXIR_VER=1.13.4
ARG ERLANG_VER=24.3.4.15
ARG ALPINE_VER=3.17.5
ARG ELIXIR_VER=1.14.5
ARG ERLANG_VER=25.3.2.14
ARG ALPINE_VER=3.17.9
FROM ${ELIXIR_IMG}:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} as build

View file

@ -0,0 +1 @@
Added support for argon2 passwords and their conversion for migration from Akkoma fork to upstream.

View file

View file

@ -0,0 +1 @@
Restrict incoming activities from unknown actors to a subset that does not imply a previous relationship and early rejection of unrecognized activity types.

View file

View file

@ -0,0 +1 @@
Elixir 1.14 and Erlang/OTP 23 is now the minimum supported release

View file

@ -0,0 +1 @@
Resolved edge case where the API can report you are following a user but the relationship is not fully established.

1
changelog.d/ldap-ca.add Normal file
View file

@ -0,0 +1 @@
LDAP configuration now permits overriding the CA root certificate file for TLS validation.

View file

@ -0,0 +1 @@
LDAP authentication has been refactored to operate as a GenServer process which will maintain an active connection to the LDAP server.

1
changelog.d/ldap-tls.fix Normal file
View file

@ -0,0 +1 @@
STARTTLS certificate and hostname verification for LDAP authentication

View file

1
changelog.d/ldaps.fix Normal file
View file

@ -0,0 +1 @@
LDAPS connections (implicit TLS) are now supported.

View file

@ -0,0 +1 @@
Include list id in StatusView

View file

View file

@ -0,0 +1 @@
Add `id_filter` to MRF to filter URLs and their domain prior to fetching

View file

@ -0,0 +1 @@
Add `group_key` to notifications

View file

@ -0,0 +1 @@
Add a rate limiter to the OAuth App creation endpoint and ensure registered apps are assigned to users.

View file

@ -0,0 +1 @@
ReceiverWorker will cancel processing jobs instead of retrying if the user cannot be fetched due to 403, 404, or 410 errors or if the account is disabled locally.

View file

@ -0,0 +1 @@
Adjust more Oban workers to enforce unique job constraints.

View file

@ -0,0 +1 @@
Oban updated to 2.18.3

View file

@ -0,0 +1 @@
Allow providing avatar/header descriptions

View file

@ -0,0 +1 @@
Rich Media preview fetching will skip making an HTTP HEAD request to check a URL for allowed content type and length if the Tesla adapter is Gun or Finch

View file

@ -0,0 +1 @@
scrubbers/default: Allow "mention hashtag" classes used by Mastodon

View file

@ -0,0 +1 @@
Added dependencies for Swoosh's Mua mail adapter

View file

View file

@ -0,0 +1 @@
Accept application/activity+json for requests to .well-known/nodeinfo

View file

@ -1 +0,0 @@
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.12 --push .

View file

@ -1,8 +0,0 @@
FROM elixir:1.13.4-otp-25
# Single RUN statement, otherwise intermediate images are created
# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run
RUN apt-get update &&\
apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\
mix local.hex --force &&\
mix local.rebar --force

View file

@ -1,4 +1,4 @@
FROM elixir:1.12.3
FROM elixir:1.14.5-otp-25
# Single RUN statement, otherwise intermediate images are created
# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run

View file

@ -1 +1 @@
docker buildx build --platform linux/amd64,linux/arm64 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.13.4-otp-25 --push .
docker buildx build --platform linux/amd64,linux/arm64 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.14.5-otp-25 --push .

View file

@ -342,7 +342,7 @@ config :pleroma, :manifest,
icons: [
%{
src: "/static/logo.svg",
sizes: "144x144",
sizes: "512x512",
purpose: "any",
type: "image/svg+xml"
}
@ -610,7 +610,8 @@ config :pleroma, Oban,
plugins: [{Oban.Plugins.Pruner, max_age: 900}],
crontab: [
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker},
{"*/10 * * * *", Pleroma.Workers.Cron.AppCleanupWorker}
]
config :pleroma, Pleroma.Formatter,
@ -624,14 +625,17 @@ config :pleroma, Pleroma.Formatter,
config :pleroma, :ldap,
enabled: System.get_env("LDAP_ENABLED") == "true",
host: System.get_env("LDAP_HOST") || "localhost",
port: String.to_integer(System.get_env("LDAP_PORT") || "389"),
host: System.get_env("LDAP_HOST", "localhost"),
port: String.to_integer(System.get_env("LDAP_PORT", "389")),
ssl: System.get_env("LDAP_SSL") == "true",
sslopts: [],
tls: System.get_env("LDAP_TLS") == "true",
tlsopts: [],
base: System.get_env("LDAP_BASE") || "dc=example,dc=com",
uid: System.get_env("LDAP_UID") || "cn"
base: System.get_env("LDAP_BASE", "dc=example,dc=com"),
uid: System.get_env("LDAP_UID", "cn"),
# defaults to CAStore's Mozilla roots
cacertfile: System.get_env("LDAP_CACERTFILE", nil),
mail: System.get_env("LDAP_MAIL", "mail")
oauth_consumer_strategies =
System.get_env("OAUTH_CONSUMER_STRATEGIES")
@ -724,6 +728,7 @@ config :pleroma, :rate_limit,
timeline: {500, 3},
search: [{1000, 10}, {1000, 30}],
app_account_creation: {1_800_000, 25},
oauth_app_creation: {900_000, 5},
relations_actions: {10_000, 10},
relation_id_action: {60_000, 2},
statuses_actions: {10_000, 15},

View file

@ -2241,14 +2241,8 @@ config :pleroma, :config_description, [
label: "SSL options",
type: :keyword,
description: "Additional SSL options",
suggestions: [cacertfile: "path/to/file/with/PEM/cacerts", verify: :verify_peer],
suggestions: [verify: :verify_peer],
children: [
%{
key: :cacertfile,
type: :string,
description: "Path to file with PEM encoded cacerts",
suggestions: ["path/to/file/with/PEM/cacerts"]
},
%{
key: :verify,
type: :atom,
@ -2268,14 +2262,8 @@ config :pleroma, :config_description, [
label: "TLS options",
type: :keyword,
description: "Additional TLS options",
suggestions: [cacertfile: "path/to/file/with/PEM/cacerts", verify: :verify_peer],
suggestions: [verify: :verify_peer],
children: [
%{
key: :cacertfile,
type: :string,
description: "Path to file with PEM encoded cacerts",
suggestions: ["path/to/file/with/PEM/cacerts"]
},
%{
key: :verify,
type: :atom,
@ -2292,11 +2280,25 @@ config :pleroma, :config_description, [
},
%{
key: :uid,
label: "UID",
label: "UID Attribute",
type: :string,
description:
"LDAP attribute name to authenticate the user, e.g. when \"cn\", the filter will be \"cn=username,base\"",
suggestions: ["cn"]
},
%{
key: :cacertfile,
label: "CACertfile",
type: :string,
description: "Path to CA certificate file"
},
%{
key: :mail,
label: "Mail Attribute",
type: :string,
description:
"LDAP attribute name to use as the email address when automatically registering the user on first login",
suggestions: ["mail"]
}
]
},

View file

@ -742,6 +742,21 @@ config :pleroma, Pleroma.Emails.Mailer,
auth: :always
```
An example for Mua adapter:
```elixir
config :pleroma, Pleroma.Emails.Mailer,
enabled: true,
adapter: Swoosh.Adapters.Mua,
relay: "mail.example.com",
port: 465,
auth: [
username: "YOUR_USERNAME@domain.tld",
password: "YOUR_SMTP_PASSWORD"
],
protocol: :ssl
```
### :email_notifications
Email notifications settings.
@ -968,12 +983,13 @@ Pleroma account will be created with the same name as the LDAP user name.
* `enabled`: enables LDAP authentication
* `host`: LDAP server hostname
* `port`: LDAP port, e.g. 389 or 636
* `ssl`: true to use SSL, usually implies the port 636
* `ssl`: true to use implicit SSL/TLS, usually port 636
* `sslopts`: additional SSL options
* `tls`: true to start TLS, usually implies the port 389
* `tls`: true to use explicit TLS (STARTTLS), usually port 389
* `tlsopts`: additional TLS options
* `base`: LDAP base, e.g. "dc=example,dc=com"
* `uid`: LDAP attribute name to authenticate the user, e.g. when "cn", the filter will be "cn=username,base"
* `cacertfile`: Path to alternate CA root certificates file
Note, if your LDAP server is an Active Directory server the correct value is commonly `uid: "cn"`, but if you use an
OpenLDAP server the value may be `uid: "uid"`.

View file

@ -433,7 +433,7 @@ Response:
* On success: URL of the unfollowed relay
```json
{"https://example.com/relay"}
"https://example.com/relay"
```
## `POST /api/v1/pleroma/admin/users/invite_token`
@ -1193,20 +1193,23 @@ Loads json generated from `config/descriptions.exs`.
- Response:
```json
[
{
"id": 1234,
"data": {
"actor": {
"id": 1,
"nickname": "lain"
{
"items": [
{
"id": 1234,
"data": {
"actor": {
"id": 1,
"nickname": "lain"
},
"action": "relay_follow"
},
"action": "relay_follow"
},
"time": 1502812026, // timestamp
"message": "[2017-08-15 15:47:06] @nick0 followed relay: https://example.org/relay" // log message
}
]
"time": 1502812026, // timestamp
"message": "[2017-08-15 15:47:06] @nick0 followed relay: https://example.org/relay" // log message
}
],
"total": 1
}
```
## `POST /api/v1/pleroma/admin/reload_emoji`

View file

@ -42,6 +42,7 @@ Has these additional fields under the `pleroma` object:
- `quotes_count`: the count of status quotes.
- `non_anonymous`: true if the source post specifies the poll results are not anonymous. Currently only implemented by Smithereen.
- `bookmark_folder`: the ID of the folder bookmark is stored within (if any).
- `list_id`: the ID of the list the post is addressed to (if any, only returned to author).
The `GET /api/v1/statuses/:id/source` endpoint additionally has the following attributes:
@ -103,7 +104,7 @@ Has these additional fields under the `pleroma` object:
- `background_image`: nullable URL string, background image of the user
- `tags`: Lists an array of tags for the user
- `relationship` (object): Includes fields as documented for Mastodon API https://docs.joinmastodon.org/entities/relationship/
- `is_moderator`: boolean, nullable, true if user is a moderator
- `is_moderator`: boolean, nullable, true if user is a moderator
- `is_admin`: boolean, nullable, true if user is an admin
- `confirmation_pending`: boolean, true if a new user account is waiting on email confirmation to be activated
- `hide_favorites`: boolean, true when the user has hiding favorites enabled
@ -120,6 +121,8 @@ Has these additional fields under the `pleroma` object:
- `notification_settings`: object, can be absent. See `/api/v1/pleroma/notification_settings` for the parameters/keys returned.
- `accepts_chat_messages`: boolean, but can be null if we don't have that information about a user
- `favicon`: nullable URL string, Favicon image of the user's instance
- `avatar_description`: string, image description for user avatar, defaults to empty string
- `header_description`: string, image description for user banner, defaults to empty string
### Source
@ -255,6 +258,8 @@ Additional parameters can be added to the JSON body/Form data:
- `actor_type` - the type of this account.
- `accepts_chat_messages` - if false, this account will reject all chat messages.
- `language` - user's preferred language for receiving emails (digest, confirmation, etc.)
- `avatar_description` - image description for user avatar
- `header_description` - image description for user banner
All images (avatar, banner and background) can be reset to the default by sending an empty string ("") instead of a file.

View file

@ -14,7 +14,7 @@ Note: This article is potentially outdated because at this time we may not have
- PostgreSQL 11.0以上 (Ubuntu16.04では9.5しか提供されていないので,[](https://www.postgresql.org/download/linux/ubuntu/)こちらから新しいバージョンを入手してください)
- `postgresql-contrib` 11.0以上 (同上)
- Elixir 1.13 以上 ([Debianのリポジトリからインストールしないこと ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください)
- Elixir 1.14 以上 ([Debianのリポジトリからインストールしないこと ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください)
- `erlang-dev`
- `erlang-nox`
- `git`

View file

@ -1,8 +1,8 @@
## Required dependencies
* PostgreSQL >=11.0
* Elixir >=1.13.0 <1.17
* Erlang OTP >=22.2.0 (supported: <27)
* Elixir >=1.14.0 <1.17
* Erlang OTP >=23.0.0 (supported: <27)
* git
* file / libmagic
* gcc or clang

View file

@ -0,0 +1,25 @@
defmodule Mix.Tasks.Pleroma.TestRunner do
@shortdoc "Retries tests once if they fail"
use Mix.Task
def run(args \\ []) do
case System.cmd("mix", ["test"] ++ args, into: IO.stream(:stdio, :line)) do
{_, 0} ->
:ok
_ ->
retry(args)
end
end
def retry(args) do
case System.cmd("mix", ["test", "--failed"] ++ args, into: IO.stream(:stdio, :line)) do
{_, 0} ->
:ok
_ ->
exit(1)
end
end
end

View file

@ -94,6 +94,7 @@ defmodule Pleroma.Application do
children =
[
Pleroma.PromEx,
Pleroma.LDAP,
Pleroma.Repo,
Config.TransferTask,
Pleroma.Emoji,

View file

@ -22,7 +22,8 @@ defmodule Pleroma.Config.TransferTask do
{:pleroma, :markup},
{:pleroma, :streamer},
{:pleroma, :pools},
{:pleroma, :connections_pool}
{:pleroma, :connections_pool},
{:pleroma, :ldap}
]
defp reboot_time_subkeys,

View file

@ -85,6 +85,36 @@ defmodule Pleroma.Constants do
]
)
const(activity_types,
do: [
"Create",
"Update",
"Delete",
"Follow",
"Accept",
"Reject",
"Add",
"Remove",
"Like",
"Announce",
"Undo",
"Flag",
"EmojiReact"
]
)
const(allowed_activity_types_from_strangers,
do: [
"Block",
"Create",
"Flag",
"Follow",
"Like",
"EmojiReact",
"Announce"
]
)
# basic regex, just there to weed out potential mistakes
# https://datatracker.ietf.org/doc/html/rfc2045#section-5.1
const(mime_regex,

View file

@ -52,6 +52,7 @@ defmodule Pleroma.HTTP.AdapterHelper do
case adapter() do
Tesla.Adapter.Gun -> AdapterHelper.Gun
Tesla.Adapter.Hackney -> AdapterHelper.Hackney
{Tesla.Adapter.Finch, _} -> AdapterHelper.Finch
_ -> AdapterHelper.Default
end
end
@ -118,4 +119,13 @@ defmodule Pleroma.HTTP.AdapterHelper do
host_charlist
end
end
@spec can_stream? :: bool()
def can_stream? do
case Application.get_env(:tesla, :adapter) do
Tesla.Adapter.Gun -> true
{Tesla.Adapter.Finch, _} -> true
_ -> false
end
end
end

View file

@ -0,0 +1,33 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.AdapterHelper.Finch do
@behaviour Pleroma.HTTP.AdapterHelper
alias Pleroma.Config
alias Pleroma.HTTP.AdapterHelper
@spec options(keyword(), URI.t()) :: keyword()
def options(incoming_opts \\ [], %URI{} = _uri) do
proxy =
[:http, :proxy_url]
|> Config.get()
|> AdapterHelper.format_proxy()
config_opts = Config.get([:http, :adapter], [])
config_opts
|> Keyword.merge(incoming_opts)
|> AdapterHelper.maybe_add_proxy(proxy)
|> maybe_stream()
end
# Finch uses [response: :stream]
defp maybe_stream(opts) do
case Keyword.pop(opts, :stream, nil) do
{true, opts} -> Keyword.put(opts, :response, :stream)
{_, opts} -> opts
end
end
end

View file

@ -32,6 +32,7 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
|> AdapterHelper.maybe_add_proxy(proxy)
|> Keyword.merge(incoming_opts)
|> put_timeout()
|> maybe_stream()
end
defp add_scheme_opts(opts, %{scheme: "http"}), do: opts
@ -47,6 +48,14 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
Keyword.put(opts, :timeout, recv_timeout)
end
# Gun uses [body_as: :stream]
defp maybe_stream(opts) do
case Keyword.pop(opts, :stream, nil) do
{true, opts} -> Keyword.put(opts, :body_as, :stream)
{_, opts} -> opts
end
end
@spec pool_timeout(pool()) :: non_neg_integer()
def pool_timeout(pool) do
default = Config.get([:pools, :default, :recv_timeout], 5_000)

245
lib/pleroma/ldap.ex Normal file
View file

@ -0,0 +1,245 @@
defmodule Pleroma.LDAP do
use GenServer
require Logger
alias Pleroma.Config
alias Pleroma.User
import Pleroma.Web.Auth.Helpers, only: [fetch_user: 1]
@connection_timeout 2_000
@search_timeout 2_000
def start_link(_) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
@impl true
def init(state) do
case {Config.get(Pleroma.Web.Auth.Authenticator), Config.get([:ldap, :enabled])} do
{Pleroma.Web.Auth.LDAPAuthenticator, true} ->
{:ok, state, {:continue, :connect}}
{Pleroma.Web.Auth.LDAPAuthenticator, false} ->
Logger.error(
"LDAP Authenticator enabled but :pleroma, :ldap is not enabled. Auth will not work."
)
{:ok, state}
{_, true} ->
Logger.warning(
":pleroma, :ldap is enabled but Pleroma.Web.Authenticator is not set to the LDAPAuthenticator. LDAP will not be used."
)
{:ok, state}
_ ->
{:ok, state}
end
end
@impl true
def handle_continue(:connect, _state), do: do_handle_connect()
@impl true
def handle_info(:connect, _state), do: do_handle_connect()
def handle_info({:bind_after_reconnect, name, password, from}, state) do
result = bind_user(state[:handle], name, password)
GenServer.reply(from, result)
{:noreply, state}
end
defp do_handle_connect do
state =
case connect() do
{:ok, handle} ->
:eldap.controlling_process(handle, self())
Process.link(handle)
[handle: handle]
_ ->
Logger.error("Failed to connect to LDAP. Retrying in 5000ms")
Process.send_after(self(), :connect, 5_000)
[]
end
{:noreply, state}
end
@impl true
def handle_call({:bind_user, name, password}, from, state) do
case bind_user(state[:handle], name, password) do
:needs_reconnect ->
Process.send(self(), {:bind_after_reconnect, name, password, from}, [])
{:noreply, state, {:continue, :connect}}
result ->
{:reply, result, state, :hibernate}
end
end
@impl true
def terminate(_, state) do
handle = Keyword.get(state, :handle)
if not is_nil(handle) do
:eldap.close(handle)
end
:ok
end
def bind_user(name, password) do
GenServer.call(__MODULE__, {:bind_user, name, password})
end
defp connect do
ldap = Config.get(:ldap, [])
host = Keyword.get(ldap, :host, "localhost")
port = Keyword.get(ldap, :port, 389)
ssl = Keyword.get(ldap, :ssl, false)
tls = Keyword.get(ldap, :tls, false)
cacertfile = Keyword.get(ldap, :cacertfile) || CAStore.file_path()
if ssl, do: Application.ensure_all_started(:ssl)
default_secure_opts = [
verify: :verify_peer,
cacerts: decode_certfile(cacertfile),
customize_hostname_check: [
fqdn_fun: fn _ -> to_charlist(host) end
]
]
sslopts = Keyword.merge(default_secure_opts, Keyword.get(ldap, :sslopts, []))
tlsopts = Keyword.merge(default_secure_opts, Keyword.get(ldap, :tlsopts, []))
default_options = [{:port, port}, {:ssl, ssl}, {:timeout, @connection_timeout}]
# :sslopts can only be included in :eldap.open/2 when {ssl: true}
# or the connection will fail
options =
if ssl do
default_options ++ [{:sslopts, sslopts}]
else
default_options
end
case :eldap.open([to_charlist(host)], options) do
{:ok, handle} ->
try do
cond do
tls ->
case :eldap.start_tls(
handle,
tlsopts,
@connection_timeout
) do
:ok ->
{:ok, handle}
error ->
Logger.error("Could not start TLS: #{inspect(error)}")
:eldap.close(handle)
end
true ->
{:ok, handle}
end
after
:ok
end
{:error, error} ->
Logger.error("Could not open LDAP connection: #{inspect(error)}")
{:error, {:ldap_connection_error, error}}
end
end
defp bind_user(handle, name, password) do
uid = Config.get([:ldap, :uid], "cn")
base = Config.get([:ldap, :base])
case :eldap.simple_bind(handle, "#{uid}=#{name},#{base}", password) do
:ok ->
case fetch_user(name) do
%User{} = user ->
user
_ ->
register_user(handle, base, uid, name)
end
# eldap does not inform us of socket closure
# until it is used
{:error, {:gen_tcp_error, :closed}} ->
:eldap.close(handle)
:needs_reconnect
{:error, error} = e ->
Logger.error("Could not bind LDAP user #{name}: #{inspect(error)}")
e
end
end
defp register_user(handle, base, uid, name) do
case :eldap.search(handle, [
{:base, to_charlist(base)},
{:filter, :eldap.equalityMatch(to_charlist(uid), to_charlist(name))},
{:scope, :eldap.wholeSubtree()},
{:timeout, @search_timeout}
]) do
# The :eldap_search_result record structure changed in OTP 24.3 and added a controls field
# https://github.com/erlang/otp/pull/5538
{:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals}} ->
try_register(name, attributes)
{:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals, _controls}} ->
try_register(name, attributes)
error ->
Logger.error("Couldn't register user because LDAP search failed: #{inspect(error)}")
{:error, {:ldap_search_error, error}}
end
end
defp try_register(name, attributes) do
mail_attribute = Config.get([:ldap, :mail])
params = %{
name: name,
nickname: name,
password: nil
}
params =
case List.keyfind(attributes, to_charlist(mail_attribute), 0) do
{_, [mail]} -> Map.put_new(params, :email, :erlang.list_to_binary(mail))
_ -> params
end
changeset = User.register_changeset_ldap(%User{}, params)
case User.register(changeset) do
{:ok, user} -> user
error -> error
end
end
defp decode_certfile(file) do
with {:ok, data} <- File.read(file) do
data
|> :public_key.pem_decode()
|> Enum.map(fn {_, b, _} -> b end)
else
_ ->
Logger.error("Unable to read certfile: #{file}")
[]
end
end
end

View file

@ -20,15 +20,13 @@ defmodule Pleroma.Maps do
end
def filter_empty_values(data) do
# TODO: Change to Map.filter in Elixir 1.13+
data
|> Enum.filter(fn
|> Map.filter(fn
{_k, nil} -> false
{_k, ""} -> false
{_k, []} -> false
{_k, %{} = v} -> Map.keys(v) != []
{_k, _v} -> true
end)
|> Map.new()
end
end

View file

@ -58,8 +58,12 @@ defmodule Pleroma.Object.Fetcher do
end
end
@typep fetcher_errors ::
:error | :reject | :allowed_depth | :fetch | :containment | :transmogrifier
# Note: will create a Create activity, which we need internally at the moment.
@spec fetch_object_from_id(String.t(), list()) :: {:ok, Object.t()} | {:error | :reject, any()}
@spec fetch_object_from_id(String.t(), list()) ::
{:ok, Object.t()} | {fetcher_errors(), any()} | Pipeline.errors()
def fetch_object_from_id(id, options \\ []) do
with {_, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)},
{_, true} <- {:allowed_depth, Federator.allowed_thread_distance?(options[:depth])},
@ -141,6 +145,7 @@ defmodule Pleroma.Object.Fetcher do
Logger.debug("Fetching object #{id} via AP")
with {:scheme, true} <- {:scheme, String.starts_with?(id, "http")},
{_, true} <- {:mrf, MRF.id_filter(id)},
{:ok, body} <- get_object(id),
{:ok, data} <- safe_json_decode(body),
:ok <- Containment.contain_origin_from_id(id, data) do
@ -156,6 +161,9 @@ defmodule Pleroma.Object.Fetcher do
{:error, e} ->
{:error, e}
{:mrf, false} ->
{:error, {:reject, "Filtered by id"}}
e ->
{:error, e}
end

View file

@ -419,6 +419,11 @@ defmodule Pleroma.User do
end
end
def image_description(image, default \\ "")
def image_description(%{"name" => name}, _default), do: name
def image_description(_, default), do: default
# Should probably be renamed or removed
@spec ap_id(User.t()) :: String.t()
def ap_id(%User{nickname: nickname}), do: "#{Endpoint.url()}/users/#{nickname}"
@ -586,16 +591,26 @@ defmodule Pleroma.User do
|> validate_length(:bio, max: bio_limit)
|> validate_length(:name, min: 1, max: name_limit)
|> validate_inclusion(:actor_type, Pleroma.Constants.allowed_user_actor_types())
|> validate_image_description(:avatar_description, params)
|> validate_image_description(:header_description, params)
|> put_fields()
|> put_emoji()
|> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
|> put_change_if_present(:avatar, &put_upload(&1, :avatar))
|> put_change_if_present(:banner, &put_upload(&1, :banner))
|> put_change_if_present(
:avatar,
&put_upload(&1, :avatar, Map.get(params, :avatar_description))
)
|> put_change_if_present(
:banner,
&put_upload(&1, :banner, Map.get(params, :header_description))
)
|> put_change_if_present(:background, &put_upload(&1, :background))
|> put_change_if_present(
:pleroma_settings_store,
&{:ok, Map.merge(struct.pleroma_settings_store, &1)}
)
|> maybe_update_image_description(:avatar, Map.get(params, :avatar_description))
|> maybe_update_image_description(:banner, Map.get(params, :header_description))
|> validate_fields(false)
end
@ -674,13 +689,41 @@ defmodule Pleroma.User do
end
end
defp put_upload(value, type) do
defp put_upload(value, type, description \\ nil) do
with %Plug.Upload{} <- value,
{:ok, object} <- ActivityPub.upload(value, type: type) do
{:ok, object} <- ActivityPub.upload(value, type: type, description: description) do
{:ok, object.data}
end
end
defp validate_image_description(changeset, key, params) do
description_limit = Config.get([:instance, :description_limit], 5_000)
description = Map.get(params, key)
if is_binary(description) and String.length(description) > description_limit do
changeset
|> add_error(key, "#{key} is too long")
else
changeset
end
end
defp maybe_update_image_description(changeset, image_field, description)
when is_binary(description) do
with {:image_missing, true} <- {:image_missing, not changed?(changeset, image_field)},
{:existing_image, %{"id" => id}} <-
{:existing_image, Map.get(changeset.data, image_field)},
{:object, %Object{} = object} <- {:object, Object.get_by_ap_id(id)},
{:ok, object} <- Object.update_data(object, %{"name" => description}) do
put_change(changeset, image_field, object.data)
else
{:description_too_long, true} -> {:error}
_ -> changeset
end
end
defp maybe_update_image_description(changeset, _, _), do: changeset
def update_as_admin_changeset(struct, params) do
struct
|> update_changeset(params)

View file

@ -92,9 +92,6 @@ defmodule Pleroma.User.Backup do
else
true ->
{:error, "Backup is missing id. Please insert it into the Repo first."}
e ->
{:error, e}
end
end
@ -121,14 +118,13 @@ defmodule Pleroma.User.Backup do
end
defp permitted?(user) do
with {_, %__MODULE__{inserted_at: inserted_at}} <- {:last, get_last(user)},
days = Config.get([__MODULE__, :limit_days]),
diff = Timex.diff(NaiveDateTime.utc_now(), inserted_at, :days),
{_, true} <- {:diff, diff > days} do
true
with {_, %__MODULE__{inserted_at: inserted_at}} <- {:last, get_last(user)} do
days = Config.get([__MODULE__, :limit_days])
diff = Timex.diff(NaiveDateTime.utc_now(), inserted_at, :days)
diff > days
else
{:last, nil} -> true
{:diff, false} -> false
end
end
@ -297,9 +293,6 @@ defmodule Pleroma.User.Backup do
)
acc
_ ->
acc
end
end)

View file

@ -12,7 +12,7 @@ defmodule Pleroma.User.Import do
require Logger
@spec perform(atom(), User.t(), list()) :: :ok | list() | {:error, any()}
@spec perform(atom(), User.t(), String.t()) :: :ok | {:error, any()}
def perform(:mute_import, %User{} = user, actor) do
with {:ok, %User{} = muted_user} <- User.get_or_fetch(actor),
{_, false} <- {:existing_mute, User.mutes_user?(user, muted_user)},
@ -49,7 +49,7 @@ defmodule Pleroma.User.Import do
defp handle_error(op, user_id, error) do
Logger.debug("#{op} failed for #{user_id} with: #{inspect(error)}")
error
{:error, error}
end
def blocks_import(%User{} = user, [_ | _] = actors) do

View file

@ -1542,16 +1542,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp get_actor_url(_url), do: nil
defp normalize_image(%{"url" => url}) do
defp normalize_image(%{"url" => url} = data) do
%{
"type" => "Image",
"url" => [%{"href" => url}]
}
|> maybe_put_description(data)
end
defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image()
defp normalize_image(_), do: nil
defp maybe_put_description(map, %{"name" => description}) when is_binary(description) do
Map.put(map, "name", description)
end
defp maybe_put_description(map, _), do: map
defp object_to_user_data(data, additional) do
fields =
data

View file

@ -311,7 +311,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
post_inbox_relayed_create(conn, params)
else
conn
|> put_status(:bad_request)
|> put_status(403)
|> json("Not federating")
end
end

View file

@ -108,6 +108,14 @@ defmodule Pleroma.Web.ActivityPub.MRF do
def filter(%{} = object), do: get_policies() |> filter(object)
def id_filter(policies, id) when is_binary(id) do
policies
|> Enum.filter(&function_exported?(&1, :id_filter, 1))
|> Enum.all?(& &1.id_filter(id))
end
def id_filter(id) when is_binary(id), do: get_policies() |> id_filter(id)
@impl true
def pipeline_filter(%{} = message, meta) do
object = meta[:object_data]

View file

@ -13,6 +13,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
{:reject, activity}
end
@impl true
def id_filter(id) do
Logger.debug("REJECTING #{id}")
false
end
@impl true
def describe, do: {:ok, %{}}
end

View file

@ -4,6 +4,7 @@
defmodule Pleroma.Web.ActivityPub.MRF.Policy do
@callback filter(Pleroma.Activity.t()) :: {:ok | :reject, Pleroma.Activity.t()}
@callback id_filter(String.t()) :: boolean()
@callback describe() :: {:ok | :error, map()}
@callback config_description() :: %{
optional(:children) => [map()],
@ -13,5 +14,5 @@ defmodule Pleroma.Web.ActivityPub.MRF.Policy do
description: String.t()
}
@callback history_awareness() :: :auto | :manual
@optional_callbacks config_description: 0, history_awareness: 0
@optional_callbacks config_description: 0, history_awareness: 0, id_filter: 1
end

View file

@ -191,6 +191,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|> MRF.instance_list_from_tuples()
end
@impl true
def id_filter(id) do
host_info = URI.parse(id)
with {:ok, _} <- check_accept(host_info, %{}),
{:ok, _} <- check_reject(host_info, %{}) do
true
else
_ -> false
end
end
@impl true
def filter(%{"type" => "Delete", "actor" => actor} = activity) do
%{host: actor_host} = URI.parse(actor)

View file

@ -22,22 +22,27 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
defp activity_pub, do: Config.get([:pipeline, :activity_pub], ActivityPub)
defp config, do: Config.get([:pipeline, :config], Config)
@spec common_pipeline(map(), keyword()) ::
{:ok, Activity.t() | Object.t(), keyword()} | {:error | :reject, any()}
@type results :: {:ok, Activity.t() | Object.t(), keyword()}
@type errors :: {:error | :reject, any()}
# The Repo.transaction will wrap the result in an {:ok, _}
# and only returns an {:error, _} if the error encountered was related
# to the SQL transaction
@spec common_pipeline(map(), keyword()) :: results() | errors()
def common_pipeline(object, meta) do
case Repo.transaction(fn -> do_common_pipeline(object, meta) end, Utils.query_timeout()) do
{:ok, {:ok, activity, meta}} ->
side_effects().handle_after_transaction(meta)
{:ok, activity, meta}
{:ok, value} ->
value
{:ok, {:error, _} = error} ->
error
{:ok, {:reject, _} = error} ->
error
{:error, e} ->
{:error, e}
{:reject, e} ->
{:reject, e}
end
end

View file

@ -129,8 +129,22 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"vcard:bday" => birthday,
"webfinger" => "acct:#{User.full_nickname(user)}"
}
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))
|> Map.merge(
maybe_make_image(
&User.avatar_url/2,
User.image_description(user.avatar, nil),
"icon",
user
)
)
|> Map.merge(
maybe_make_image(
&User.banner_url/2,
User.image_description(user.banner, nil),
"image",
user
)
)
|> Map.merge(Utils.make_json_ld_header())
end
@ -305,16 +319,24 @@ defmodule Pleroma.Web.ActivityPub.UserView do
end
end
defp maybe_make_image(func, key, user) do
defp maybe_make_image(func, description, key, user) do
if image = func.(user, no_default: true) do
%{
key => %{
"type" => "Image",
"url" => image
}
key =>
%{
"type" => "Image",
"url" => image
}
|> maybe_put_description(description)
}
else
%{}
end
end
defp maybe_put_description(map, description) when is_binary(description) do
Map.put(map, "name", description)
end
defp maybe_put_description(map, _description), do: map
end

View file

@ -813,6 +813,16 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
allOf: [BooleanLike],
nullable: true,
description: "User's birthday will be visible"
},
avatar_description: %Schema{
type: :string,
nullable: true,
description: "Avatar image description."
},
header_description: %Schema{
type: :string,
nullable: true,
description: "Header image description."
}
},
example: %{

View file

@ -158,6 +158,10 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
type: :object,
properties: %{
id: %Schema{type: :string},
group_key: %Schema{
type: :string,
description: "Group key shared by similar notifications"
},
type: notification_type(),
created_at: %Schema{type: :string, format: :"date-time"},
account: %Schema{
@ -180,6 +184,7 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
},
example: %{
"id" => "34975861",
"group-key" => "ungrouped-34975861",
"type" => "mention",
"created_at" => "2019-11-23T07:49:02.064Z",
"account" => Account.schema().example,

View file

@ -111,7 +111,9 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
format: :uri,
nullable: true,
description: "Favicon image of the user's instance"
}
},
avatar_description: %Schema{type: :string},
header_description: %Schema{type: :string}
}
},
source: %Schema{
@ -152,6 +154,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
example: %{
"acct" => "foobar",
"avatar" => "https://mypleroma.com/images/avi.png",
"avatar_description" => "",
"avatar_static" => "https://mypleroma.com/images/avi.png",
"bot" => false,
"created_at" => "2020-03-24T13:05:58.000Z",
@ -162,6 +165,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
"followers_count" => 0,
"following_count" => 1,
"header" => "https://mypleroma.com/images/banner.png",
"header_description" => "",
"header_static" => "https://mypleroma.com/images/banner.png",
"id" => "9tKi3esbG7OQgZ2920",
"locked" => false,

View file

@ -249,6 +249,12 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
nullable: true,
description:
"A datetime (ISO 8601) that states when the post was pinned or `null` if the post is not pinned"
},
list_id: %Schema{
type: :integer,
nullable: true,
description:
"The ID of the list the post is addressed to (if any, only returned to author)"
}
}
},

View file

@ -3,18 +3,14 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Auth.LDAPAuthenticator do
alias Pleroma.LDAP
alias Pleroma.User
require Logger
import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1]
import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1]
@behaviour Pleroma.Web.Auth.Authenticator
@base Pleroma.Web.Auth.PleromaAuthenticator
@connection_timeout 10_000
@search_timeout 10_000
defdelegate get_registration(conn), to: @base
defdelegate create_from_registration(conn, registration), to: @base
defdelegate handle_error(conn, error), to: @base
@ -24,7 +20,7 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do
def get_user(%Plug.Conn{} = conn) do
with {:ldap, true} <- {:ldap, Pleroma.Config.get([:ldap, :enabled])},
{:ok, {name, password}} <- fetch_credentials(conn),
%User{} = user <- ldap_user(name, password) do
%User{} = user <- LDAP.bind_user(name, password) do
{:ok, user}
else
{:ldap, _} ->
@ -34,107 +30,4 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do
error
end
end
defp ldap_user(name, password) do
ldap = Pleroma.Config.get(:ldap, [])
host = Keyword.get(ldap, :host, "localhost")
port = Keyword.get(ldap, :port, 389)
ssl = Keyword.get(ldap, :ssl, false)
sslopts = Keyword.get(ldap, :sslopts, [])
options =
[{:port, port}, {:ssl, ssl}, {:timeout, @connection_timeout}] ++
if sslopts != [], do: [{:sslopts, sslopts}], else: []
case :eldap.open([to_charlist(host)], options) do
{:ok, connection} ->
try do
if Keyword.get(ldap, :tls, false) do
:application.ensure_all_started(:ssl)
case :eldap.start_tls(
connection,
Keyword.get(ldap, :tlsopts, []),
@connection_timeout
) do
:ok ->
:ok
error ->
Logger.error("Could not start TLS: #{inspect(error)}")
end
end
bind_user(connection, ldap, name, password)
after
:eldap.close(connection)
end
{:error, error} ->
Logger.error("Could not open LDAP connection: #{inspect(error)}")
{:error, {:ldap_connection_error, error}}
end
end
defp bind_user(connection, ldap, name, password) do
uid = Keyword.get(ldap, :uid, "cn")
base = Keyword.get(ldap, :base)
case :eldap.simple_bind(connection, "#{uid}=#{name},#{base}", password) do
:ok ->
case fetch_user(name) do
%User{} = user ->
user
_ ->
register_user(connection, base, uid, name)
end
error ->
Logger.error("Could not bind LDAP user #{name}: #{inspect(error)}")
{:error, {:ldap_bind_error, error}}
end
end
defp register_user(connection, base, uid, name) do
case :eldap.search(connection, [
{:base, to_charlist(base)},
{:filter, :eldap.equalityMatch(to_charlist(uid), to_charlist(name))},
{:scope, :eldap.wholeSubtree()},
{:timeout, @search_timeout}
]) do
# The :eldap_search_result record structure changed in OTP 24.3 and added a controls field
# https://github.com/erlang/otp/pull/5538
{:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals}} ->
try_register(name, attributes)
{:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals, _controls}} ->
try_register(name, attributes)
error ->
Logger.error("Couldn't register user because LDAP search failed: #{inspect(error)}")
{:error, {:ldap_search_error, error}}
end
end
defp try_register(name, attributes) do
params = %{
name: name,
nickname: name,
password: nil
}
params =
case List.keyfind(attributes, ~c"mail", 0) do
{_, [mail]} -> Map.put_new(params, :email, :erlang.list_to_binary(mail))
_ -> params
end
changeset = User.register_changeset_ldap(%User{}, params)
case User.register(changeset) do
{:ok, user} -> user
error -> error
end
end
end

View file

@ -26,7 +26,7 @@ defmodule Pleroma.Web.CommonAPI do
require Pleroma.Constants
require Logger
@spec block(User.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()}
@spec block(User.t(), User.t()) :: {:ok, Activity.t()} | Pipeline.errors()
def block(blocked, blocker) do
with {:ok, block_data, _} <- Builder.block(blocker, blocked),
{:ok, block, _} <- Pipeline.common_pipeline(block_data, local: true) do
@ -35,7 +35,7 @@ defmodule Pleroma.Web.CommonAPI do
end
@spec post_chat_message(User.t(), User.t(), String.t(), list()) ::
{:ok, Activity.t()} | {:error, any()}
{:ok, Activity.t()} | Pipeline.errors()
def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do
with maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]),
:ok <- validate_chat_attachment_attribution(maybe_attachment, user),
@ -58,7 +58,7 @@ defmodule Pleroma.Web.CommonAPI do
)} do
{:ok, activity}
else
{:common_pipeline, {:reject, _} = e} -> e
{:common_pipeline, e} -> e
e -> e
end
end
@ -99,7 +99,8 @@ defmodule Pleroma.Web.CommonAPI do
end
end
@spec unblock(User.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()}
@spec unblock(User.t(), User.t()) ::
{:ok, Activity.t()} | {:ok, :no_activity} | Pipeline.errors() | {:error, :not_blocking}
def unblock(blocked, blocker) do
with {_, %Activity{} = block} <- {:fetch_block, Utils.fetch_latest_block(blocker, blocked)},
{:ok, unblock_data, _} <- Builder.undo(blocker, block),
@ -120,7 +121,9 @@ defmodule Pleroma.Web.CommonAPI do
end
@spec follow(User.t(), User.t()) ::
{:ok, User.t(), User.t(), Activity.t() | Object.t()} | {:error, :rejected}
{:ok, User.t(), User.t(), Activity.t() | Object.t()}
| {:error, :rejected}
| Pipeline.errors()
def follow(followed, follower) do
timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout])
@ -145,7 +148,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
@spec accept_follow_request(User.t(), User.t()) :: {:ok, User.t()} | {:error, any()}
@spec accept_follow_request(User.t(), User.t()) :: {:ok, User.t()} | Pipeline.errors()
def accept_follow_request(follower, followed) do
with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
{:ok, accept_data, _} <- Builder.accept(followed, follow_activity),
@ -154,7 +157,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
@spec reject_follow_request(User.t(), User.t()) :: {:ok, User.t()} | {:error, any()} | nil
@spec reject_follow_request(User.t(), User.t()) :: {:ok, User.t()} | Pipeline.errors() | nil
def reject_follow_request(follower, followed) do
with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
{:ok, reject_data, _} <- Builder.reject(followed, follow_activity),
@ -163,7 +166,8 @@ defmodule Pleroma.Web.CommonAPI do
end
end
@spec delete(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()}
@spec delete(String.t(), User.t()) ::
{:ok, Activity.t()} | Pipeline.errors() | {:error, :not_found | String.t()}
def delete(activity_id, user) do
with {_, %Activity{data: %{"object" => _, "type" => "Create"}} = activity} <-
{:find_activity, Activity.get_by_id(activity_id, filter: [])},
@ -213,7 +217,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
@spec repeat(String.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, any()}
@spec repeat(String.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, :not_found}
def repeat(id, user, params \\ %{}) do
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
object = %Object{} <- Object.normalize(activity, fetch: false),
@ -231,7 +235,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
@spec unrepeat(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()}
@spec unrepeat(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, :not_found | String.t()}
def unrepeat(id, user) do
with {_, %Activity{data: %{"type" => "Create"}} = activity} <-
{:find_activity, Activity.get_by_id(id)},
@ -247,7 +251,8 @@ defmodule Pleroma.Web.CommonAPI do
end
end
@spec favorite(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()}
@spec favorite(String.t(), User.t()) ::
{:ok, Activity.t()} | {:ok, :already_liked} | {:error, :not_found | String.t()}
def favorite(id, %User{} = user) do
case favorite_helper(user, id) do
{:ok, _} = res ->
@ -285,7 +290,8 @@ defmodule Pleroma.Web.CommonAPI do
end
end
@spec unfavorite(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()}
@spec unfavorite(String.t(), User.t()) ::
{:ok, Activity.t()} | {:error, :not_found | String.t()}
def unfavorite(id, user) do
with {_, %Activity{data: %{"type" => "Create"}} = activity} <-
{:find_activity, Activity.get_by_id(id)},
@ -302,7 +308,7 @@ defmodule Pleroma.Web.CommonAPI do
end
@spec react_with_emoji(String.t(), User.t(), String.t()) ::
{:ok, Activity.t()} | {:error, any()}
{:ok, Activity.t()} | {:error, String.t()}
def react_with_emoji(id, user, emoji) do
with %Activity{} = activity <- Activity.get_by_id(id),
object <- Object.normalize(activity, fetch: false),
@ -316,7 +322,7 @@ defmodule Pleroma.Web.CommonAPI do
end
@spec unreact_with_emoji(String.t(), User.t(), String.t()) ::
{:ok, Activity.t()} | {:error, any()}
{:ok, Activity.t()} | {:error, String.t()}
def unreact_with_emoji(id, user, emoji) do
with %Activity{} = reaction_activity <- Utils.get_latest_reaction(id, user, emoji),
{_, {:ok, _}} <- {:cancel_jobs, maybe_cancel_jobs(reaction_activity)},
@ -329,7 +335,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
@spec vote(Object.t(), User.t(), list()) :: {:ok, list(), Object.t()} | {:error, any()}
@spec vote(Object.t(), User.t(), list()) :: {:ok, list(), Object.t()} | Pipeline.errors()
def vote(%Object{data: %{"type" => "Question"}} = object, %User{} = user, choices) do
with :ok <- validate_not_author(object, user),
:ok <- validate_existing_votes(user, object),
@ -461,7 +467,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
@spec update(Activity.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, any()}
@spec update(Activity.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, nil}
def update(orig_activity, %User{} = user, changes) do
with orig_object <- Object.normalize(orig_activity),
{:ok, new_object} <- make_update_data(user, orig_object, changes),
@ -497,7 +503,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
@spec pin(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, term()}
@spec pin(String.t(), User.t()) :: {:ok, Activity.t()} | Pipeline.errors()
def pin(id, %User{} = user) do
with %Activity{} = activity <- create_activity_by_id(id),
true <- activity_belongs_to_actor(activity, user.ap_id),
@ -537,7 +543,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
@spec unpin(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, term()}
@spec unpin(String.t(), User.t()) :: {:ok, Activity.t()} | Pipeline.errors()
def unpin(id, user) do
with %Activity{} = activity <- create_activity_by_id(id),
{:ok, unpin_data, _} <- Builder.unpin(user, activity.object),
@ -552,7 +558,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
@spec add_mute(Activity.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, any()}
@spec add_mute(Activity.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, String.t()}
def add_mute(activity, user, params \\ %{}) do
expires_in = Map.get(params, :expires_in, 0)

View file

@ -102,7 +102,8 @@ defmodule Pleroma.Web.Federator do
# NOTE: we use the actor ID to do the containment, this is fine because an
# actor shouldn't be acting on objects outside their own AP server.
with {_, {:ok, _user}} <- {:actor, User.get_or_fetch_by_ap_id(actor)},
with {_, {:ok, user}} <- {:actor, User.get_or_fetch_by_ap_id(actor)},
{:user_active, true} <- {:user_active, match?(true, user.is_active)},
nil <- Activity.normalize(params["id"]),
{_, :ok} <-
{:correct_origin?, Containment.contain_origin_from_id(actor, params)},
@ -121,11 +122,6 @@ defmodule Pleroma.Web.Federator do
Logger.debug("Unhandled actor #{actor}, #{inspect(e)}")
{:error, e}
{:error, {:validate_object, _}} = e ->
Logger.error("Incoming AP doc validation error: #{inspect(e)}")
Logger.debug(Jason.encode!(params, pretty: true))
e
e ->
# Just drop those for now
Logger.debug(fn -> "Unhandled activity\n" <> Jason.encode!(params, pretty: true) end)

View file

@ -232,6 +232,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|> Maps.put_if_present(:is_discoverable, params[:discoverable])
|> Maps.put_if_present(:birthday, params[:birthday])
|> Maps.put_if_present(:language, Pleroma.Web.Gettext.normalize_locale(params[:language]))
|> Maps.put_if_present(:avatar_description, params[:avatar_description])
|> Maps.put_if_present(:header_description, params[:header_description])
# What happens here:
#
@ -277,6 +279,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
{:error, %Ecto.Changeset{errors: [{:name, {_, _}} | _]}} ->
render_error(conn, :request_entity_too_large, "Name is too long")
{:error, %Ecto.Changeset{errors: [{:avatar_description, {_, _}} | _]}} ->
render_error(conn, :request_entity_too_large, "Avatar description is too long")
{:error, %Ecto.Changeset{errors: [{:header_description, {_, _}} | _]}} ->
render_error(conn, :request_entity_too_large, "Banner description is too long")
{:error, %Ecto.Changeset{errors: [{:fields, {"invalid", _}} | _]}} ->
render_error(conn, :request_entity_too_large, "One or more field entries are too long")

View file

@ -19,6 +19,8 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
plug(Pleroma.Web.Plugs.RateLimiter, [name: :oauth_app_creation] when action == :create)
plug(:skip_auth when action in [:create, :verify_credentials])
plug(Pleroma.Web.ApiSpec.CastAndValidate)

View file

@ -92,14 +92,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
User.get_follow_state(reading_user, target)
end
followed_by =
if following_relationships do
case FollowingRelationship.find(following_relationships, target, reading_user) do
%{state: :follow_accept} -> true
_ -> false
end
else
User.following?(target, reading_user)
followed_by = FollowingRelationship.following?(target, reading_user)
following = FollowingRelationship.following?(reading_user, target)
requested =
cond do
following -> false
true -> match?(:follow_pending, follow_state)
end
subscribing =
@ -114,7 +113,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
# NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags
%{
id: to_string(target.id),
following: follow_state == :follow_accept,
following: following,
followed_by: followed_by,
blocking:
UserRelationship.exists?(
@ -150,7 +149,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
),
subscribing: subscribing,
notifying: subscribing,
requested: follow_state == :follow_pending,
requested: requested,
domain_blocking: User.blocks_domain?(reading_user, target),
showing_reblogs:
not UserRelationship.exists?(
@ -220,8 +219,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
avatar = User.avatar_url(user) |> MediaProxy.url()
avatar_static = User.avatar_url(user) |> MediaProxy.preview_url(static: true)
avatar_description = User.image_description(user.avatar)
header = User.banner_url(user) |> MediaProxy.url()
header_static = User.banner_url(user) |> MediaProxy.preview_url(static: true)
header_description = User.image_description(user.banner)
following_count =
if !user.hide_follows_count or !user.hide_follows or self,
@ -322,7 +323,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
skip_thread_containment: user.skip_thread_containment,
background_image: image_url(user.background) |> MediaProxy.url(),
accepts_chat_messages: user.accepts_chat_messages,
favicon: favicon
favicon: favicon,
avatar_description: avatar_description,
header_description: header_description
}
}
|> maybe_put_role(user, opts[:for])

View file

@ -95,6 +95,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
response = %{
id: to_string(notification.id),
group_key: "ungrouped-" <> to_string(notification.id),
type: notification.type,
created_at: CommonAPI.Utils.to_masto_date(notification.inserted_at),
account: account,

View file

@ -465,7 +465,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
parent_visible: visible_for_user?(reply_to, opts[:for]),
pinned_at: pinned_at,
quotes_count: object.data["quotesCount"] || 0,
bookmark_folder: bookmark_folder
bookmark_folder: bookmark_folder,
list_id: get_list_id(object, client_posted_this_activity)
}
}
end
@ -803,19 +804,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
defp build_application(_), do: nil
# Workaround for Elixir issue #10771
# Avoid applying URI.merge unless necessary
# TODO: revert to always attempting URI.merge(image_url_data, page_url_data)
# when Elixir 1.12 is the minimum supported version
@spec build_image_url(struct() | nil, struct()) :: String.t() | nil
defp build_image_url(
%URI{scheme: image_scheme, host: image_host} = image_url_data,
%URI{} = _page_url_data
)
when not is_nil(image_scheme) and not is_nil(image_host) do
image_url_data |> to_string
end
@spec build_image_url(URI.t(), URI.t()) :: String.t()
defp build_image_url(%URI{} = image_url_data, %URI{} = page_url_data) do
URI.merge(page_url_data, image_url_data) |> to_string
end
@ -847,4 +836,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
nil
end
end
defp get_list_id(object, client_posted_this_activity) do
with true <- client_posted_this_activity,
%{data: %{"listMessage" => list_ap_id}} when is_binary(list_ap_id) <- object,
%{id: list_id} <- Pleroma.List.get_by_ap_id(list_ap_id) do
list_id
else
_ -> nil
end
end
end

View file

@ -8,6 +8,7 @@ defmodule Pleroma.Web.OAuth.App do
import Ecto.Query
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.OAuth.Token
@type t :: %__MODULE__{}
@ -155,4 +156,29 @@ defmodule Pleroma.Web.OAuth.App do
Map.put(acc, key, error)
end)
end
@spec maybe_update_owner(Token.t()) :: :ok
def maybe_update_owner(%Token{app_id: app_id, user_id: user_id}) when not is_nil(user_id) do
__MODULE__.update(app_id, %{user_id: user_id})
:ok
end
def maybe_update_owner(_), do: :ok
@spec remove_orphans(pos_integer()) :: :ok
def remove_orphans(limit \\ 100) do
fifteen_mins_ago = DateTime.add(DateTime.utc_now(), -900, :second)
Repo.transaction(fn ->
from(a in __MODULE__,
where: is_nil(a.user_id) and a.inserted_at < ^fifteen_mins_ago,
limit: ^limit
)
|> Repo.all()
|> Enum.each(&Repo.delete(&1))
end)
:ok
end
end

View file

@ -318,6 +318,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
def token_exchange(%Plug.Conn{} = conn, params), do: bad_request(conn, params)
def after_token_exchange(%Plug.Conn{} = conn, %{token: token} = view_params) do
App.maybe_update_owner(token)
conn
|> AuthHelper.put_session_token(token.token)
|> json(OAuthView.render("token.json", view_params))

View file

@ -47,6 +47,11 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
Pleroma.Password.Pbkdf2.verify_pass(password, password_hash)
end
def checkpw(password, "$argon2" <> _ = password_hash) do
# Handle argon2 passwords for Akkoma migration
Argon2.verify_pass(password, password_hash)
end
def checkpw(_password, _password_hash) do
Logger.error("Password hash not recognized")
false
@ -56,6 +61,10 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
do_update_password(user, password)
end
def maybe_update_password(%User{password_hash: "$argon2" <> _} = user, password) do
do_update_password(user, password)
end
def maybe_update_password(user, _), do: {:ok, user}
defp do_update_password(user, password) do

View file

@ -0,0 +1,89 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.InboxGuardPlug do
import Plug.Conn
import Pleroma.Constants, only: [activity_types: 0, allowed_activity_types_from_strangers: 0]
alias Pleroma.Config
alias Pleroma.User
def init(options) do
options
end
def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
with {_, true} <- {:federating, Config.get!([:instance, :federating])} do
conn
|> filter_activity_types()
else
{:federating, false} ->
conn
|> json(403, "Not federating")
|> halt()
end
end
def call(conn, _opts) do
with {_, true} <- {:federating, Config.get!([:instance, :federating])},
conn = filter_activity_types(conn),
{:known, true} <- {:known, known_actor?(conn)} do
conn
else
{:federating, false} ->
conn
|> json(403, "Not federating")
|> halt()
{:known, false} ->
conn
|> filter_from_strangers()
end
end
# Early rejection of unrecognized types
defp filter_activity_types(%{body_params: %{"type" => type}} = conn) do
with true <- type in activity_types() do
conn
else
_ ->
conn
|> json(400, "Invalid activity type")
|> halt()
end
end
# If signature failed but we know this actor we should
# accept it as we may only need to refetch their public key
# during processing
defp known_actor?(%{body_params: data}) do
case Pleroma.Object.Containment.get_actor(data) |> User.get_cached_by_ap_id() do
%User{} -> true
_ -> false
end
end
# Only permit a subset of activity types from strangers
# or else it will add actors you've never interacted with
# to the database
defp filter_from_strangers(%{body_params: %{"type" => type}} = conn) do
with true <- type in allowed_activity_types_from_strangers() do
conn
else
_ ->
conn
|> json(400, "Invalid activity type for an unknown actor")
|> halt()
end
end
defp json(conn, status, resp) do
json_resp = Jason.encode!(resp)
conn
|> put_resp_content_type("application/json")
|> resp(status, json_resp)
|> halt()
end
end

View file

@ -11,16 +11,39 @@ defmodule Pleroma.Web.RichMedia.Helpers do
@spec rich_media_get(String.t()) :: {:ok, String.t()} | get_errors()
def rich_media_get(url) do
headers = [{"user-agent", Pleroma.Application.user_agent() <> "; Bot"}]
case Pleroma.HTTP.AdapterHelper.can_stream?() do
true -> stream(url)
false -> head_first(url)
end
|> handle_result(url)
end
defp stream(url) do
with {_, {:ok, %Tesla.Env{status: 200, body: stream_body, headers: headers}}} <-
{:get, Pleroma.HTTP.get(url, req_headers(), http_options())},
{_, :ok} <- {:content_type, check_content_type(headers)},
{_, :ok} <- {:content_length, check_content_length(headers)},
{:read_stream, {:ok, body}} <- {:read_stream, read_stream(stream_body)} do
{:ok, body}
end
end
defp head_first(url) do
with {_, {:ok, %Tesla.Env{status: 200, headers: headers}}} <-
{:head, Pleroma.HTTP.head(url, headers, http_options())},
{:head, Pleroma.HTTP.head(url, req_headers(), http_options())},
{_, :ok} <- {:content_type, check_content_type(headers)},
{_, :ok} <- {:content_length, check_content_length(headers)},
{_, {:ok, %Tesla.Env{status: 200, body: body}}} <-
{:get, Pleroma.HTTP.get(url, headers, http_options())} do
{:get, Pleroma.HTTP.get(url, req_headers(), http_options())} do
{:ok, body}
else
end
end
defp handle_result(result, url) do
case result do
{:ok, body} ->
{:ok, body}
{:head, _} ->
Logger.debug("Rich media error for #{url}: HTTP HEAD failed")
{:error, :head}
@ -29,8 +52,12 @@ defmodule Pleroma.Web.RichMedia.Helpers do
Logger.debug("Rich media error for #{url}: content-type is #{type}")
{:error, :content_type}
{:content_length, {_, length}} ->
Logger.debug("Rich media error for #{url}: content-length is #{length}")
{:content_length, :error} ->
Logger.debug("Rich media error for #{url}: content-length exceeded")
{:error, :body_too_large}
{:read_stream, :error} ->
Logger.debug("Rich media error for #{url}: content-length exceeded")
{:error, :body_too_large}
{:get, _} ->
@ -59,7 +86,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
{_, maybe_content_length} ->
case Integer.parse(maybe_content_length) do
{content_length, ""} when content_length <= max_body -> :ok
{_, ""} -> {:error, maybe_content_length}
{_, ""} -> :error
_ -> :ok
end
@ -68,13 +95,37 @@ defmodule Pleroma.Web.RichMedia.Helpers do
end
end
defp http_options do
timeout = Config.get!([:rich_media, :timeout])
defp read_stream(stream) do
max_body = Keyword.get(http_options(), :max_body)
try do
result =
Stream.transform(stream, 0, fn chunk, total_bytes ->
new_total = total_bytes + byte_size(chunk)
if new_total > max_body do
raise("Exceeds max body limit of #{max_body}")
else
{[chunk], new_total}
end
end)
|> Enum.into(<<>>)
{:ok, result}
rescue
_ -> :error
end
end
defp http_options do
[
pool: :rich_media,
max_body: Config.get([:rich_media, :max_body], 5_000_000),
tesla_middleware: [{Tesla.Middleware.Timeout, timeout: timeout}]
stream: true
]
end
defp req_headers do
[{"user-agent", Pleroma.Application.user_agent() <> "; Bot"}]
end
end

View file

@ -189,7 +189,7 @@ defmodule Pleroma.Web.Router do
end
pipeline :well_known do
plug(:accepts, ["json", "jrd", "jrd+json", "xml", "xrd+xml"])
plug(:accepts, ["activity+json", "json", "jrd", "jrd+json", "xml", "xrd+xml"])
end
pipeline :config do
@ -217,6 +217,10 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Web.Plugs.MappedSignatureToIdentityPlug)
end
pipeline :inbox_guard do
plug(Pleroma.Web.Plugs.InboxGuardPlug)
end
pipeline :static_fe do
plug(Pleroma.Web.Plugs.StaticFEPlug)
end
@ -920,7 +924,7 @@ defmodule Pleroma.Web.Router do
end
scope "/", Pleroma.Web.ActivityPub do
pipe_through(:activitypub)
pipe_through([:activitypub, :inbox_guard])
post("/inbox", ActivityPubController, :inbox)
post("/users/:nickname/inbox", ActivityPubController, :inbox)
end

View file

@ -0,0 +1,21 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.Cron.AppCleanupWorker do
@moduledoc """
Cleans up registered apps that were never associated with a user.
"""
use Oban.Worker, queue: "background"
alias Pleroma.Web.OAuth.App
@impl true
def perform(_job) do
App.remove_orphans()
end
@impl true
def timeout(_job), do: :timer.seconds(30)
end

View file

@ -7,7 +7,7 @@ defmodule Pleroma.Workers.ReceiverWorker do
alias Pleroma.User
alias Pleroma.Web.Federator
use Oban.Worker, queue: :federator_incoming, max_attempts: 5
use Oban.Worker, queue: :federator_incoming, max_attempts: 5, unique: [period: :infinity]
@impl true
@ -33,7 +33,7 @@ defmodule Pleroma.Workers.ReceiverWorker do
query_string: query_string
}
with {:ok, %User{} = _actor} <- User.get_or_fetch_by_ap_id(conn_data.params["actor"]),
with {:ok, %User{}} <- User.get_or_fetch_by_ap_id(conn_data.params["actor"]),
{:ok, _public_key} <- Signature.refetch_public_key(conn_data),
{:signature, true} <- {:signature, Signature.validate_signature(conn_data)},
{:ok, res} <- Federator.perform(:incoming_ap_doc, params) do
@ -56,17 +56,29 @@ defmodule Pleroma.Workers.ReceiverWorker do
def timeout(_job), do: :timer.seconds(5)
defp process_errors({:error, {:error, _} = error}), do: process_errors(error)
defp process_errors(errors) do
case errors do
{:error, :origin_containment_failed} -> {:cancel, :origin_containment_failed}
{:error, :already_present} -> {:cancel, :already_present}
{:error, {:validate_object, _} = reason} -> {:cancel, reason}
{:error, {:error, {:validate, {:error, _changeset} = reason}}} -> {:cancel, reason}
{:error, {:reject, _} = reason} -> {:cancel, reason}
{:signature, false} -> {:cancel, :invalid_signature}
{:error, "Object has been deleted"} = reason -> {:cancel, reason}
{:error, {:side_effects, {:error, :no_object_actor}} = reason} -> {:cancel, reason}
# User fetch failures
{:error, :not_found} = reason -> {:cancel, reason}
{:error, :forbidden} = reason -> {:cancel, reason}
# Inactive user
{:error, {:user_active, false} = reason} -> {:cancel, reason}
# Validator will error and return a changeset error
# e.g., duplicate activities or if the object was deleted
{:error, {:validate, {:error, _changeset} = reason}} -> {:cancel, reason}
# Duplicate detection during Normalization
{:error, :already_present} -> {:cancel, :already_present}
# MRFs will return a reject
{:error, {:reject, _} = reason} -> {:cancel, reason}
# HTTP Sigs
{:signature, false} -> {:cancel, :invalid_signature}
# Origin / URL validation failed somewhere possibly due to spoofing
{:error, :origin_containment_failed} -> {:cancel, :origin_containment_failed}
# Unclear if this can be reached
{:error, {:side_effects, {:error, :no_object_actor}} = reason} -> {:cancel, reason}
# Catchall
{:error, _} = e -> e
e -> {:error, e}
end

View file

@ -5,7 +5,7 @@
defmodule Pleroma.Workers.RemoteFetcherWorker do
alias Pleroma.Object.Fetcher
use Oban.Worker, queue: :background
use Oban.Worker, queue: :background, unique: [period: :infinity]
@impl true
def perform(%Job{args: %{"op" => "fetch_remote", "id" => id} = args}) do

View file

@ -7,7 +7,7 @@ defmodule Pleroma.Workers.RichMediaWorker do
alias Pleroma.Web.RichMedia.Backfill
alias Pleroma.Web.RichMedia.Card
use Oban.Worker, queue: :background, max_attempts: 3, unique: [period: 300]
use Oban.Worker, queue: :background, max_attempts: 3, unique: [period: :infinity]
@impl true
def perform(%Job{args: %{"op" => "expire", "url" => url} = _args}) do

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.UserRefreshWorker do
use Oban.Worker, queue: :background, max_attempts: 1, unique: [period: 300]
use Oban.Worker, queue: :background, max_attempts: 1, unique: [period: :infinity]
alias Pleroma.User

View file

@ -7,7 +7,7 @@ defmodule Pleroma.Workers.WebPusherWorker do
alias Pleroma.Repo
alias Pleroma.Web.Push.Impl
use Oban.Worker, queue: :web_push
use Oban.Worker, queue: :web_push, unique: [period: :infinity]
@impl true
def perform(%Job{args: %{"op" => "web_push", "notification_id" => notification_id}}) do

View file

@ -5,7 +5,7 @@ defmodule Pleroma.Mixfile do
[
app: :pleroma,
version: version("2.7.0"),
elixir: "~> 1.13",
elixir: "~> 1.14",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: Mix.compilers(),
elixirc_options: [warnings_as_errors: warnings_as_errors(), prune_code_paths: false],
@ -153,7 +153,7 @@ defmodule Pleroma.Mixfile do
{:calendar, "~> 1.0"},
{:cachex, "~> 3.2"},
{:tesla, "~> 1.11"},
{:castore, "~> 0.1"},
{:castore, "~> 1.0"},
{:cowlib, "~> 2.9", override: true},
{:gun, "~> 2.0.0-rc.1", override: true},
{:finch, "~> 0.15"},
@ -169,6 +169,8 @@ defmodule Pleroma.Mixfile do
{:swoosh, "~> 1.16.9"},
{:phoenix_swoosh, "~> 1.1"},
{:gen_smtp, "~> 0.13"},
{:mua, "~> 0.2.0"},
{:mail, "~> 0.3.0"},
{:ex_syslogger, "~> 1.4"},
{:floki, "~> 0.35"},
{:timex, "~> 3.6"},
@ -203,6 +205,7 @@ defmodule Pleroma.Mixfile do
{:websock_adapter, "~> 0.5.6"},
{:oban_live_dashboard, "~> 0.1.1"},
{:multipart, "~> 0.4.0", optional: true},
{:argon2_elixir, "~> 4.0"},
## dev & test
{:phoenix_live_reload, "~> 1.3.3", only: :dev},

View file

@ -1,5 +1,6 @@
%{
"accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm", "11b18c220bcc2eab63b5470c038ef10eb6783bcb1fcdb11aa4137defa5ac1bb8"},
"argon2_elixir": {:hex, :argon2_elixir, "4.0.0", "7f6cd2e4a93a37f61d58a367d82f830ad9527082ff3c820b8197a8a736648941", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f9da27cf060c9ea61b1bd47837a28d7e48a8f6fa13a745e252556c14f9132c7f"},
"bandit": {:hex, :bandit, "1.5.5", "df28f1c41f745401fe9e85a6882033f5f3442ab6d30c8a2948554062a4ab56e0", [:mix], [{:hpax, "~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "f21579a29ea4bc08440343b2b5f16f7cddf2fea5725d31b72cf973ec729079e1"},
"base62": {:hex, :base62, "1.2.2", "85c6627eb609317b70f555294045895ffaaeb1758666ab9ef9ca38865b11e629", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "d41336bda8eaa5be197f1e4592400513ee60518e5b9f4dcf38f4b4dae6f377bb"},
"bbcode_pleroma": {:hex, :bbcode_pleroma, "0.2.0", "d36f5bca6e2f62261c45be30fa9b92725c0655ad45c99025cb1c3e28e25803ef", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "19851074419a5fedb4ef49e1f01b30df504bb5dbb6d6adfc135238063bebd1c3"},
@ -10,7 +11,7 @@
"cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"},
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "6630c42aaaab124e697b4e513190c89d8b64e410", [ref: "6630c42aaaab124e697b4e513190c89d8b64e410"]},
"castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"},
"castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"},
"cc_precompiler": {:hex, :cc_precompiler, "0.1.9", "e8d3364f310da6ce6463c3dd20cf90ae7bbecbf6c5203b98bf9b48035592649b", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "9dcab3d0f3038621f1601f13539e7a9ee99843862e66ad62827b0c42b2f58a54"},
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
@ -22,7 +23,7 @@
"cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
"cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"},
"credo": {:hex, :credo, "1.7.3", "05bb11eaf2f2b8db370ecaa6a6bda2ec49b2acd5e0418bc106b73b07128c0436", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "35ea675a094c934c22fb1dca3696f3c31f2728ae6ef5a53b5d648c11180a4535"},
"credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"},
"crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
"custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"},
"db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"},
@ -71,6 +72,7 @@
"jumper": {:hex, :jumper, "1.0.2", "68cdcd84472a00ac596b4e6459a41b3062d4427cbd4f1e8c8793c5b54f1406a7", [:mix], [], "hexpm", "9b7782409021e01ab3c08270e26f36eb62976a38c1aa64b2eaf6348422f165e1"},
"linkify": {:hex, :linkify, "0.5.3", "5f8143d8f61f5ff08d3aeeff47ef6509492b4948d8f08007fbf66e4d2246a7f2", [:mix], [], "hexpm", "3ef35a1377d47c25506e07c1c005ea9d38d700699d92ee92825f024434258177"},
"logger_backends": {:hex, :logger_backends, "1.0.0", "09c4fad6202e08cb0fbd37f328282f16539aca380f512523ce9472b28edc6bdf", [:mix], [], "hexpm", "1faceb3e7ec3ef66a8f5746c5afd020e63996df6fd4eb8cdb789e5665ae6c9ce"},
"mail": {:hex, :mail, "0.3.1", "cb0a14e4ed8904e4e5a08214e686ccf6f9099346885db17d8c309381f865cc5c", [:mix], [], "hexpm", "1db701e89865c1d5fa296b2b57b1cd587587cca8d8a1a22892b35ef5a8e352a6"},
"majic": {:hex, :majic, "1.0.0", "37e50648db5f5c2ff0c9fb46454d034d11596c03683807b9fb3850676ffdaab3", [:make, :mix], [{:elixir_make, "~> 0.6.1", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "7905858f76650d49695f14ea55cd9aaaee0c6654fa391671d4cf305c275a0a9e"},
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
@ -84,12 +86,13 @@
"mock": {:hex, :mock, "0.3.8", "7046a306b71db2488ef54395eeb74df0a7f335a7caca4a3d3875d1fc81c884dd", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "7fa82364c97617d79bb7d15571193fc0c4fe5afd0c932cef09426b3ee6fe2022"},
"mogrify": {:hex, :mogrify, "0.9.3", "238c782f00271dace01369ad35ae2e9dd020feee3443b9299ea5ea6bed559841", [:mix], [], "hexpm", "0189b1e1de27455f2b9ae8cf88239cefd23d38de9276eb5add7159aea51731e6"},
"mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"},
"mua": {:hex, :mua, "0.2.3", "46b29b7b2bb14105c0b7be9526f7c452df17a7841b30b69871c024a822ff551c", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "7fe861a87fcc06a980d3941bbcb2634e5f0f30fd6ad15ef6c0423ff9dc7e46de"},
"multipart": {:hex, :multipart, "0.4.0", "634880a2148d4555d050963373d0e3bbb44a55b2badd87fa8623166172e9cda0", [:mix], [{:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "3c5604bc2fb17b3137e5d2abdf5dacc2647e60c5cc6634b102cf1aef75a06f0a"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"},
"nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
"oban": {:hex, :oban, "2.18.2", "583e78965ee15263ac968e38c983bad169ae55eadaa8e1e39912562badff93ba", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9dd25fd35883a91ed995e9fe516e479344d3a8623dfe2b8c3fc8e5be0228ec3a"},
"oban": {:hex, :oban, "2.18.3", "1608c04f8856c108555c379f2f56bc0759149d35fa9d3b825cb8a6769f8ae926", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36ca6ca84ef6518f9c2c759ea88efd438a3c81d667ba23b02b062a0aa785475e"},
"oban_live_dashboard": {:hex, :oban_live_dashboard, "0.1.1", "8aa4ceaf381c818f7d5c8185cc59942b8ac82ef0cf559881aacf8d3f8ac7bdd3", [:mix], [{:oban, "~> 2.15", [hex: :oban, repo: "hexpm", optional: false]}, {:phoenix_live_dashboard, "~> 0.7", [hex: :phoenix_live_dashboard, repo: "hexpm", optional: false]}], "hexpm", "16dc4ce9c9a95aa2e655e35ed4e675652994a8def61731a18af85e230e1caa63"},
"octo_fetch": {:hex, :octo_fetch, "0.4.0", "074b5ecbc08be10b05b27e9db08bc20a3060142769436242702931c418695b19", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "cf8be6f40cd519d7000bb4e84adcf661c32e59369ca2827c4e20042eda7a7fc6"},
"open_api_spex": {:hex, :open_api_spex, "3.18.2", "8c855e83bfe8bf81603d919d6e892541eafece3720f34d1700b58024dadde247", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "aa3e6dcfc0ad6a02596b2172662da21c9dd848dac145ea9e603f54e3d81b8d2b"},

View file

@ -0,0 +1,21 @@
defmodule Pleroma.Repo.Migrations.AssignAppUser do
use Ecto.Migration
alias Pleroma.Repo
alias Pleroma.Web.OAuth.App
alias Pleroma.Web.OAuth.Token
def up do
Repo.all(Token)
|> Enum.group_by(fn x -> Map.get(x, :app_id) end)
|> Enum.each(fn {_app_id, tokens} ->
token =
Enum.filter(tokens, fn x -> not is_nil(x.user_id) end)
|> List.first()
App.maybe_update_owner(token)
end)
end
def down, do: :ok
end

View file

@ -22,7 +22,8 @@ defmodule Pleroma.HTML.Scrubber.Default do
"u-url",
"mention",
"u-url mention",
"mention u-url"
"mention u-url",
"mention hashtag"
])
Meta.allow_tag_with_this_attribute_values(:a, "rel", [

View file

@ -23,7 +23,8 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
"u-url",
"mention",
"u-url mention",
"mention u-url"
"mention u-url",
"mention hashtag"
])
Meta.allow_tag_with_this_attribute_values(:a, "rel", [

View file

@ -1,117 +0,0 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"Curve25519Key": "toot:Curve25519Key",
"Device": "toot:Device",
"Ed25519Key": "toot:Ed25519Key",
"Ed25519Signature": "toot:Ed25519Signature",
"EncryptedMessage": "toot:EncryptedMessage",
"PropertyValue": "schema:PropertyValue",
"alsoKnownAs": {
"@id": "as:alsoKnownAs",
"@type": "@id"
},
"cipherText": "toot:cipherText",
"claim": {
"@id": "toot:claim",
"@type": "@id"
},
"deviceId": "toot:deviceId",
"devices": {
"@id": "toot:devices",
"@type": "@id"
},
"discoverable": "toot:discoverable",
"featured": {
"@id": "toot:featured",
"@type": "@id"
},
"featuredTags": {
"@id": "toot:featuredTags",
"@type": "@id"
},
"fingerprintKey": {
"@id": "toot:fingerprintKey",
"@type": "@id"
},
"focalPoint": {
"@container": "@list",
"@id": "toot:focalPoint"
},
"identityKey": {
"@id": "toot:identityKey",
"@type": "@id"
},
"indexable": "toot:indexable",
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"memorial": "toot:memorial",
"messageFranking": "toot:messageFranking",
"messageType": "toot:messageType",
"movedTo": {
"@id": "as:movedTo",
"@type": "@id"
},
"publicKeyBase64": "toot:publicKeyBase64",
"schema": "http://schema.org#",
"suspended": "toot:suspended",
"toot": "http://joinmastodon.org/ns#",
"value": "schema:value"
}
],
"attachment": [
{
"name": "Website",
"type": "PropertyValue",
"value": "<a href=\"https://bastianallgeier.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"\">bastianallgeier.com</span><span class=\"invisible\"></span></a>"
},
{
"name": "Project",
"type": "PropertyValue",
"value": "<a href=\"https://getkirby.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"\">getkirby.com</span><span class=\"invisible\"></span></a>"
},
{
"name": "Github",
"type": "PropertyValue",
"value": "<a href=\"https://github.com/bastianallgeier\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"\">github.com/bastianallgeier</span><span class=\"invisible\"></span></a>"
}
],
"devices": "https://mastodon.social/users/bastianallgeier/collections/devices",
"discoverable": true,
"endpoints": {
"sharedInbox": "https://mastodon.social/inbox"
},
"featured": "https://mastodon.social/users/bastianallgeier/collections/featured",
"featuredTags": "https://mastodon.social/users/bastianallgeier/collections/tags",
"followers": "https://mastodon.social/users/bastianallgeier/followers",
"following": "https://mastodon.social/users/bastianallgeier/following",
"icon": {
"mediaType": "image/jpeg",
"type": "Image",
"url": "https://files.mastodon.social/accounts/avatars/000/007/393/original/0180a20079617c71.jpg"
},
"id": "https://mastodon.social/users/bastianallgeier",
"image": {
"mediaType": "image/jpeg",
"type": "Image",
"url": "https://files.mastodon.social/accounts/headers/000/007/393/original/13d644ab46d50478.jpeg"
},
"inbox": "https://mastodon.social/users/bastianallgeier/inbox",
"indexable": false,
"manuallyApprovesFollowers": false,
"memorial": false,
"name": "Bastian Allgeier",
"outbox": "https://mastodon.social/users/bastianallgeier/outbox",
"preferredUsername": "bastianallgeier",
"publicKey": {
"id": "https://mastodon.social/users/bastianallgeier#main-key",
"owner": "https://mastodon.social/users/bastianallgeier",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3fz+hpgVztO9z6HUhyzv\nwP++ERBBoIwSLKf1TyIM8bvzGFm2YXaO5uxu1HvumYFTYc3ACr3q4j8VUb7NMxkQ\nlzu4QwPjOFJ43O+fY+HSPORXEDW5fXDGC5DGpox4+i08LxRmx7L6YPRUSUuPN8nI\nWyq1Qsq1zOQrNY/rohMXkBdSXxqC3yIRqvtLt4otCgay/5tMogJWkkS6ZKyFhb9z\nwVVy1fsbV10c9C+SHy4NH26CKaTtpTYLRBMjhTCS8bX8iDSjGIf2aZgYs1ir7gEz\n9wf5CvLiENmVWGwm64t6KSEAkA4NJ1hzgHUZPCjPHZE2SmhO/oHaxokTzqtbbENJ\n1QIDAQAB\n-----END PUBLIC KEY-----\n"
},
"published": "2016-11-01T00:00:00Z",
"summary": "<p>Designer &amp; developer. Creator of Kirby CMS</p>",
"tag": [],
"type": "Person",
"url": "https://mastodon.social/@bastianallgeier"
}

View file

@ -1,62 +1,109 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"claim": {
"@id": "toot:claim",
"@type": "@id"
},
"memorial": "toot:memorial",
"atomUri": "ostatus:atomUri",
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"blurhash": "toot:blurhash",
"conversation": "ostatus:conversation",
"ostatus": "http://ostatus.org#",
"discoverable": "toot:discoverable",
"focalPoint": {
"@container": "@list",
"@id": "toot:focalPoint"
},
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
"ostatus": "http://ostatus.org#",
"votersCount": "toot:votersCount",
"Hashtag": "as:Hashtag",
"Emoji": "toot:Emoji",
"alsoKnownAs": {
"@id": "as:alsoKnownAs",
"@type": "@id"
},
"sensitive": "as:sensitive",
"movedTo": {
"@id": "as:movedTo",
"@type": "@id"
},
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
"conversation": "ostatus:conversation",
"Device": "toot:Device",
"schema": "http://schema.org#",
"toot": "http://joinmastodon.org/ns#",
"votersCount": "toot:votersCount"
"cipherText": "toot:cipherText",
"suspended": "toot:suspended",
"messageType": "toot:messageType",
"featuredTags": {
"@id": "toot:featuredTags",
"@type": "@id"
},
"Curve25519Key": "toot:Curve25519Key",
"deviceId": "toot:deviceId",
"Ed25519Signature": "toot:Ed25519Signature",
"featured": {
"@id": "toot:featured",
"@type": "@id"
},
"devices": {
"@id": "toot:devices",
"@type": "@id"
},
"value": "schema:value",
"PropertyValue": "schema:PropertyValue",
"messageFranking": "toot:messageFranking",
"publicKeyBase64": "toot:publicKeyBase64",
"identityKey": {
"@id": "toot:identityKey",
"@type": "@id"
},
"Ed25519Key": "toot:Ed25519Key",
"indexable": "toot:indexable",
"EncryptedMessage": "toot:EncryptedMessage",
"fingerprintKey": {
"@id": "toot:fingerprintKey",
"@type": "@id"
}
}
],
"atomUri": "https://chaos.social/users/distantnative/statuses/109336635639931467",
"attachment": [
{
"blurhash": "UAK1zS00OXIUxuMxIUM{?b-:-;W:Di?b%2M{",
"height": 960,
"mediaType": "image/jpeg",
"name": null,
"type": "Document",
"url": "https://assets.chaos.social/media_attachments/files/109/336/634/286/114/657/original/2e6122063d8bfb26.jpeg",
"width": 346
}
],
"attributedTo": "https://chaos.social/users/distantnative",
"cc": [
"https://chaos.social/users/distantnative/followers"
],
"content": "<p>Favorite piece of anthropology meta discourse.</p>",
"contentMap": {
"en": "<p>Favorite piece of anthropology meta discourse.</p>"
},
"conversation": "tag:chaos.social,2022-11-13:objectId=71843781:objectType=Conversation",
"id": "https://chaos.social/users/distantnative/statuses/109336635639931467",
"actor": "https://phpc.social/users/denniskoch",
"cc": [],
"id": "https://phpc.social/users/denniskoch/statuses/112847382711461301/activity",
"inReplyTo": null,
"inReplyToAtomUri": null,
"published": "2022-11-13T13:04:20Z",
"replies": {
"first": {
"items": [],
"next": "https://chaos.social/users/distantnative/statuses/109336635639931467/replies?only_other_accounts=true&page=true",
"partOf": "https://chaos.social/users/distantnative/statuses/109336635639931467/replies",
"type": "CollectionPage"
"object": {
"atomUri": "https://phpc.social/users/denniskoch/statuses/112847382711461301",
"attachment": [],
"attributedTo": "https://phpc.social/users/denniskoch",
"cc": [],
"content": "<p><span class=\"h-card\" translate=\"no\"><a href=\"https://mastodon.social/@bastianallgeier\" class=\"u-url mention\">@<span>bastianallgeier</span></a></span> <span class=\"h-card\" translate=\"no\"><a href=\"https://chaos.social/@distantnative\" class=\"u-url mention\">@<span>distantnative</span></a></span> <span class=\"h-card\" translate=\"no\"><a href=\"https://fosstodon.org/@kev\" class=\"u-url mention\">@<span>kev</span></a></span> Another main argument: Discord is popular. Many people have an account, so you can just join an server quickly. Also you know the app and how to get around.</p>",
"contentMap": {
"en": "<p><span class=\"h-card\" translate=\"no\"><a href=\"https://mastodon.social/@bastianallgeier\" class=\"u-url mention\">@<span>bastianallgeier</span></a></span> <span class=\"h-card\" translate=\"no\"><a href=\"https://chaos.social/@distantnative\" class=\"u-url mention\">@<span>distantnative</span></a></span> <span class=\"h-card\" translate=\"no\"><a href=\"https://fosstodon.org/@kev\" class=\"u-url mention\">@<span>kev</span></a></span> Another main argument: Discord is popular. Many people have an account, so you can just join an server quickly. Also you know the app and how to get around.</p>"
},
"id": "https://chaos.social/users/distantnative/statuses/109336635639931467/replies",
"type": "Collection"
"conversation": "tag:mastodon.social,2024-07-25:objectId=760068442:objectType=Conversation",
"id": "https://phpc.social/users/denniskoch/statuses/112847382711461301",
"published": "2024-07-25T13:33:29Z",
"replies": null,
"sensitive": false,
"tag": [],
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"type": "Note",
"url": "https://phpc.social/@denniskoch/112847382711461301"
},
"published": "2024-07-25T13:33:29Z",
"signature": {
"created": "2024-07-25T13:33:29Z",
"creator": "https://phpc.social/users/denniskoch#main-key",
"signatureValue": "slz9BKJzd2n1S44wdXGOU+bV/wsskdgAaUpwxj8R16mYOL8+DTpE6VnfSKoZGsBBJT8uG5gnVfVEz1YsTUYtymeUgLMh7cvd8VnJnZPS+oixbmBRVky/Myf91TEgQQE7G4vDmTdB4ii54hZrHcOOYYf5FKPNRSkMXboKA6LMqNtekhbI+JTUJYIB02WBBK6PUyo15f6B1RJ6HGWVgud9NE0y1EZXfrkqUt682p8/9D49ORf7AwjXUJibKic2RbPvhEBj70qUGfBm4vvgdWhSUn1IG46xh+U0+NrTSUED82j1ZVOeua/2k/igkGs8cSBkY35quXTkPz6gbqCCH66CuA==",
"type": "RsaSignature2017"
},
"sensitive": false,
"summary": null,
"tag": [],
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"type": "Note",
"url": "https://chaos.social/@distantnative/109336635639931467"
"type": "Create"
}

View file

@ -41,6 +41,10 @@ defmodule Pleroma.HTMLTest do
<span class="h-card"><a class="u-url mention animate-spin">@<span>foo</span></a></span>
"""
@mention_hashtags_sample """
<a href="https://mastodon.example/tags/linux" class="mention hashtag" rel="tag">#<span>linux</span></a>
"""
describe "StripTags scrubber" do
test "works as expected" do
expected = """
@ -126,6 +130,15 @@ defmodule Pleroma.HTMLTest do
Pleroma.HTML.Scrubber.TwitterText
)
end
test "does allow mention hashtags" do
expected = """
<a href="https://mastodon.example/tags/linux" class="mention hashtag" rel="tag">#<span>linux</span></a>
"""
assert expected ==
HTML.filter_tags(@mention_hashtags_sample, Pleroma.HTML.Scrubber.Default)
end
end
describe "default scrubber" do
@ -189,6 +202,15 @@ defmodule Pleroma.HTMLTest do
Pleroma.HTML.Scrubber.Default
)
end
test "does allow mention hashtags" do
expected = """
<a href="https://mastodon.example/tags/linux" class="mention hashtag" rel="tag">#<span>linux</span></a>
"""
assert expected ==
HTML.filter_tags(@mention_hashtags_sample, Pleroma.HTML.Scrubber.Default)
end
end
describe "extract_first_external_url_from_object" do

View file

@ -657,7 +657,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
end
test "without valid signature, " <>
"it only accepts Create activities and requires enabled federation",
"it accepts Create activities and requires enabled federation",
%{conn: conn} do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Jason.decode!()
@ -684,6 +684,54 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|> json_response(400)
end
# When activity is delivered to the inbox and we cannot immediately verify signature
# we capture all the params and process it later in the Oban job.
# Once we begin processing it through Oban we risk fetching the actor to validate the
# activity which just leads to inserting a new user to process a Delete not relevant to us.
test "Activities of certain types from an unknown actor are discarded", %{conn: conn} do
example_bad_types =
Pleroma.Constants.activity_types() --
Pleroma.Constants.allowed_activity_types_from_strangers()
Enum.each(example_bad_types, fn bad_type ->
params =
%{
"type" => bad_type,
"actor" => "https://unknown.mastodon.instance/users/somebody"
}
|> Jason.encode!()
conn
|> assign(:valid_signature, false)
|> put_req_header("content-type", "application/activity+json")
|> post("/inbox", params)
|> json_response(400)
assert all_enqueued() == []
end)
end
test "Unknown activity types are discarded", %{conn: conn} do
unknown_types = ["Poke", "Read", "Dazzle"]
Enum.each(unknown_types, fn bad_type ->
params =
%{
"type" => bad_type,
"actor" => "https://unknown.mastodon.instance/users/somebody"
}
|> Jason.encode!()
conn
|> assign(:valid_signature, true)
|> put_req_header("content-type", "application/activity+json")
|> post("/inbox", params)
|> json_response(400)
assert all_enqueued() == []
end)
end
test "accepts Add/Remove activities", %{conn: conn} do
object_id = "c61d6733-e256-4fe1-ab13-1e369789423f"

View file

@ -232,12 +232,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert user.avatar == %{
"type" => "Image",
"url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}]
"url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}],
"name" => "profile picture"
}
assert user.banner == %{
"type" => "Image",
"url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}]
"url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}],
"name" => "profile picture"
}
end
@ -432,6 +434,35 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert user.birthday == ~D[2001-02-12]
end
test "fetches avatar description" do
user_id = "https://example.com/users/marcin"
user_data =
"test/fixtures/users_mock/user.json"
|> File.read!()
|> String.replace("{{nickname}}", "marcin")
|> Jason.decode!()
|> Map.delete("featured")
|> Map.update("icon", %{}, fn image -> Map.put(image, "name", "image description") end)
|> Jason.encode!()
Tesla.Mock.mock(fn
%{
method: :get,
url: ^user_id
} ->
%Tesla.Env{
status: 200,
body: user_data,
headers: [{"content-type", "application/activity+json"}]
}
end)
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
assert user.avatar["name"] == "image description"
end
end
test "it fetches the appropriate tag-restricted posts" do

View file

@ -252,6 +252,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_message = build_remote_message()
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
assert SimplePolicy.id_filter(remote_message["actor"])
end
test "activity has a matching host" do
@ -260,6 +261,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_message = build_remote_message()
assert {:reject, _} = SimplePolicy.filter(remote_message)
refute SimplePolicy.id_filter(remote_message["actor"])
end
test "activity matches with wildcard domain" do
@ -268,6 +270,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_message = build_remote_message()
assert {:reject, _} = SimplePolicy.filter(remote_message)
refute SimplePolicy.id_filter(remote_message["actor"])
end
test "actor has a matching host" do
@ -276,6 +279,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_user = build_remote_user()
assert {:reject, _} = SimplePolicy.filter(remote_user)
refute SimplePolicy.id_filter(remote_user["id"])
end
test "reject Announce when object would be rejected" do
@ -288,6 +292,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
}
assert {:reject, _} = SimplePolicy.filter(announce)
# Note: Non-Applicable for id_filter/1
end
test "reject by URI object" do
@ -300,6 +305,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
}
assert {:reject, _} = SimplePolicy.filter(announce)
# Note: Non-Applicable for id_filter/1
end
end
@ -370,6 +376,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
assert SimplePolicy.id_filter(local_message["actor"])
assert SimplePolicy.id_filter(remote_message["actor"])
end
test "is not empty but activity doesn't have a matching host" do
@ -380,6 +388,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert {:reject, _} = SimplePolicy.filter(remote_message)
assert SimplePolicy.id_filter(local_message["actor"])
refute SimplePolicy.id_filter(remote_message["actor"])
end
test "activity has a matching host" do
@ -390,6 +400,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
assert SimplePolicy.id_filter(local_message["actor"])
assert SimplePolicy.id_filter(remote_message["actor"])
end
test "activity matches with wildcard domain" do
@ -400,6 +412,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
assert SimplePolicy.id_filter(local_message["actor"])
assert SimplePolicy.id_filter(remote_message["actor"])
end
test "actor has a matching host" do
@ -408,6 +422,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_user = build_remote_user()
assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
assert SimplePolicy.id_filter(remote_user["id"])
end
end

View file

@ -68,6 +68,23 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
result = UserView.render("user.json", %{user: user})
assert result["icon"]["url"] == "https://someurl"
assert result["image"]["url"] == "https://somebanner"
refute result["icon"]["name"]
refute result["image"]["name"]
end
test "Avatar has a description if the user set one" do
user =
insert(:user,
avatar: %{
"url" => [%{"href" => "https://someurl"}],
"name" => "a drawing of pleroma-tan using pleroma groups"
}
)
result = UserView.render("user.json", %{user: user})
assert result["icon"]["name"] == "a drawing of pleroma-tan using pleroma groups"
end
test "renders an invisible user with the invisible property set to true" do

View file

@ -430,6 +430,75 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do
assert :ok == File.rm(Path.absname("test/tmp/large_binary.data"))
end
test "adds avatar description with a new avatar", %{user: user, conn: conn} do
new_avatar = %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
res =
patch(conn, "/api/v1/accounts/update_credentials", %{
"avatar" => new_avatar,
"avatar_description" => "me and pleroma tan"
})
assert json_response_and_validate_schema(res, 200)
user = User.get_by_id(user.id)
assert user.avatar["name"] == "me and pleroma tan"
end
test "adds avatar description to existing avatar", %{user: user, conn: conn} do
new_avatar = %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
assert user.avatar == %{}
conn
|> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
assert conn
|> assign(:user, User.get_by_id(user.id))
|> patch("/api/v1/accounts/update_credentials", %{
"avatar_description" => "me and pleroma tan"
})
|> json_response_and_validate_schema(200)
user = User.get_by_id(user.id)
assert user.avatar["name"] == "me and pleroma tan"
end
test "limit", %{user: user, conn: conn} do
new_header = %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
assert user.banner == %{}
conn
|> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
description_limit = Config.get([:instance, :description_limit], 100)
description = String.duplicate(".", description_limit + 1)
conn =
conn
|> assign(:user, User.get_by_id(user.id))
|> patch("/api/v1/accounts/update_credentials", %{
"header_description" => description
})
assert %{"error" => "Banner description is too long"} =
json_response_and_validate_schema(conn, 413)
end
test "Strip / from upload files", %{user: user, conn: conn} do
new_image = %Plug.Upload{
content_type: "image/jpeg",

View file

@ -96,7 +96,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
hide_follows_count: false,
relationship: %{},
skip_thread_containment: false,
accepts_chat_messages: nil
accepts_chat_messages: nil,
avatar_description: "",
header_description: ""
}
}
@ -340,7 +342,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
hide_follows_count: false,
relationship: %{},
skip_thread_containment: false,
accepts_chat_messages: nil
accepts_chat_messages: nil,
avatar_description: "",
header_description: ""
}
}
@ -456,6 +460,45 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
test_relationship_rendering(user, other_user, expected)
end
test "relationship does not indicate following if a FollowingRelationship is missing" do
user = insert(:user)
other_user = insert(:user, local: false)
# Create a follow relationship with the real Follow Activity and Accept it
assert {:ok, _, _, _} = CommonAPI.follow(other_user, user)
assert {:ok, _} = CommonAPI.accept_follow_request(user, other_user)
assert %{data: %{"state" => "accept"}} =
Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, other_user)
# Fetch the relationship and forcibly delete it to simulate
# a Follow Accept that did not complete processing
%{following_relationships: [relationship]} =
Pleroma.UserRelationship.view_relationships_option(user, [other_user])
assert {:ok, _} = Pleroma.Repo.delete(relationship)
assert %{following_relationships: [], user_relationships: []} ==
Pleroma.UserRelationship.view_relationships_option(user, [other_user])
expected =
Map.merge(
@blank_response,
%{
following: false,
followed_by: false,
muting: false,
muting_notifications: false,
subscribing: false,
notifying: false,
showing_reblogs: true,
id: to_string(other_user.id)
}
)
test_relationship_rendering(user, other_user, expected)
end
test "represent a relationship for the blocking and blocked user" do
user = insert(:user)
other_user = insert(:user)

View file

@ -56,6 +56,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
expected = %{
id: to_string(notification.id),
group_key: "ungrouped-#{to_string(notification.id)}",
pleroma: %{is_seen: false, is_muted: false},
type: "pleroma:chat_mention",
account: AccountView.render("show.json", %{user: user, for: recipient}),
@ -75,6 +76,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
expected = %{
id: to_string(notification.id),
group_key: "ungrouped-#{to_string(notification.id)}",
pleroma: %{is_seen: false, is_muted: false},
type: "mention",
account:
@ -99,6 +101,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
expected = %{
id: to_string(notification.id),
group_key: "ungrouped-#{to_string(notification.id)}",
pleroma: %{is_seen: false, is_muted: false},
type: "favourite",
account: AccountView.render("show.json", %{user: another_user, for: user}),
@ -119,6 +122,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
expected = %{
id: to_string(notification.id),
group_key: "ungrouped-#{to_string(notification.id)}",
pleroma: %{is_seen: false, is_muted: false},
type: "reblog",
account: AccountView.render("show.json", %{user: another_user, for: user}),
@ -137,6 +141,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
expected = %{
id: to_string(notification.id),
group_key: "ungrouped-#{to_string(notification.id)}",
pleroma: %{is_seen: false, is_muted: false},
type: "follow",
account: AccountView.render("show.json", %{user: follower, for: followed}),
@ -165,6 +170,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
expected = %{
id: to_string(notification.id),
group_key: "ungrouped-#{to_string(notification.id)}",
pleroma: %{is_seen: false, is_muted: false},
type: "move",
account: AccountView.render("show.json", %{user: old_user, for: follower}),
@ -190,6 +196,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
expected = %{
id: to_string(notification.id),
group_key: "ungrouped-#{to_string(notification.id)}",
pleroma: %{is_seen: false, is_muted: false},
type: "pleroma:emoji_reaction",
emoji: "",
@ -229,6 +236,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
expected = %{
id: to_string(notification.id),
group_key: "ungrouped-#{to_string(notification.id)}",
pleroma: %{is_seen: false, is_muted: false},
type: "pleroma:emoji_reaction",
emoji: ":dinosaur:",
@ -248,6 +256,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
expected = %{
id: to_string(notification.id),
group_key: "ungrouped-#{to_string(notification.id)}",
pleroma: %{is_seen: false, is_muted: false},
type: "poll",
account:
@ -274,6 +283,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
expected = %{
id: to_string(notification.id),
group_key: "ungrouped-#{to_string(notification.id)}",
pleroma: %{is_seen: false, is_muted: false},
type: "pleroma:report",
account: AccountView.render("show.json", %{user: reporting_user, for: moderator_user}),
@ -300,6 +310,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
expected = %{
id: to_string(notification.id),
group_key: "ungrouped-#{to_string(notification.id)}",
pleroma: %{is_seen: false, is_muted: false},
type: "update",
account: AccountView.render("show.json", %{user: user, for: repeat_user}),
@ -322,6 +333,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
expected = %{
id: to_string(notification.id),
group_key: "ungrouped-#{to_string(notification.id)}",
pleroma: %{is_seen: true, is_muted: true},
type: "favourite",
account: AccountView.render("show.json", %{user: another_user, for: user}),
@ -345,6 +357,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
expected = %{
id: to_string(notification.id),
group_key: "ungrouped-#{to_string(notification.id)}",
pleroma: %{is_seen: false, is_muted: false},
type: "status",
account:

Some files were not shown because too many files have changed in this diff Show more