Compare commits

...
Sign in to create a new pull request.

232 commits

Author SHA1 Message Date
KMY(雪あすか)
8b6e55b8dc
Bump version to 5.26 LTS (#895) 2024-10-20 19:11:57 +09:00
KMY(雪あすか)
6a9212459f
Fix: 名前空間付きのEmojiReactアクティビティを受信 (#888) (#894) 2024-10-20 19:11:45 +09:00
KMY(雪あすか)
46fec44ecc
Merge pull request #856 from kmycode/kb-draft-5.25-lts
Release: 5.25 LTS
2024-10-01 08:16:58 +09:00
KMY
898e89bfad Bump version to 5.25 LTS 2024-09-30 21:44:45 +09:00
KMY
c33fea88b2 Merge remote-tracking branch 'parent/stable-4.2' into kb-draft-5.25-lts 2024-09-30 21:41:34 +09:00
Claire
7e47439787 Bump version to 4.2.13 2024-09-30 13:28:03 +02:00
Claire
245a74f9ca Add “A Mastodon update is available.” message on admin dashboard for non-bugfix updates (#32106) 2024-09-30 13:28:03 +02:00
Claire
d2842db18d Ignore CVE-2024-8796, which does not impact us 2024-09-30 13:28:03 +02:00
Claire
346c37df80 Fix replies collection being cached improperly 2024-09-30 13:28:03 +02:00
Claire
20f06798a0 Change Mastodon to issue correctly-signed queries by default (#31994) 2024-09-30 13:28:03 +02:00
Claire
e66aaee1a4 Fix security context sometimes not being added in LD-Signed activities (#31871) 2024-09-30 13:28:03 +02:00
Claire
9bfbba3224 Fix issue when encountering reblog of deleted post in feed rebuild (#32001) 2024-09-30 13:28:03 +02:00
Claire
378af3a0a0 Update dependency fugit 2024-09-30 13:28:03 +02:00
Claire
d096965eec Update dependency puma 2024-09-30 13:28:03 +02:00
Claire
2abaa9b68a Update dependency omniauth-saml 2024-09-30 13:28:03 +02:00
Claire
df36f12d46 Update dependency ruby-saml 2024-09-30 13:28:03 +02:00
David Roetzel
0e8f23ebee
Merge commit from fork
This should not change the set of words matched by `USERNAME_RE` but does
change the one matched by `MENTION_RE`. Indeed, the previous regexp allowed
a domain part to start with `.` or `-`, which the new regexp does not allow.

Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2024-09-30 12:25:54 +02:00
KMY(雪あすか)
3c851f2d21
Merge pull request #843 from kmycode/kb-draft-5.24-lts
Release: 5.24
2024-09-10 12:29:28 +09:00
KMY(雪あすか)
af63f9564c
Merge commit from fork
Fix: LD Signaturesで署名された投稿の検索許可(検索範囲)が改竄できる問題
2024-09-10 12:01:59 +09:00
KMY
b6278e0d39 Fix: LD Signaturesで署名された投稿の検索許可(検索範囲)が改竄できる問題 2024-09-10 07:23:17 +09:00
KMY
ec9644b9a6 Bump version to 5.24 LTS 2024-09-10 06:56:10 +09:00
KMY(雪あすか)
4e94e72911
Merge pull request #806 from kmycode/kb-draft-5.23-lts
Release: 5.23 LTS
2024-08-19 19:04:29 +09:00
KMY
836c02d816 Bump version to 5.23 LTS 2024-08-19 18:45:19 +09:00
KMY
8bc6f3c73c Merge commit 'f9a929ed5c' into kb-draft-5.23-lts 2024-08-19 18:42:48 +09:00
Claire
f9a929ed5c
Bump version to v4.2.12 (#31491) 2024-08-19 11:13:45 +02:00
Claire
d675803f07
Fix broken notifications for mentions from local moderators in 4.2.11 (#31484) 2024-08-19 09:52:32 +02:00
KMY(雪あすか)
a2d3de48f4
Merge pull request #797 from kmycode/kb-draft-5.22-lts
Release: 5.22 LTS
2024-08-17 07:26:46 +09:00
KMY(雪あすか)
a53ec866d5
Bump version to 5.22-lts 2024-08-16 22:19:52 +09:00
KMY
3ab66262de Merge commit 'a02ff33f0e' into kb-draft-5.22-lts 2024-08-16 22:15:12 +09:00
Claire
a02ff33f0e Bump version to v4.2.11 2024-08-16 12:30:59 +02:00
Claire
a652293842 Update dependenxy rexml 2024-08-16 12:30:59 +02:00
Matt Jankowski
63ad8254ff Fix mastodon:stats decoration of stats rake task (#31104) 2024-08-16 12:30:59 +02:00
Jeong Arm
8fe1cefe4c Handle featured collections without items (#27581) 2024-08-16 12:30:59 +02:00
Claire
86f15cef66 Change search popout to not list unusable search options when logged out (#27918) 2024-08-16 12:30:59 +02:00
June
49820ecefa Fix not all legal images showing in file picker when uploading custom emoji (#28076) 2024-08-16 12:30:59 +02:00
Jonathan de Jong
9d2e59bb45 Fix error when encountering malformed Tag objects from Kbin (#28235) 2024-08-16 12:30:59 +02:00
Michael Stanclift
6fcb1f5799 Fix OCR when using S3/CDN for assets (#28551) 2024-08-16 12:30:59 +02:00
Claire
297ad9aeb8 Fix already-invalid reports failing to resolve (#29027) 2024-08-16 12:30:59 +02:00
Claire
e1be281e3d Fix report reason selector in moderation interface not unselecting rules when changing category (#29026) 2024-08-16 12:30:59 +02:00
Claire
c06436eb91 Fix development environment admin account not being auto-approved (#29958) 2024-08-16 12:30:59 +02:00
Râu Cao
d1854798c9 Fix local account search on LDAP login being case-sensitive (#30113)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2024-08-16 12:30:59 +02:00
Claire
a2c7f7f690 Fix division by zero on some video/GIF files (#30600) 2024-08-16 12:30:59 +02:00
Matt Jankowski
6f2a3fa5d1 Restore verbose option to media remove cli (#30536) 2024-08-16 12:30:59 +02:00
Adam Niedzielski
7b7d404efe Fix ß bug in regexp for mentions and tags (#31122) 2024-08-16 12:30:59 +02:00
Claire
0fc738a323 Fix hashtag matching pattern matching some link anchors (#30190) 2024-08-16 12:30:59 +02:00
Claire
5cb36daa0f Fix Web UI trying to save user settings when logged out (#30324) 2024-08-16 12:30:59 +02:00
Claire
a8039dda13 Fix click event handling when clicking outside of an open dropdown menu (#31251) 2024-08-16 12:30:59 +02:00
Claire
0a345ad5e1 Fix logic of block/mute bypass for mentions from moderators (#31271) 2024-08-16 12:30:59 +02:00
Django
29c35ef4f9 Add support for incoming <s> tag (#31375) 2024-08-16 12:30:59 +02:00
Claire
13bab94265 Fix duplicate orderedItems in user archive's outbox.json (#31099) 2024-08-16 12:30:59 +02:00
Adam Niedzielski
161aa0f8f6 Select correct self link when parsing Webfinger response (#31110) 2024-08-16 12:30:59 +02:00
Claire
fe92b241b2 Fix status processing failing halfway when a remote post has a malformed replies attribute (#31246) 2024-08-16 12:30:59 +02:00
Claire
a5641a9244 Fix incorrect rate limit on PUT requests (#31356) 2024-08-16 12:30:59 +02:00
KMY(雪あすか)
31ad8c7fdf
Merge pull request #770 from kmycode/kb-draft-5.21-lts
Release: 5.21-lts
2024-07-05 07:35:58 +09:00
KMY
77b7b8caaa Bump version to 5.21-lts 2024-07-05 06:41:03 +09:00
KMY
6896542a76 Merge commit 'a5b4a2b7e7' into kb-draft-5.21-lts 2024-07-05 06:40:30 +09:00
Claire
a5b4a2b7e7
Bump version to v4.2.10 (#30910) 2024-07-04 16:46:35 +02:00
Claire
d4bf22b632
Merge pull request from GHSA-xjvf-fm67-4qc3 2024-07-04 16:45:52 +02:00
Claire
4fb4721072
Merge pull request from GHSA-58x8-3qxw-6hm7
* Fix insufficient permission checking for public timeline endpoints

Note that this changes unauthenticated access failure code from 401 to 422

* Add more tests for public timelines

* Require user token in `/api/v1/statuses/:id/translate` and `/api/v1/scheduled_statuses`
2024-07-04 16:26:49 +02:00
Claire
df974a912b
Merge pull request from GHSA-vp5r-5pgw-jwqx
* Fix streaming sessions not being closed when revoking access to an app

* Add tests for GHSA-7w3c-p9j8-mq3x
2024-07-04 16:11:28 +02:00
Claire
6cd9bd6ae1 fix: Return HTTP 422 when scheduled status time is less than 5 minutes (#30584) 2024-07-03 10:57:46 +02:00
David Roetzel
9b6219c48f Improve encoding detection for link cards (#30780) 2024-07-03 10:57:46 +02:00
Eugen Rochko
88b2d6eca5 Change search modifiers to be case-insensitive (#30865) 2024-07-03 10:57:46 +02:00
David Roetzel
846f59c6e9 Add size limit for link preview URLs (#30854) 2024-07-03 10:57:46 +02:00
Tim Rogers
17f69c0002 Added check for STATSD_ADDR setting to emit a warning and proceed rather than crashing if the address is unreachable (#30691) 2024-07-02 15:08:24 +02:00
Claire
1e87634a43 Update dependency charlock_holmes 2024-07-02 15:08:24 +02:00
Claire
5fd7cd79e0 Specify yarn version to avoid confusion with main which uses Yarn 4 2024-07-02 15:08:24 +02:00
Claire
fcae9435ec Fix /admin/accounts/:account_id/statuses/:id for edited posts with media attachments (#30819) 2024-07-02 15:08:24 +02:00
Claire
55408f8085 Update dependency cbor 2024-07-02 15:08:24 +02:00
Claire
3f75c6f048 Update dependency rails 2024-07-02 15:08:24 +02:00
Claire
bfc287fd6b Remove dependency on posix-spawn (#18559) 2024-07-02 15:08:24 +02:00
Claire
19ed22dc58 Fix duplicate @context attribute in user export (#30653) 2024-06-18 15:37:41 +02:00
Claire
520b2086af Change PWA start URL from /home to / (#27377) 2024-06-18 15:37:41 +02:00
KMY(雪あすか)
e925fd6802
Merge pull request #753 from kmycode/kbtopic-fix-emoji-reaction-rack-attack-for-lts
Release: 5.20 LTS
2024-06-03 07:44:23 +09:00
KMY
75d7e4fbdd Bump version to 5.20 LTS 2024-06-02 11:21:40 +09:00
KMY
e511b02de5 Fix: 絵文字リアクションに厳しいレートリミットが適用される問題 2024-06-02 11:18:01 +09:00
KMY(雪あすか)
441c8712e1
Merge pull request #749 from kmycode/kb-draft-5.19-lts
Release: 5.19 LTS
2024-05-31 08:32:45 +09:00
KMY
cc2bfe5188 Bump version to 5.19 LTS 2024-05-30 23:26:15 +09:00
KMY
c2a19f8a81 Merge remote-tracking branch 'parent/stable-4.2' into kb-draft-5.19-lts 2024-05-30 23:25:06 +09:00
Claire
c93aacafde
Bump version to v4.2.9 (#30470) 2024-05-30 15:34:50 +02:00
Claire
9740c7eaea Fix rate-limiting incorrectly triggering a session cookie on most endpoints (#30483) 2024-05-30 15:14:03 +02:00
Claire
8ab0ca7d64
Merge pull request from GHSA-c2r5-cfqr-c553
* Add hardening monkey-patch to prevent IP spoofing on misconfigured installations

* Remove rack-attack safelist
2024-05-30 14:24:29 +02:00
Claire
7920aa59e8
Merge pull request from GHSA-q3rg-xx5v-4mxh 2024-05-30 14:14:04 +02:00
Claire
943792c187
Merge pull request from GHSA-5fq7-3p3j-9vrf 2024-05-30 14:03:13 +02:00
Emelia Smith
186f916192 Fix: remove broken OAuth Application vacuuming & throttle OAuth Application registrations (#30316)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2024-05-29 16:39:26 +02:00
Claire
f9c41ae43b Normalize language code of incoming posts (#30403) 2024-05-29 15:31:26 +02:00
Claire
b8edc95e8a Fix leaking Elasticsearch connections in Sidekiq processes (#30450) 2024-05-29 15:31:26 +02:00
Claire
16213a678d Update dependency rexml to 3.2.8 2024-05-29 15:31:26 +02:00
Claire
a8dd32102f Update dependency nokogiri to 1.16.5 2024-05-17 12:30:00 +02:00
Claire
6fc07ff31f Update dependency fastimage to 2.3.1 2024-05-17 12:30:00 +02:00
Claire
997b021b69 Update dependency rotp to 6.3.0 2024-05-17 12:30:00 +02:00
Claire
2865bfadaf Update dependency json-jwt to 1.15.3.1 2024-05-17 12:30:00 +02:00
Claire
8c72e80019 Update dependency rack-cors to 2.0.2 2024-05-17 12:30:00 +02:00
Claire
8cf78825a2 Fix off-by-one in tootctl media commands (#30306) 2024-05-17 12:30:00 +02:00
Emelia Smith
67b2e62331 Fix missing destory audit logs for Domain Allows (#30125) 2024-05-17 12:30:00 +02:00
Claire
56b7d1a7b6 Fix not being able to block a subdomain of an already-blocked domain through the API (#30119) 2024-05-17 12:30:00 +02:00
Claire
51ef619140 Fix Idempotency-Key ignored when scheduling a post (#30084) 2024-05-17 12:30:00 +02:00
Tim Rogers
e69780ec59 Fixed crash when supplying FFMPEG_BINARY environment variable (#30022) 2024-05-17 12:30:00 +02:00
Claire
c3be5a3d2e Remove caching in cache_collection (#29862) 2024-05-17 12:30:00 +02:00
Claire
86807e4799 Improve email address validation (#29838) 2024-05-17 12:30:00 +02:00
Matt Jankowski
0143c9d3e1 Fix results/query in api/v1/featured_tags/suggestions (#29597) 2024-05-17 12:30:00 +02:00
Jeong Arm
ab3f9852f2 Normalize idna domain before account unblock domain (#29530) 2024-05-17 12:30:00 +02:00
Claire
7af69f5cf5 Fix admin account created by mastodon:setup not being auto-approved (#29379) 2024-05-17 12:30:00 +02:00
Emelia Smith
f784213c64 Return domain block digests from admin domain blocks API (#29092) 2024-05-17 12:30:00 +02:00
Claire
6536d96d1b Add fallback redirection when getting a webfinger query WEB_DOMAIN@WEB_DOMAIN (#28592) 2024-05-17 12:30:00 +02:00
Matt Jankowski
ed8e4bab4c Fix reference to non-existent var in CLI maintenance command (#28363) 2024-05-17 12:30:00 +02:00
KMY(雪あすか)
6a5ea61928
Merge pull request #606 from kmycode/kb-draft-5.18-lts
Release: 5.18 LTS
2024-02-24 08:53:57 +09:00
KMY
c329c56625 Bump version to 5.18 LTS 2024-02-24 08:41:19 +09:00
KMY
6ba77a9feb Merge remote-tracking branch 'parent/stable-4.2' into kb-draft-5.18-lts 2024-02-24 08:38:12 +09:00
KMY
631a037524 Fix: 公開範囲「ログインユーザーのみ」翻訳 2024-02-24 08:33:54 +09:00
Claire
bdb6650ebc
Bump version to v4.2.8 (#29370) 2024-02-23 14:09:41 +01:00
Claire
f3ad918950
Fix processing of Link objects in Image objects (#29363) 2024-02-23 09:53:04 +01:00
Claire
9a7802655f
Fix link verifications when page size exceeds 1MB (#29361) 2024-02-22 19:12:53 +01:00
Claire
328a9b8157
Change registrations to be disabled by default for new servers (#29353) 2024-02-22 18:15:59 +01:00
Claire
4fd22acb4a
Fix auto-close email being sent to users with devops permissions instead of settings permissions (#29356) 2024-02-22 18:15:38 +01:00
Claire
28b666b0d5
Automatically switch from open to approved registrations in absence of moderators (#29337) 2024-02-22 14:39:42 +01:00
Claire
fbb07893b8
Update dependencies (#29346) 2024-02-22 13:25:53 +01:00
KMY(雪あすか)
4fbaadaf1b
Merge pull request #572 from kmycode/kb-draft-5.17-lts
Release: 5.17 LTS
2024-02-16 22:16:23 +09:00
Claire
c5d56de98d Fix linting failure 2024-02-16 13:57:04 +01:00
KMY
e8c1a13dd4 Fix test 2024-02-16 21:53:19 +09:00
KMY
188d9bdbf6 Bump version to 5.17 LTS 2024-02-16 20:31:02 +09:00
KMY
f6588fe079 Merge commit '0e4e98fad1' into kb_lts 2024-02-16 20:30:06 +09:00
Claire
0e4e98fad1 Bump version to v4.2.7 2024-02-16 11:57:02 +01:00
Claire
15de520201
Merge pull request from GHSA-jhrq-qvrm-qr36
* Fix insufficient Content-Type checking of fetched ActivityStreams objects

* Allow JSON-LD documents with multiple profiles
2024-02-16 11:56:12 +01:00
Claire
684f99908f Update dependency pg to 1.5.5 2024-02-16 09:19:35 +01:00
KMY(雪あすか)
55581c9335
Merge pull request #546 from kmycode/kb-draft-5.16-lts
Release: 5.16 LTS
2024-02-15 09:15:22 +09:00
Claire
7d3899d5c9 Update nsa gem to version 0.3.0 (#29065) (#29206)
Co-authored-by: Matt Jankowski <matt@jankowski.online>
2024-02-15 08:55:07 +09:00
Claire
f0a1dc1f1e Fix user creation failure handling in OAuth paths (#29207) 2024-02-15 08:54:59 +09:00
Claire
35f830b7ec Fix OmniAuth tests (#29201) 2024-02-15 08:44:37 +09:00
KMY
d0cc051298 Bump version to 5.16-lts 2024-02-15 08:16:14 +09:00
KMY
9190f53d7b Merge commit '7c8ca0c6d6' into kb-draft-5.16-lts 2024-02-15 08:15:29 +09:00
KMY(雪あすか)
d9b9e66bb5 Fix: リモートアカウント情報のNgWord検査でNULLが出る問題 (#541)
* Fix: リモートアカウント情報のNgWord検査でNULLが出る問題

* Add test
2024-02-15 08:15:11 +09:00
Claire
e4ec4ce217
Update nsa gem to version 0.3.0 (#29065) (#29206)
Co-authored-by: Matt Jankowski <matt@jankowski.online>
2024-02-14 23:27:02 +01:00
Claire
870ee80fd3 Fix user creation failure handling in OAuth paths (#29207) 2024-02-14 22:55:31 +01:00
Claire
76a37bd040 Fix OmniAuth tests (#29201) 2024-02-14 16:06:38 +01:00
Claire
7c8ca0c6d6 Bump version to v4.2.6 2024-02-14 15:16:34 +01:00
Claire
f1700523f1
Merge pull request from GHSA-vm39-j3vx-pch3
* Prevent different identities from a same SSO provider from accessing a same account

* Lock auth provider changes behind `ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH=true`

* Rename methods to avoid confusion between OAuth and OmniAuth
2024-02-14 15:16:07 +01:00
Claire
0b0c7af2c1
Merge pull request from GHSA-7w3c-p9j8-mq3x
* Ensure destruction of OAuth Applications notifies streaming

Due to doorkeeper using a dependent: delete_all relationship, the destroy of an OAuth Application bypassed the existing AccessTokenExtension callbacks for announcing destructing of access tokens.

* Ensure password resets revoke access to Streaming API

* Improve performance of deleting OAuth tokens

---------

Co-authored-by: Emelia Smith <ThisIsMissEm@users.noreply.github.com>
2024-02-14 15:15:34 +01:00
Claire
1a33d348d0 Add sidekiq_unique_jobs:delete_all_locks task and disable sidekiq-unique-jobs UI by default (#29199) 2024-02-14 13:17:45 +01:00
Emelia Smith
6d43b63275 Disable administrative doorkeeper routes (#29187) 2024-02-14 11:03:21 +01:00
Claire
ae2dce813a Update dependency sidekiq-unique-jobs to 7.1.33 2024-02-14 11:02:55 +01:00
Claire
b7230cd759 Update dependency nokogiri to 1.16.2 2024-02-14 11:02:11 +01:00
KMY(雪あすか)
5274a399d6
Merge pull request #513 from kmycode/kb-draft-5.15-lts
Release: 5.15 LTS
2024-02-02 09:52:08 +09:00
KMY
3bae139748 Bump version to 5.15 LTS 2024-02-02 09:19:01 +09:00
KMY
088c8482ff Fix: リモートからの参照を無限に受け入れる問題 2024-02-02 09:19:01 +09:00
KMY
0cdc6faa46 Fix test 2024-02-02 09:19:01 +09:00
KMY
8a306337e5 Merge remote-tracking branch 'parent/stable-4.2' into kb-draft-5.15-lts 2024-02-02 07:34:42 +09:00
Claire
a6641f828b
Merge pull request from GHSA-3fjr-858r-92rw
* Fix insufficient origin validation

* Bump version to v4.2.5
2024-02-01 15:56:46 +01:00
KMY(雪あすか)
58969aed33
Merge pull request #488 from kmycode/kb-draft-5.14-lts
Release: 5.14 LTS
2024-01-25 12:21:00 +09:00
KMY
ae71ed50e8 Ruby version to 3.2.3 2024-01-25 09:59:06 +09:00
KMY
1d42b6b82f Merge remote-tracking branch 'parent/stable-4.2' into kb-draft-5.14-lts 2024-01-25 07:45:24 +09:00
Claire
4633bb8ce0 Bump version to v4.2.4 2024-01-24 15:31:13 +01:00
Claire
1ab050eb52 Change PostgreSQL version check to check for PostgreSQL 10+ 2024-01-24 15:31:13 +01:00
Claire
4eb98ef755 Ignore the devise-two-factor advisory as we have rate limits in place (#28733) 2024-01-24 15:31:13 +01:00
Claire
7a22999f92 Bump ruby version to 3.2.3 2024-01-24 15:31:13 +01:00
Claire
c5c464804d Update dependency puma to v6.4.2 2024-01-24 15:31:13 +01:00
Claire
779237f054 Fix error when processing remote files with unusually long names (#28823) 2024-01-24 15:31:13 +01:00
Claire
b377f82b1d Fix processing of compacted single-item JSON-LD collections (#28816) 2024-01-24 15:31:13 +01:00
Claire
6fe2a47357 Add rate-limit of TOTP authentication attempts at controller level (#28801) 2024-01-24 15:31:13 +01:00
Jonathan de Jong
2dbf176d23 Retry 401 errors on replies fetching (#28788)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2024-01-24 15:31:13 +01:00
Jeong Arm
499bc716a5 Ignore RecordNotUnique errors in LinkCrawlWorker (#28748) 2024-01-24 15:31:13 +01:00
Claire
3837ec2227 Fix Mastodon not correctly processing HTTP Signatures with query strings (#28476) 2024-01-24 15:31:13 +01:00
Claire
1998c561b2 Convert signature verification specs to request specs (#28443) 2024-01-24 15:31:13 +01:00
Claire
c0a9db3611 Fix potential redirection loop of streaming endpoint (#28665) 2024-01-24 15:31:13 +01:00
Claire
01caa18e5b Fix streaming API redirection ignoring the port of streaming_api_base_url (#28558) 2024-01-24 15:31:13 +01:00
Claire
c609b726cb Fix error when processing link preview with an array as inLanguage (#28252) 2024-01-24 15:31:13 +01:00
Eugen Rochko
4d96d716c4 Fix unsupported time zone or locale preventing sign-up (#28035)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2024-01-24 15:31:13 +01:00
Brian Holley
3ecc991f63 Fix "Hide these posts from home" list setting not refreshing when switching lists (#27763) 2024-01-24 15:31:13 +01:00
Eugen Rochko
8f2dac0567 Fix missing background behind dismissable banner in web UI (#27479) 2024-01-24 15:31:13 +01:00
Claire
dfc8fcc6f0 Fix width of large text icon buttons (#27127) 2024-01-24 15:31:13 +01:00
gunchleoc
e8c5754142 Fix line wrapping of language selection button with long locale codes (#27100) 2024-01-24 15:31:13 +01:00
MitarashiDango
0a01bc01d2 Fix Undo Announce activity is not sent, when not followed by the reblogged post author (#18482)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2024-01-24 15:31:13 +01:00
Claire
a12b7551cf Fix N+1s because of association preloaders not actually getting called (#28339) 2024-01-24 15:31:13 +01:00
Claire
7abc61887f Fix empty column explainer getting cropped under certain conditions (#28337) 2024-01-24 15:31:13 +01:00
Claire
279be07679 Fix LinkCrawlWorker error when encountering empty OEmbed response (#28268) 2024-01-24 15:31:13 +01:00
KMY
11db9e44b1 Bump version to 5.14 LTS 2024-01-24 21:35:54 +09:00
KMY(雪あすか)
eb280d93a9 Fix: ドメインブロックがOutboxにおいて動作しない問題 (LTS) 2024-01-24 21:35:54 +09:00
KMY(雪あすか)
49d561178d
Fix: ブースト時に選択できる公開範囲に限定投稿が含まれる問題 (LTS) (#461) 2024-01-15 12:27:48 +09:00
KMY(雪あすか)
33f254be42
Remove: #454 リンクプレビューを生成する設定の削除、無効化 (LTS) (#459) 2024-01-15 12:27:29 +09:00
KMY(雪あすか)
ad7f23556b
Merge pull request #418 from kmycode/kb-draft-5.13-lts
Release: 5.13 LTS
2024-01-11 09:19:25 +09:00
KMY
b21c0458b6 Bump version to 5.13 LTS 2024-01-11 08:32:59 +09:00
KMY
4a5f0f9259 Remove: reject_send_mediaも削除 2024-01-11 08:32:59 +09:00
KMY
4f6d89f161 Fix test 2024-01-10 18:59:25 +09:00
KMY
5ba8141df9 Remove: #372 削除予定のドメインブロック項目をいったん削除 2024-01-10 18:46:14 +09:00
KMY
4b10bf23ab Remove: SidekiqHealthScheduler 2024-01-09 18:38:04 +09:00
KMY
aa5e50e5d5 Change: NodeInfoのバージョン表記 2024-01-09 15:57:45 +09:00
KMY
3e4bd83326 Fix test 2024-01-09 13:25:49 +09:00
KMY
e17ddc1b6a Fix test 2024-01-09 13:16:24 +09:00
KMY
7edb05337e Add: #437 ドメインブロックで「未ログインユーザーに非公開」の設定を「非公開」にコピーするマイグレーションコード 2024-01-09 13:09:23 +09:00
KMY
267e9cbcc8 Remove: #429 ドメインブロックの「未ログインユーザーに非公開にする」オプション 2024-01-07 16:02:41 +09:00
KMY
06123147d5 Fix: アンテナに登録された投稿がアンテナ削除時Redisから削除されない問題 (#417)
* Fix: アンテナに登録された投稿がRedisから削除されない問題

* Fix test

* Tootctlに変更

* 処理を共通化
2024-01-04 15:32:58 +09:00
Claire
d7875adad2
Fix call to inefficient delete_matched cache method in domain blocks (#28367) 2023-12-19 11:27:37 +01:00
KMY(雪あすか)
e227885d0b
Merge pull request #347 from kmycode/kb-draft-5.12-lts
Release: 5.12 LTS
2023-12-15 10:09:13 +09:00
KMY
38105dfbaa Bump version to 5.12 LTS 2023-12-15 09:43:03 +09:00
KMY(雪あすか)
e8b6c16b52 Merge pull request from GHSA-qg32-3vrh-w6mw 2023-12-15 09:43:03 +09:00
KMY(雪あすか)
e9b69478d1 Fix: #355 LTLのインデックスが適切にはられていない問題 (#356) 2023-12-13 10:46:25 +09:00
KMY(雪あすか)
0dec7b450b
Fix: #355 LTLのインデックスが適切にはられていない問題 (#356) 2023-12-13 09:13:35 +09:00
KMY(雪あすか)
5cce294953 Fix: #284 FetchInstanceInfoWorkerが原因でSidekiqのJobが詰まる問題 (#342)
* Fix: #284 `FetchInstanceInfoWorker`が原因でSidekiqのJobが詰まる問題

* Fix: InstanceInfoを取得するタイミング

* Fix test

* Fix test

* Fix: HTTPコード

* 調整
2023-12-12 09:54:29 +09:00
KMY(雪あすか)
f0a8bad941
Merge pull request #332 from kmycode/kb-draft-5.11-lts
Release: 5.11 LTS
2023-12-06 08:40:31 +09:00
KMY
d79c88394b Bump version to 5.11 2023-12-06 08:16:24 +09:00
KMY
4f62dbdd64 Merge commit '90371a4fc4' into kb-draft-5.11-lts 2023-12-06 08:04:15 +09:00
Claire
90371a4fc4 Bump version to v4.2.3 2023-12-05 15:35:05 +01:00
Claire
71b60b09f4 Update dependency json-ld to v3.3.1 2023-12-05 15:35:05 +01:00
KMY(雪あすか)
2af8b653cb
Release: 5.10 LTS (#324) 2023-12-05 09:33:54 +09:00
KMY(雪あすか)
5863a7756e
Merge pull request #330 from kmycode/upstream-4.2.2-lts
Upstream 4.2.2 lts
2023-12-05 09:29:58 +09:00
KMY
9310c1c81b Merge remote-tracking branch 'parent/stable-4.2' into upstream-4.2.2-lts 2023-12-05 09:05:29 +09:00
Claire
4b8fe9df73 Bump version to v4.2.2 2023-12-04 15:28:15 +01:00
Claire
7b9496322f Change dismissed banners to be stored server-side (#27055) 2023-12-04 15:28:15 +01:00
Claire
09115731d6 Change GIF max matrix size error to explicitly mention GIF files (#27927) 2023-12-04 15:28:15 +01:00
Claire
e11100d782 Clamp dates when serializing to Elasticsearch API (#28081) 2023-12-04 15:28:15 +01:00
Jonathan de Jong
252ea2fc67 Have Follow activities bypass availability (#27586)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2023-12-04 15:28:15 +01:00
Claire
8d02e58ff4 Fix upper border radius of onboarding columns (#27890) 2023-12-04 15:28:15 +01:00
Claire
1076a6cd62 Fix incoming status creation date not being restricted to standard ISO8601 (#27655) 2023-12-04 15:28:15 +01:00
Claire
54a07731d1 Fix posts from threads received out-of-order sometimes not being inserted into timelines (#27653) 2023-12-04 15:28:15 +01:00
Claire
81d7cfd544 Fix posts from force-sensitized accounts being able to trend (#27620) 2023-12-04 15:28:15 +01:00
Claire
e6f4c91c5c Fix hashtag matching pattern matching some URLs (#27584) 2023-12-04 15:28:15 +01:00
Claire
de86e822f4 Fix error when trying to delete already-deleted file with OpenStack Swift (#27569) 2023-12-04 15:28:15 +01:00
Claire
4c38706474 Fix batch attachment deletion when using OpenStack Swift (#27554) 2023-12-04 15:28:15 +01:00
Renaud Chaput
4fc2523546 Do not display the navigation banner in the logo container (#27476) 2023-12-04 15:28:15 +01:00
Renaud Chaput
d5bc10b711 The class props should be className (#27462) 2023-12-04 15:28:15 +01:00
Claire
c66ade7de8 Fix processing LDSigned activities from actors with unknown public keys (#27474) 2023-12-04 15:28:15 +01:00
Claire
bece853e3c Fix error and incorrect URLs in /api/v1/accounts/:id/featured_tags for remote accounts (#27459) 2023-12-04 15:28:15 +01:00
Claire
700ae1f918 Fix report processing notice not mentioning the report number when performing a custom action (#27442) 2023-12-04 15:28:15 +01:00
Claire
13205b54fd Fix handling of inLanguage attribute in preview card processing (#27423) 2023-12-04 15:28:15 +01:00
KMY(雪あすか)
8be33d4316 Fix when unfollow a tag, my post also disappears from the home timeline (#27391) 2023-12-04 15:28:15 +01:00
Claire
cdedae6d63 Fix some link anchors being recognized as hashtags (#27271) 2023-12-04 15:28:15 +01:00
Claire
aa69ca74ed Fix incorrect serialization of regional languages in contentMap (#27207) 2023-12-04 15:28:15 +01:00
gunchleoc
156d32689b Only strip country code when language not listed in SUPPORTED_LOCALES (#27099) 2023-12-04 15:28:15 +01:00
Claire
ef149674f0 Change Content-Security-Policy to be tighter on media paths (#26889) 2023-12-04 15:28:15 +01:00
KMY(雪あすか)
129705db44
Fix: スタンプを外すとき、自分が外したスタンプが投稿から全部消える問題 (#317) 2023-11-29 13:29:15 +09:00
KMY(雪あすか)
8383288219
Change: サークルの送り先アカウント指定方法をaccount_username(Fedibirdと同様)に変更 (#283) (LTS) (#308)
* Change: サークルの送り先アカウント指定方法を`account_username`(Fedibirdと同様)に変更 (#283)

* Change: サークルの送り先アカウント指定方法を`account_username`(Fedibirdと同様)に変更

* Test: テストを追加

* Maybe Fix: Fedibirdで自分限定と認識される問題

* Fix test
2023-11-28 12:54:03 +09:00
Claire
eea2654236
Fix format-dependent redirects being cached regardless of requested format (#27634) 2023-11-13 17:58:00 +01:00
272 changed files with 3781 additions and 1462 deletions

View file

@ -1,4 +1,10 @@
--- ---
ignore: ignore:
# Sidekiq security issue, fixes in the latest Sidekiq 7 but we can not upgrade. Will be fixed in Sidekiq 6.5.10 # devise-two-factor advisory about brute-forcing TOTP
- CVE-2023-26141 # We have rate-limits on authentication endpoints in place (including second
# factor verification) since Mastodon v3.2.0
- CVE-2024-0227
# devise-two-factor advisory about generated secrets being weaker than expected
# We call `generate_otp_secret` ourselves with a requested length of 32 characters,
# which exceeds the recommended remediation of 26 characters, so we're safe
- CVE-2024-8796

View file

@ -130,6 +130,7 @@ Naming/VariableNumber:
- 'db/migrate/20190820003045_update_statuses_index.rb' - 'db/migrate/20190820003045_update_statuses_index.rb'
- 'db/migrate/20190823221802_add_local_index_to_statuses.rb' - 'db/migrate/20190823221802_add_local_index_to_statuses.rb'
- 'db/migrate/20200119112504_add_public_index_to_statuses.rb' - 'db/migrate/20200119112504_add_public_index_to_statuses.rb'
- 'db/migrate/20231212225737_improve_index_for_public_timeline_speed.rb'
- 'spec/models/account_spec.rb' - 'spec/models/account_spec.rb'
- 'spec/models/domain_block_spec.rb' - 'spec/models/domain_block_spec.rb'
- 'spec/models/user_spec.rb' - 'spec/models/user_spec.rb'
@ -441,6 +442,7 @@ Rails/SkipsModelValidations:
- 'db/migrate/20190511134027_add_silenced_at_suspended_at_to_accounts.rb' - 'db/migrate/20190511134027_add_silenced_at_suspended_at_to_accounts.rb'
- 'db/migrate/20191007013357_update_pt_locales.rb' - 'db/migrate/20191007013357_update_pt_locales.rb'
- 'db/migrate/20220316233212_update_kurdish_locales.rb' - 'db/migrate/20220316233212_update_kurdish_locales.rb'
- 'db/migrate/20240109035435_remove_hidden_anonymous_from_domain_blocks.rb'
- 'db/post_migrate/20190511152737_remove_suspended_silenced_account_fields.rb' - 'db/post_migrate/20190511152737_remove_suspended_silenced_account_fields.rb'
- 'db/post_migrate/20200917193528_migrate_notifications_type.rb' - 'db/post_migrate/20200917193528_migrate_notifications_type.rb'
- 'db/post_migrate/20201017234926_fill_account_suspension_origin.rb' - 'db/post_migrate/20201017234926_fill_account_suspension_origin.rb'

View file

@ -1 +1 @@
3.2.2 3.2.3

BIN
.yarn/install-state.gz Normal file

Binary file not shown.

View file

@ -2,6 +2,247 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [4.2.13] - 2024-09-30
### Security
- Fix ReDoS vulnerability on some Ruby versions ([GHSA-jpxp-r43f-rhvx](https://github.com/mastodon/mastodon/security/advisories/GHSA-jpxp-r43f-rhvx))
- Update dependencies
### Added
- Add “A Mastodon update is available.” message on admin dashboard for non-bugfix updates (#32106 by @ClearlyClaire)
### Changed
- Change Mastodon to issue correct HTTP signatures by default (#31994 by @ClearlyClaire)
### Fixed
- Fix replies collection being cached improperly
- Fix security context sometimes not being added in LD-Signed activities (#31871 by @ClearlyClaire)
- Fix error when encountering reblog of deleted post in feed rebuild (#32001 by @ClearlyClaire)
## [4.2.12] - 2024-08-19
### Fixed
- Fix broken notifications for mentions from local moderators ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31484))
## [4.2.11] - 2024-08-16
### Added
- Add support for incoming `<s>` tag ([mediaformat](https://github.com/mastodon/mastodon/pull/31375))
### Changed
- Change logic of block/mute bypass for mentions from moderators to only apply to visible roles with moderation powers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31271))
### Fixed
- Fix incorrect rate limit on PUT requests ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31356))
- Fix presence of `ß` in adjacent word preventing mention and hashtag matching ([adamniedzielski](https://github.com/mastodon/mastodon/pull/31122))
- Fix processing of webfinger responses with multiple `self` links ([adamniedzielski](https://github.com/mastodon/mastodon/pull/31110))
- Fix duplicate `orderedItems` in user archive's `outbox.json` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31099))
- Fix click event handling when clicking outside of an open dropdown menu ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31251))
- Fix status processing failing halfway when a remote post has a malformed `replies` attribute ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/31246))
- Fix `--verbose` option of `tootctl media remove`, which was previously erroneously removed ([mjankowski](https://github.com/mastodon/mastodon/pull/30536))
- Fix division by zero on some video/GIF files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30600))
- Fix Web UI trying to save user settings despite being logged out ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30324))
- Fix hashtag regexp matching some link anchors ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30190))
- Fix local account search on LDAP login being case-sensitive ([raucao](https://github.com/mastodon/mastodon/pull/30113))
- Fix development environment admin account not being auto-approved ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29958))
- Fix report reason selector in moderation interface not unselecting rules when changing category ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29026))
- Fix already-invalid reports failing to resolve ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29027))
- Fix OCR when using S3/CDN for assets ([vmstan](https://github.com/mastodon/mastodon/pull/28551))
- Fix error when encountering malformed `Tag` objects from Kbin ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/28235))
- Fix not all allowed image formats showing in file picker when uploading custom emoji ([june128](https://github.com/mastodon/mastodon/pull/28076))
- Fix search popout listing unusable search options when logged out ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27918))
- Fix processing of featured collections lacking an `items` attribute ([tribela](https://github.com/mastodon/mastodon/pull/27581))
- Fix `mastodon:stats` decoration of stats rake task ([mjankowski](https://github.com/mastodon/mastodon/pull/31104))
## [4.2.10] - 2024-07-04
### Security
- Fix incorrect permission checking on multiple API endpoints ([GHSA-58x8-3qxw-6hm7](https://github.com/mastodon/mastodon/security/advisories/GHSA-58x8-3qxw-6hm7))
- Fix incorrect authorship checking when processing some activities (CVE-2024-37903, [GHSA-xjvf-fm67-4qc3](https://github.com/mastodon/mastodon/security/advisories/GHSA-xjvf-fm67-4qc3))
- Fix ongoing streaming sessions not being invalidated when application tokens get revoked ([GHSA-vp5r-5pgw-jwqx](https://github.com/mastodon/mastodon/security/advisories/GHSA-vp5r-5pgw-jwqx))
- Update dependencies
### Added
- Add yarn version specification to avoid confusion with Yarn 3 and Yarn 4
### Changed
- Change preview cards generation to skip unusually long URLs ([oneiros](https://github.com/mastodon/mastodon/pull/30854))
- Change search modifiers to be case-insensitive ([Gargron](https://github.com/mastodon/mastodon/pull/30865))
- Change `STATSD_ADDR` handling to emit a warning rather than crashing if the address is unreachable ([timothyjrogers](https://github.com/mastodon/mastodon/pull/30691))
- Change PWA start URL from `/home` to `/` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27377))
### Removed
- Removed dependency on `posix-spawn` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18559))
### Fixed
- Fix scheduled statuses scheduled in less than 5 minutes being immediately published ([danielmbrasil](https://github.com/mastodon/mastodon/pull/30584))
- Fix encoding detection for link cards ([oneiros](https://github.com/mastodon/mastodon/pull/30780))
- Fix `/admin/accounts/:account_id/statuses/:id` for edited posts with media attachments ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30819))
- Fix duplicate `@context` attribute in user archive export ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30653))
## [4.2.9] - 2024-05-30
### Security
- Update dependencies
- Fix private mention filtering ([GHSA-5fq7-3p3j-9vrf](https://github.com/mastodon/mastodon/security/advisories/GHSA-5fq7-3p3j-9vrf))
- Fix password change endpoint not being rate-limited ([GHSA-q3rg-xx5v-4mxh](https://github.com/mastodon/mastodon/security/advisories/GHSA-q3rg-xx5v-4mxh))
- Add hardening around rate-limit bypass ([GHSA-c2r5-cfqr-c553](https://github.com/mastodon/mastodon/security/advisories/GHSA-c2r5-cfqr-c553))
### Added
- Add rate-limit on OAuth application registration ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/30316))
- Add fallback redirection when getting a webfinger query `WEB_DOMAIN@WEB_DOMAIN` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28592))
- Add `digest` attribute to `Admin::DomainBlock` entity in REST API ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/29092))
### Removed
- Remove superfluous application-level caching in some controllers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29862))
- Remove aggressive OAuth application vacuuming ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/30316))
### Fixed
- Fix leaking Elasticsearch connections in Sidekiq processes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30450))
- Fix language of remote posts not being recognized when using unusual casing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30403))
- Fix off-by-one in `tootctl media` commands ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30306))
- Fix removal of allowed domains (in `LIMITED_FEDERATION_MODE`) not being recorded in the audit log ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/30125))
- Fix not being able to block a subdomain of an already-blocked domain through the API ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30119))
- Fix `Idempotency-Key` being ignored when scheduling a post ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30084))
- Fix crash when supplying the `FFMPEG_BINARY` environment variable ([timothyjrogers](https://github.com/mastodon/mastodon/pull/30022))
- Fix improper email address validation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29838))
- Fix results/query in `api/v1/featured_tags/suggestions` ([mjankowski](https://github.com/mastodon/mastodon/pull/29597))
- Fix unblocking internationalized domain names under certain conditions ([tribela](https://github.com/mastodon/mastodon/pull/29530))
- Fix admin account created by `mastodon:setup` not being auto-approved ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29379))
- Fix reference to non-existent var in CLI maintenance command ([mjankowski](https://github.com/mastodon/mastodon/pull/28363))
## [4.2.8] - 2024-02-23
### Added
- Add hourly task to automatically require approval for new registrations in the absence of moderators ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29318), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/29355))
In order to prevent future abandoned Mastodon servers from being used for spam, harassment and other malicious activity, Mastodon will now automatically switch new user registrations to require moderator approval whenever they are left open and no activity (including non-moderation actions from apps) from any logged-in user with permission to access moderation reports has been detected in a full week.
When this happens, users with the permission to change server settings will receive an email notification.
This feature is disabled when `EMAIL_DOMAIN_ALLOWLIST` is used, and can also be disabled with `DISABLE_AUTOMATIC_SWITCHING_TO_APPROVED_REGISTRATIONS=true`.
### Changed
- Change registrations to be closed by default on new installations ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29280))
If you are running a server and never changed your registrations mode from the default, updating will automatically close your registrations.
Simply re-enable them through the administration interface or using `tootctl settings registrations open` if you want to enable them again.
### Fixed
- Fix processing of remote ActivityPub actors making use of `Link` objects as `Image` `url` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29335))
- Fix link verifications when page size exceeds 1MB ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29358))
## [4.2.7] - 2024-02-16
### Fixed
- Fix OmniAuth tests and edge cases in error handling ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29201), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/29207))
- Fix new installs by upgrading to the latest release of the `nsa` gem, instead of a no longer existing commit ([mjankowski](https://github.com/mastodon/mastodon/pull/29065))
### Security
- Fix insufficient checking of remote posts ([GHSA-jhrq-qvrm-qr36](https://github.com/mastodon/mastodon/security/advisories/GHSA-jhrq-qvrm-qr36))
## [4.2.6] - 2024-02-14
### Security
- Update the `sidekiq-unique-jobs` dependency (see [GHSA-cmh9-rx85-xj38](https://github.com/mhenrixon/sidekiq-unique-jobs/security/advisories/GHSA-cmh9-rx85-xj38))
In addition, we have disabled the web interface for `sidekiq-unique-jobs` out of caution.
If you need it, you can re-enable it by setting `ENABLE_SIDEKIQ_UNIQUE_JOBS_UI=true`.
If you only need to clear all locks, you can now use `bundle exec rake sidekiq_unique_jobs:delete_all_locks`.
- Update the `nokogiri` dependency (see [GHSA-xc9x-jj77-9p9j](https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-xc9x-jj77-9p9j))
- Disable administrative Doorkeeper routes ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/29187))
- Fix ongoing streaming sessions not being invalidated when applications get deleted in some cases ([GHSA-7w3c-p9j8-mq3x](https://github.com/mastodon/mastodon/security/advisories/GHSA-7w3c-p9j8-mq3x))
In some rare cases, the streaming server was not notified of access tokens revocation on application deletion.
- Change external authentication behavior to never reattach a new identity to an existing user by default ([GHSA-vm39-j3vx-pch3](https://github.com/mastodon/mastodon/security/advisories/GHSA-vm39-j3vx-pch3))
Up until now, Mastodon has allowed new identities from external authentication providers to attach to an existing local user based on their verified e-mail address.
This allowed upgrading users from a database-stored password to an external authentication provider, or move from one authentication provider to another.
However, this behavior may be unexpected, and means that when multiple authentication providers are configured, the overall security would be that of the least secure authentication provider.
For these reasons, this behavior is now locked under the `ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH` environment variable.
In addition, regardless of this environment variable, Mastodon will refuse to attach two identities from the same authentication provider to the same account.
## [4.2.5] - 2024-02-01
### Security
- Fix insufficient origin validation (CVE-2024-23832, [GHSA-3fjr-858r-92rw](https://github.com/mastodon/mastodon/security/advisories/GHSA-3fjr-858r-92rw))
## [4.2.4] - 2024-01-24
### Fixed
- Fix error when processing remote files with unusually long names ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28823))
- Fix processing of compacted single-item JSON-LD collections ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28816))
- Retry 401 errors on replies fetching ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/28788))
- Fix `RecordNotUnique` errors in LinkCrawlWorker ([tribela](https://github.com/mastodon/mastodon/pull/28748))
- Fix Mastodon not correctly processing HTTP Signatures with query strings ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28443), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/28476))
- Fix potential redirection loop of streaming endpoint ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28665))
- Fix streaming API redirection ignoring the port of `streaming_api_base_url` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28558))
- Fix error when processing link preview with an array as `inLanguage` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28252))
- Fix unsupported time zone or locale preventing sign-up ([Gargron](https://github.com/mastodon/mastodon/pull/28035))
- Fix "Hide these posts from home" list setting not refreshing when switching lists ([brianholley](https://github.com/mastodon/mastodon/pull/27763))
- Fix missing background behind dismissable banner in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/27479))
- Fix line wrapping of language selection button with long locale codes ([gunchleoc](https://github.com/mastodon/mastodon/pull/27100), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27127))
- Fix `Undo Announce` activity not being sent to non-follower authors ([MitarashiDango](https://github.com/mastodon/mastodon/pull/18482))
- Fix N+1s because of association preloaders not actually getting called ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28339))
- Fix empty column explainer getting cropped under certain conditions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28337))
- Fix `LinkCrawlWorker` error when encountering empty OEmbed response ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28268))
- Fix call to inefficient `delete_matched` cache method in domain blocks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28367))
### Security
- Add rate-limit of TOTP authentication attempts at controller level ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28801))
## [4.2.3] - 2023-12-05
### Fixed
- Fix dependency on `json-canonicalization` version that has been made unavailable since last release
## [4.2.2] - 2023-12-04
### Changed
- Change dismissed banners to be stored server-side ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27055))
- Change GIF max matrix size error to explicitly mention GIF files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27927))
- Change `Follow` activities delivery to bypass availability check ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/27586))
- Change single-column navigation notice to be displayed outside of the logo container ([renchap](https://github.com/mastodon/mastodon/pull/27462), [renchap](https://github.com/mastodon/mastodon/pull/27476))
- Change Content-Security-Policy to be tighter on media paths ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26889))
- Change post language code to include country code when relevant ([gunchleoc](https://github.com/mastodon/mastodon/pull/27099), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27207))
### Fixed
- Fix upper border radius of onboarding columns ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27890))
- Fix incoming status creation date not being restricted to standard ISO8601 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27655), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/28081))
- Fix some posts from threads received out-of-order sometimes not being inserted into timelines ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27653))
- Fix posts from force-sensitized accounts being able to trend ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27620))
- Fix error when trying to delete already-deleted file with OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27569))
- Fix batch attachment deletion when using OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27554))
- Fix processing LDSigned activities from actors with unknown public keys ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27474))
- Fix error and incorrect URLs in `/api/v1/accounts/:id/featured_tags` for remote accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27459))
- Fix report processing notice not mentioning the report number when performing a custom action ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27442))
- Fix handling of `inLanguage` attribute in preview card processing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27423))
- Fix own posts being removed from home timeline when unfollowing a used hashtag ([kmycode](https://github.com/mastodon/mastodon/pull/27391))
- Fix some link anchors being recognized as hashtags ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27271), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27584))
- Fix format-dependent redirects being cached regardless of requested format ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27634))
## [4.2.1] - 2023-10-10 ## [4.2.1] - 2023-10-10
### Added ### Added

View file

@ -2,7 +2,7 @@
# This needs to be bookworm-slim because the Ruby image is built on bookworm-slim # This needs to be bookworm-slim because the Ruby image is built on bookworm-slim
ARG NODE_VERSION="20.6-bookworm-slim" ARG NODE_VERSION="20.6-bookworm-slim"
FROM ghcr.io/moritzheiber/ruby-jemalloc:3.2.2-slim as ruby FROM ghcr.io/moritzheiber/ruby-jemalloc:3.2.3-slim as ruby
FROM node:${NODE_VERSION} as build FROM node:${NODE_VERSION} as build
COPY --link --from=ruby /opt/ruby /opt/ruby COPY --link --from=ruby /opt/ruby /opt/ruby

View file

@ -61,11 +61,10 @@ gem 'kaminari', '~> 1.2'
gem 'link_header', '~> 0.0' gem 'link_header', '~> 0.0'
gem 'mime-types', '~> 3.5.0', require: 'mime/types/columnar' gem 'mime-types', '~> 3.5.0', require: 'mime/types/columnar'
gem 'nokogiri', '~> 1.15' gem 'nokogiri', '~> 1.15'
gem 'nsa', github: 'jhawthorn/nsa', ref: 'e020fcc3a54d993ab45b7194d89ab720296c111b' gem 'nsa'
gem 'oj', '~> 3.14' gem 'oj', '~> 3.14'
gem 'ox', '~> 2.14' gem 'ox', '~> 2.14'
gem 'parslet' gem 'parslet'
gem 'posix-spawn'
gem 'public_suffix', '~> 5.0' gem 'public_suffix', '~> 5.0'
gem 'pundit', '~> 2.3' gem 'pundit', '~> 2.3'
gem 'premailer-rails' gem 'premailer-rails'
@ -206,3 +205,5 @@ gem 'net-http', '~> 0.3.2'
gem 'rubyzip', '~> 2.3' gem 'rubyzip', '~> 2.3'
gem 'hcaptcha', '~> 7.1' gem 'hcaptcha', '~> 7.1'
gem 'mail', '~> 2.8'

View file

@ -7,17 +7,6 @@ GIT
hkdf (~> 0.2) hkdf (~> 0.2)
jwt (~> 2.0) jwt (~> 2.0)
GIT
remote: https://github.com/jhawthorn/nsa.git
revision: e020fcc3a54d993ab45b7194d89ab720296c111b
ref: e020fcc3a54d993ab45b7194d89ab720296c111b
specs:
nsa (0.2.8)
activesupport (>= 4.2, < 7.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
sidekiq (>= 3.5)
statsd-ruby (~> 1.4, >= 1.4.0)
GIT GIT
remote: https://github.com/mastodon/rails-settings-cached.git remote: https://github.com/mastodon/rails-settings-cached.git
revision: 86328ef0bd04ce21cc0504ff5e334591e8c2ccab revision: 86328ef0bd04ce21cc0504ff5e334591e8c2ccab
@ -39,47 +28,47 @@ GIT
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (7.0.8) actioncable (7.0.8.4)
actionpack (= 7.0.8) actionpack (= 7.0.8.4)
activesupport (= 7.0.8) activesupport (= 7.0.8.4)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
actionmailbox (7.0.8) actionmailbox (7.0.8.4)
actionpack (= 7.0.8) actionpack (= 7.0.8.4)
activejob (= 7.0.8) activejob (= 7.0.8.4)
activerecord (= 7.0.8) activerecord (= 7.0.8.4)
activestorage (= 7.0.8) activestorage (= 7.0.8.4)
activesupport (= 7.0.8) activesupport (= 7.0.8.4)
mail (>= 2.7.1) mail (>= 2.7.1)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
actionmailer (7.0.8) actionmailer (7.0.8.4)
actionpack (= 7.0.8) actionpack (= 7.0.8.4)
actionview (= 7.0.8) actionview (= 7.0.8.4)
activejob (= 7.0.8) activejob (= 7.0.8.4)
activesupport (= 7.0.8) activesupport (= 7.0.8.4)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (7.0.8) actionpack (7.0.8.4)
actionview (= 7.0.8) actionview (= 7.0.8.4)
activesupport (= 7.0.8) activesupport (= 7.0.8.4)
rack (~> 2.0, >= 2.2.4) rack (~> 2.0, >= 2.2.4)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (7.0.8) actiontext (7.0.8.4)
actionpack (= 7.0.8) actionpack (= 7.0.8.4)
activerecord (= 7.0.8) activerecord (= 7.0.8.4)
activestorage (= 7.0.8) activestorage (= 7.0.8.4)
activesupport (= 7.0.8) activesupport (= 7.0.8.4)
globalid (>= 0.6.0) globalid (>= 0.6.0)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (7.0.8) actionview (7.0.8.4)
activesupport (= 7.0.8) activesupport (= 7.0.8.4)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
@ -89,22 +78,22 @@ GEM
activemodel (>= 4.1, < 7.1) activemodel (>= 4.1, < 7.1)
case_transform (>= 0.2) case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3) jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
activejob (7.0.8) activejob (7.0.8.4)
activesupport (= 7.0.8) activesupport (= 7.0.8.4)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (7.0.8) activemodel (7.0.8.4)
activesupport (= 7.0.8) activesupport (= 7.0.8.4)
activerecord (7.0.8) activerecord (7.0.8.4)
activemodel (= 7.0.8) activemodel (= 7.0.8.4)
activesupport (= 7.0.8) activesupport (= 7.0.8.4)
activestorage (7.0.8) activestorage (7.0.8.4)
actionpack (= 7.0.8) actionpack (= 7.0.8.4)
activejob (= 7.0.8) activejob (= 7.0.8.4)
activerecord (= 7.0.8) activerecord (= 7.0.8.4)
activesupport (= 7.0.8) activesupport (= 7.0.8.4)
marcel (~> 1.0) marcel (~> 1.0)
mini_mime (>= 1.1.0) mini_mime (>= 1.1.0)
activesupport (7.0.8) activesupport (7.0.8.4)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) minitest (>= 5.1)
@ -148,6 +137,7 @@ GEM
net-http-persistent (~> 4.0) net-http-persistent (~> 4.0)
nokogiri (~> 1, >= 1.10.8) nokogiri (~> 1, >= 1.10.8)
base64 (0.1.1) base64 (0.1.1)
bcp47_spec (0.2.1)
bcrypt (3.1.18) bcrypt (3.1.18)
better_errors (2.10.1) better_errors (2.10.1)
erubi (>= 1.0.0) erubi (>= 1.0.0)
@ -201,8 +191,8 @@ GEM
xpath (~> 3.2) xpath (~> 3.2)
case_transform (0.2) case_transform (0.2)
activesupport activesupport
cbor (0.5.9.6) cbor (0.5.9.8)
charlock_holmes (0.7.7) charlock_holmes (0.7.8)
chewy (7.3.4) chewy (7.3.4)
activesupport (>= 5.2) activesupport (>= 5.2)
elasticsearch (>= 7.12.0, < 7.14.0) elasticsearch (>= 7.12.0, < 7.14.0)
@ -211,7 +201,7 @@ GEM
climate_control (0.2.0) climate_control (0.2.0)
cocoon (1.2.15) cocoon (1.2.15)
color_diff (0.1) color_diff (0.1)
concurrent-ruby (1.2.2) concurrent-ruby (1.3.4)
connection_pool (2.4.1) connection_pool (2.4.1)
cose (1.3.0) cose (1.3.0)
cbor (~> 0.5.9) cbor (~> 0.5.9)
@ -225,7 +215,7 @@ GEM
activerecord (>= 5.a) activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0) database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1) database_cleaner-core (2.0.1)
date (3.3.3) date (3.3.4)
debug_inspector (1.1.0) debug_inspector (1.1.0)
devise (4.9.2) devise (4.9.2)
bcrypt (~> 3.0) bcrypt (~> 3.0)
@ -266,7 +256,7 @@ GEM
multi_json multi_json
encryptor (3.0.0) encryptor (3.0.0)
erubi (1.12.0) erubi (1.12.0)
et-orbi (1.2.7) et-orbi (1.2.11)
tzinfo tzinfo
excon (0.100.0) excon (0.100.0)
fabrication (2.30.0) fabrication (2.30.0)
@ -298,7 +288,7 @@ GEM
faraday_middleware (1.2.0) faraday_middleware (1.2.0)
faraday (~> 1.0) faraday (~> 1.0)
fast_blank (1.0.1) fast_blank (1.0.1)
fastimage (2.2.7) fastimage (2.3.1)
ffi (1.15.5) ffi (1.15.5)
ffi-compiler (1.0.1) ffi-compiler (1.0.1)
ffi (>= 1.0.0) ffi (>= 1.0.0)
@ -316,8 +306,8 @@ GEM
fog-json (>= 1.0) fog-json (>= 1.0)
ipaddress (>= 0.8) ipaddress (>= 0.8)
formatador (0.3.0) formatador (0.3.0)
fugit (1.8.1) fugit (1.11.1)
et-orbi (~> 1, >= 1.2.7) et-orbi (~> 1, >= 1.2.11)
raabro (~> 1.4) raabro (~> 1.4)
fuubar (2.5.1) fuubar (2.5.1)
rspec-core (~> 3.0) rspec-core (~> 3.0)
@ -360,7 +350,7 @@ GEM
httplog (1.6.2) httplog (1.6.2)
rack (>= 2.0) rack (>= 2.0)
rainbow (>= 2.0.0) rainbow (>= 2.0.0)
i18n (1.14.1) i18n (1.14.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
i18n-tasks (1.0.12) i18n-tasks (1.0.12)
activesupport (>= 4.0.2) activesupport (>= 4.0.2)
@ -377,19 +367,19 @@ GEM
ipaddress (0.8.3) ipaddress (0.8.3)
jmespath (1.6.2) jmespath (1.6.2)
json (2.6.3) json (2.6.3)
json-canonicalization (0.3.2) json-canonicalization (1.0.0)
json-jwt (1.15.3) json-jwt (1.15.3.1)
activesupport (>= 4.2) activesupport (>= 4.2)
aes_key_wrap aes_key_wrap
bindata bindata
httpclient httpclient
json-ld (3.2.5) json-ld (3.3.1)
htmlentities (~> 4.3) htmlentities (~> 4.3)
json-canonicalization (~> 0.3, >= 0.3.2) json-canonicalization (~> 1.0)
link_header (~> 0.0, >= 0.0.8) link_header (~> 0.0, >= 0.0.8)
multi_json (~> 1.15) multi_json (~> 1.15)
rack (>= 2.2, < 4) rack (>= 2.2, < 4)
rdf (~> 3.2, >= 3.2.10) rdf (~> 3.3)
json-ld-preloaded (3.2.2) json-ld-preloaded (3.2.2)
json-ld (~> 3.2) json-ld (~> 3.2)
rdf (~> 3.2) rdf (~> 3.2)
@ -434,7 +424,7 @@ GEM
activesupport (>= 4) activesupport (>= 4)
railties (>= 4) railties (>= 4)
request_store (~> 1.0) request_store (~> 1.0)
loofah (2.21.3) loofah (2.21.4)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.12.0) nokogiri (>= 1.12.0)
mail (2.8.1) mail (2.8.1)
@ -442,7 +432,7 @@ GEM
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
marcel (1.0.2) marcel (1.0.4)
mario-redis-lock (1.2.1) mario-redis-lock (1.2.1)
redis (>= 3.0.5) redis (>= 3.0.5)
matrix (0.4.2) matrix (0.4.2)
@ -456,7 +446,7 @@ GEM
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
mime-types-data (3.2023.0808) mime-types-data (3.2023.0808)
mini_mime (1.1.5) mini_mime (1.1.5)
mini_portile2 (2.8.4) mini_portile2 (2.8.7)
minitest (5.19.0) minitest (5.19.0)
msgpack (1.7.1) msgpack (1.7.1)
multi_json (1.15.0) multi_json (1.15.0)
@ -471,28 +461,33 @@ GEM
net-ldap (0.18.0) net-ldap (0.18.0)
net-pop (0.1.2) net-pop (0.1.2)
net-protocol net-protocol
net-protocol (0.2.1) net-protocol (0.2.2)
timeout timeout
net-scp (4.0.0) net-scp (4.0.0)
net-ssh (>= 2.6.5, < 8.0.0) net-ssh (>= 2.6.5, < 8.0.0)
net-smtp (0.3.3) net-smtp (0.3.4)
net-protocol net-protocol
net-ssh (7.1.0) net-ssh (7.1.0)
nio4r (2.5.9) nio4r (2.7.3)
nokogiri (1.15.4) nokogiri (1.16.7)
mini_portile2 (~> 2.8.2) mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
nsa (0.3.0)
activesupport (>= 4.2, < 7.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
sidekiq (>= 3.5)
statsd-ruby (~> 1.4, >= 1.4.0)
oj (3.16.1) oj (3.16.1)
omniauth (2.1.1) omniauth (2.1.2)
hashie (>= 3.4.6) hashie (>= 3.4.6)
rack (>= 2.2.3) rack (>= 2.2.3)
rack-protection rack-protection
omniauth-rails_csrf_protection (1.0.1) omniauth-rails_csrf_protection (1.0.1)
actionpack (>= 4.2) actionpack (>= 4.2)
omniauth (~> 2.0) omniauth (~> 2.0)
omniauth-saml (2.1.0) omniauth-saml (2.1.2)
omniauth (~> 2.0) omniauth (~> 2.1)
ruby-saml (~> 1.12) ruby-saml (~> 1.17)
omniauth_openid_connect (0.6.1) omniauth_openid_connect (0.6.1)
omniauth (>= 1.9, < 3) omniauth (>= 1.9, < 3)
openid_connect (~> 1.1) openid_connect (~> 1.1)
@ -519,10 +514,9 @@ GEM
parslet (2.0.0) parslet (2.0.0)
pastel (0.8.0) pastel (0.8.0)
tty-color (~> 0.5) tty-color (~> 0.5)
pg (1.5.4) pg (1.5.5)
pghero (3.3.4) pghero (3.3.4)
activerecord (>= 6) activerecord (>= 6)
posix-spawn (0.3.15)
premailer (1.21.0) premailer (1.21.0)
addressable addressable
css_parser (>= 1.12.0) css_parser (>= 1.12.0)
@ -533,16 +527,16 @@ GEM
premailer (~> 1.7, >= 1.7.9) premailer (~> 1.7, >= 1.7.9)
private_address_check (0.5.0) private_address_check (0.5.0)
public_suffix (5.0.3) public_suffix (5.0.3)
puma (6.3.1) puma (6.4.3)
nio4r (~> 2.0) nio4r (~> 2.0)
pundit (2.3.0) pundit (2.3.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
raabro (1.4.0) raabro (1.4.0)
racc (1.7.1) racc (1.8.1)
rack (2.2.8) rack (2.2.9)
rack-attack (6.7.0) rack-attack (6.7.0)
rack (>= 1.0, < 4) rack (>= 1.0, < 4)
rack-cors (2.0.1) rack-cors (2.0.2)
rack (>= 2.0.0) rack (>= 2.0.0)
rack-oauth2 (1.21.3) rack-oauth2 (1.21.3)
activesupport activesupport
@ -550,26 +544,26 @@ GEM
httpclient httpclient
json-jwt (>= 1.11.0) json-jwt (>= 1.11.0)
rack (>= 2.1.0) rack (>= 2.1.0)
rack-protection (3.0.5) rack-protection (3.0.6)
rack rack
rack-proxy (0.7.6) rack-proxy (0.7.6)
rack rack
rack-test (2.1.0) rack-test (2.1.0)
rack (>= 1.3) rack (>= 1.3)
rails (7.0.8) rails (7.0.8.4)
actioncable (= 7.0.8) actioncable (= 7.0.8.4)
actionmailbox (= 7.0.8) actionmailbox (= 7.0.8.4)
actionmailer (= 7.0.8) actionmailer (= 7.0.8.4)
actionpack (= 7.0.8) actionpack (= 7.0.8.4)
actiontext (= 7.0.8) actiontext (= 7.0.8.4)
actionview (= 7.0.8) actionview (= 7.0.8.4)
activejob (= 7.0.8) activejob (= 7.0.8.4)
activemodel (= 7.0.8) activemodel (= 7.0.8.4)
activerecord (= 7.0.8) activerecord (= 7.0.8.4)
activestorage (= 7.0.8) activestorage (= 7.0.8.4)
activesupport (= 7.0.8) activesupport (= 7.0.8.4)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 7.0.8) railties (= 7.0.8.4)
rails-controller-testing (1.0.5) rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1) actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1)
@ -584,16 +578,17 @@ GEM
rails-i18n (7.0.7) rails-i18n (7.0.7)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8) railties (>= 6.0.0, < 8)
railties (7.0.8) railties (7.0.8.4)
actionpack (= 7.0.8) actionpack (= 7.0.8.4)
activesupport (= 7.0.8) activesupport (= 7.0.8.4)
method_source method_source
rake (>= 12.2) rake (>= 12.2)
thor (~> 1.0) thor (~> 1.0)
zeitwerk (~> 2.5) zeitwerk (~> 2.5)
rainbow (3.1.1) rainbow (3.1.1)
rake (13.0.6) rake (13.0.6)
rdf (3.2.11) rdf (3.3.1)
bcp47_spec (~> 0.2)
link_header (~> 0.0, >= 0.0.8) link_header (~> 0.0, >= 0.0.8)
rdf-normalize (0.6.1) rdf-normalize (0.6.1)
rdf (~> 3.2) rdf (~> 3.2)
@ -609,8 +604,8 @@ GEM
responders (3.1.0) responders (3.1.0)
actionpack (>= 5.2) actionpack (>= 5.2)
railties (>= 5.2) railties (>= 5.2)
rexml (3.2.6) rexml (3.3.7)
rotp (6.2.2) rotp (6.3.0)
rouge (4.1.2) rouge (4.1.2)
rpam2 (4.0.2) rpam2 (4.0.2)
rqrcode (2.2.0) rqrcode (2.2.0)
@ -673,7 +668,7 @@ GEM
rubocop-factory_bot (~> 2.22) rubocop-factory_bot (~> 2.22)
ruby-prof (1.6.3) ruby-prof (1.6.3)
ruby-progressbar (1.13.0) ruby-progressbar (1.13.0)
ruby-saml (1.15.0) ruby-saml (1.17.0)
nokogiri (>= 1.13.10) nokogiri (>= 1.13.10)
rexml rexml
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
@ -693,7 +688,7 @@ GEM
rubyzip (>= 1.2.2, < 3.0) rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0) websocket (~> 1.0)
semantic_range (3.0.0) semantic_range (3.0.0)
sidekiq (6.5.10) sidekiq (6.5.12)
connection_pool (>= 2.2.5, < 3) connection_pool (>= 2.2.5, < 3)
rack (~> 2.0) rack (~> 2.0)
redis (>= 4.5.0, < 5) redis (>= 4.5.0, < 5)
@ -703,7 +698,7 @@ GEM
rufus-scheduler (~> 3.2) rufus-scheduler (~> 3.2)
sidekiq (>= 6, < 8) sidekiq (>= 6, < 8)
tilt (>= 1.4.0) tilt (>= 1.4.0)
sidekiq-unique-jobs (7.1.29) sidekiq-unique-jobs (7.1.33)
brpoplpush-redis_script (> 0.1.1, <= 2.0.0) brpoplpush-redis_script (> 0.1.1, <= 2.0.0)
concurrent-ruby (~> 1.0, >= 1.0.5) concurrent-ruby (~> 1.0, >= 1.0.5)
redis (< 5.0) redis (< 5.0)
@ -748,9 +743,9 @@ GEM
terrapin (0.6.0) terrapin (0.6.0)
climate_control (>= 0.0.3, < 1.0) climate_control (>= 0.0.3, < 1.0)
test-prof (1.2.3) test-prof (1.2.3)
thor (1.2.2) thor (1.3.1)
tilt (2.2.0) tilt (2.2.0)
timeout (0.4.0) timeout (0.4.1)
tpm-key_attestation (0.12.0) tpm-key_attestation (0.12.0)
bindata (~> 2.4) bindata (~> 2.4)
openssl (> 2.0) openssl (> 2.0)
@ -814,7 +809,7 @@ GEM
xorcist (1.1.3) xorcist (1.1.3)
xpath (3.2.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
zeitwerk (2.6.11) zeitwerk (2.6.16)
PLATFORMS PLATFORMS
ruby ruby
@ -877,6 +872,7 @@ DEPENDENCIES
letter_opener_web (~> 2.0) letter_opener_web (~> 2.0)
link_header (~> 0.0) link_header (~> 0.0)
lograge (~> 0.12) lograge (~> 0.12)
mail (~> 2.8)
mario-redis-lock (~> 1.2) mario-redis-lock (~> 1.2)
md-paperclip-azure (~> 2.2) md-paperclip-azure (~> 2.2)
memory_profiler memory_profiler
@ -884,7 +880,7 @@ DEPENDENCIES
net-http (~> 0.3.2) net-http (~> 0.3.2)
net-ldap (~> 0.18) net-ldap (~> 0.18)
nokogiri (~> 1.15) nokogiri (~> 1.15)
nsa! nsa
oj (~> 3.14) oj (~> 3.14)
omniauth (~> 2.0) omniauth (~> 2.0)
omniauth-cas! omniauth-cas!
@ -895,7 +891,6 @@ DEPENDENCIES
parslet parslet
pg (~> 1.5) pg (~> 1.5)
pghero pghero
posix-spawn
premailer-rails premailer-rails
private_address_check (~> 0.5) private_address_check (~> 0.5)
public_suffix (~> 5.0) public_suffix (~> 5.0)

View file

@ -13,10 +13,8 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through
## Supported Versions ## Supported Versions
| Version | Supported | | Version | Supported |
| ------- | ---------------- | | ------- | --------- |
| 4.2.x | Yes | | 4.2.x | Yes |
| 4.1.x | Yes | | 4.1.x | Yes |
| 4.0.x | Until 2023-10-31 | | < 4.1 | No |
| 3.5.x | Until 2023-12-31 |
| < 3.5 | No |

View file

@ -1,6 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class AccountsIndex < Chewy::Index class AccountsIndex < Chewy::Index
include DatetimeClampingConcern
DEVELOPMENT_SETTINGS = { DEVELOPMENT_SETTINGS = {
filter: { filter: {
english_stop: { english_stop: {
@ -153,7 +155,7 @@ class AccountsIndex < Chewy::Index
field(:following_count, type: 'long', value: ->(account) { account.public_following_count }) field(:following_count, type: 'long', value: ->(account) { account.public_following_count })
field(:followers_count, type: 'long', value: ->(account) { account.public_followers_count }) field(:followers_count, type: 'long', value: ->(account) { account.public_followers_count })
field(:properties, type: 'keyword', value: ->(account) { account.searchable_properties }) field(:properties, type: 'keyword', value: ->(account) { account.searchable_properties })
field(:last_status_at, type: 'date', value: ->(account) { account.last_status_at || account.created_at }) field(:last_status_at, type: 'date', value: ->(account) { clamp_date(account.last_status_at || account.created_at) })
field(:domain, type: 'keyword', value: ->(account) { account.domain || '' }) field(:domain, type: 'keyword', value: ->(account) { account.domain || '' })
field(:display_name, type: 'text', analyzer: 'verbatim') { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } field(:display_name, type: 'text', analyzer: 'verbatim') { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' }
field(:username, type: 'text', analyzer: 'verbatim', value: ->(account) { [account.username, account.domain].compact.join('@') }) { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } field(:username, type: 'text', analyzer: 'verbatim', value: ->(account) { [account.username, account.domain].compact.join('@') }) { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' }

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
module DatetimeClampingConcern
extend ActiveSupport::Concern
MIN_ISO8601_DATETIME = '0000-01-01T00:00:00Z'.to_datetime.freeze
MAX_ISO8601_DATETIME = '9999-12-31T23:59:59Z'.to_datetime.freeze
class_methods do
def clamp_date(datetime)
datetime.clamp(MIN_ISO8601_DATETIME, MAX_ISO8601_DATETIME)
end
end
end

View file

@ -1,6 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class PublicStatusesIndex < Chewy::Index class PublicStatusesIndex < Chewy::Index
include DatetimeClampingConcern
DEVELOPMENT_SETTINGS = { DEVELOPMENT_SETTINGS = {
filter: { filter: {
english_stop: { english_stop: {
@ -154,6 +156,6 @@ class PublicStatusesIndex < Chewy::Index
field(:language, type: 'keyword') field(:language, type: 'keyword')
field(:domain, type: 'keyword', value: ->(status) { status.account.domain || '' }) field(:domain, type: 'keyword', value: ->(status) { status.account.domain || '' })
field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties })
field(:created_at, type: 'date') field(:created_at, type: 'date', value: ->(status) { clamp_date(status.created_at) })
end end
end end

View file

@ -1,6 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class StatusesIndex < Chewy::Index class StatusesIndex < Chewy::Index
include DatetimeClampingConcern
DEVELOPMENT_SETTINGS = { DEVELOPMENT_SETTINGS = {
filter: { filter: {
english_stop: { english_stop: {
@ -184,6 +186,6 @@ class StatusesIndex < Chewy::Index
field(:language, type: 'keyword') field(:language, type: 'keyword')
field(:domain, type: 'keyword', value: ->(status) { status.account.domain || '' }) field(:domain, type: 'keyword', value: ->(status) { status.account.domain || '' })
field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties }) field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties })
field(:created_at, type: 'date') field(:created_at, type: 'date', value: ->(status) { clamp_date(status.created_at) })
end end
end end

View file

@ -1,6 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class TagsIndex < Chewy::Index class TagsIndex < Chewy::Index
include DatetimeClampingConcern
settings index: index_preset(refresh_interval: '30s'), analysis: { settings index: index_preset(refresh_interval: '30s'), analysis: {
analyzer: { analyzer: {
content: { content: {
@ -42,6 +44,6 @@ class TagsIndex < Chewy::Index
field(:name, type: 'text', analyzer: 'content', value: :display_name) { field(:edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content') } field(:name, type: 'text', analyzer: 'content', value: :display_name) { field(:edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'content') }
field(:reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? }) field(:reviewed, type: 'boolean', value: ->(tag) { tag.reviewed? })
field(:usage, type: 'long', value: ->(tag, crutches) { tag.history.aggregate(crutches.time_period).accounts }) field(:usage, type: 'long', value: ->(tag, crutches) { tag.history.aggregate(crutches.time_period).accounts })
field(:last_status_at, type: 'date', value: ->(tag) { tag.last_status_at || tag.created_at }) field(:last_status_at, type: 'date', value: ->(tag) { clamp_date(tag.last_status_at || tag.created_at) })
end end
end end

View file

@ -5,8 +5,6 @@ class ActivityPub::ReferencesController < ActivityPub::BaseController
include Authorization include Authorization
include AccountOwnedConcern include AccountOwnedConcern
REFERENCES_LIMIT = 5
before_action :require_signature!, if: :authorized_fetch_mode? before_action :require_signature!, if: :authorized_fetch_mode?
before_action :set_status before_action :set_status
@ -40,17 +38,21 @@ class ActivityPub::ReferencesController < ActivityPub::BaseController
@results ||= begin @results ||= begin
references = @status.reference_objects.order(target_status_id: :asc) references = @status.reference_objects.order(target_status_id: :asc)
references = references.where('target_status_id > ?', page_params[:min_id]) if page_params[:min_id].present? references = references.where('target_status_id > ?', page_params[:min_id]) if page_params[:min_id].present?
references = references.limit(limit_param(REFERENCES_LIMIT)) references = references.limit(limit_param(references_limit))
references.pluck(:target_status_id) references.pluck(:target_status_id)
end end
end end
def references_limit
StatusReference::REFERENCES_LIMIT
end
def pagination_min_id def pagination_min_id
results.last results.last
end end
def records_continue? def records_continue?
results.size == limit_param(REFERENCES_LIMIT) results.size == limit_param(references_limit)
end end
def references_collection_presenter def references_collection_presenter

View file

@ -14,7 +14,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
before_action :set_replies before_action :set_replies
def index def index
expires_in 0, public: public_fetch_mode? expires_in 0, public: @status.distributable? && public_fetch_mode?
render json: replies_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', skip_activities: true render json: replies_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', skip_activities: true
end end

View file

@ -21,7 +21,7 @@ module Admin
account_action.save! account_action.save!
if account_action.with_report? if account_action.with_report?
redirect_to admin_reports_path, notice: I18n.t('admin.reports.processed_msg', id: params[:report_id]) redirect_to admin_reports_path, notice: I18n.t('admin.reports.processed_msg', id: resource_params[:report_id])
else else
redirect_to admin_account_path(@account.id) redirect_to admin_account_path(@account.id)
end end

View file

@ -25,6 +25,8 @@ class Admin::DomainAllowsController < Admin::BaseController
def destroy def destroy
authorize @domain_allow, :destroy? authorize @domain_allow, :destroy?
UnallowDomainService.new.call(@domain_allow) UnallowDomainService.new.call(@domain_allow)
log_action :destroy, @domain_allow
redirect_to admin_instances_path, notice: I18n.t('admin.domain_allows.destroyed_msg') redirect_to admin_instances_path, notice: I18n.t('admin.domain_allows.destroyed_msg')
end end

View file

@ -88,18 +88,18 @@ module Admin
end end
def update_params def update_params
params.require(:domain_block).permit(:severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, params.require(:domain_block).permit(:severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_sensitive, :reject_hashtag,
:reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) :reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden)
end end
def resource_params def resource_params
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_sensitive, :reject_hashtag,
:reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) :reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden)
end end
def form_domain_block_batch_params def form_domain_block_batch_params
params.require(:form_domain_block_batch).permit(domain_blocks_attributes: [:enabled, :domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, params.require(:form_domain_block_batch).permit(domain_blocks_attributes: [:enabled, :domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers,
:reject_send_sensitive, :reject_hashtag, :reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous]) :reject_send_sensitive, :reject_hashtag, :reject_straight_follow, :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden])
end end
def action_from_button def action_from_button

View file

@ -25,6 +25,6 @@ class Api::V1::Accounts::NotesController < Api::BaseController
end end
def relationships_presenter def relationships_presenter
AccountRelationshipsPresenter.new([@account.id], current_user.account_id) AccountRelationshipsPresenter.new([@account], current_user.account_id)
end end
end end

View file

@ -25,6 +25,6 @@ class Api::V1::Accounts::PinsController < Api::BaseController
end end
def relationships_presenter def relationships_presenter
AccountRelationshipsPresenter.new([@account.id], current_user.account_id) AccountRelationshipsPresenter.new([@account], current_user.account_id)
end end
end end

View file

@ -5,11 +5,10 @@ class Api::V1::Accounts::RelationshipsController < Api::BaseController
before_action :require_user! before_action :require_user!
def index def index
accounts = Account.without_suspended.where(id: account_ids).select('id') @accounts = Account.without_suspended.where(id: account_ids).select(:id, :domain).to_a
# .where doesn't guarantee that our results are in the same order # .where doesn't guarantee that our results are in the same order
# we requested them, so return the "right" order to the requestor. # we requested them, so return the "right" order to the requestor.
@accounts = accounts.index_by(&:id).values_at(*account_ids).compact render json: @accounts.index_by(&:id).values_at(*account_ids).compact, each_serializer: REST::RelationshipSerializer, relationships: relationships
render json: @accounts, each_serializer: REST::RelationshipSerializer, relationships: relationships
end end
private private

View file

@ -86,7 +86,7 @@ class Api::V1::AccountsController < Api::BaseController
end end
def relationships(**options) def relationships(**options)
AccountRelationshipsPresenter.new([@account.id], current_user.account_id, **options) AccountRelationshipsPresenter.new([@account], current_user.account_id, **options)
end end
def account_params def account_params

View file

@ -29,10 +29,11 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController
def create def create
authorize :domain_block, :create? authorize :domain_block, :create?
@domain_block = DomainBlock.new(resource_params)
existing_domain_block = resource_params[:domain].present? ? DomainBlock.rule_for(resource_params[:domain]) : nil existing_domain_block = resource_params[:domain].present? ? DomainBlock.rule_for(resource_params[:domain]) : nil
return render json: existing_domain_block, serializer: REST::Admin::ExistingDomainBlockErrorSerializer, status: 422 if existing_domain_block.present? return render json: existing_domain_block, serializer: REST::Admin::ExistingDomainBlockErrorSerializer, status: 422 if conflicts_with_existing_block?(@domain_block, existing_domain_block)
@domain_block = DomainBlock.create!(resource_params) @domain_block.save!
DomainBlockWorker.perform_async(@domain_block.id) DomainBlockWorker.perform_async(@domain_block.id)
log_action :create, @domain_block log_action :create, @domain_block
render json: @domain_block, serializer: REST::Admin::DomainBlockSerializer render json: @domain_block, serializer: REST::Admin::DomainBlockSerializer
@ -55,6 +56,10 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController
private private
def conflicts_with_existing_block?(domain_block, existing_domain_block)
existing_domain_block.present? && (existing_domain_block.domain == TagManager.instance.normalize_domain(domain_block.domain) || !domain_block.stricter_than?(existing_domain_block))
end
def set_domain_blocks def set_domain_blocks
@domain_blocks = filtered_domain_blocks.order(id: :desc).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) @domain_blocks = filtered_domain_blocks.order(id: :desc).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end end
@ -69,8 +74,8 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController
end end
def domain_block_params def domain_block_params
params.permit(:severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_reports, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, :reject_straight_follow, params.permit(:severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_reports, :reject_send_sensitive, :reject_hashtag, :reject_straight_follow,
:reject_new_follow, :detect_invalid_subscription, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) :reject_new_follow, :detect_invalid_subscription, :private_comment, :public_comment, :obfuscate, :hidden)
end end
def insert_pagination_headers def insert_pagination_headers
@ -102,7 +107,7 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController
end end
def resource_params def resource_params
params.permit(:domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_not_public_searchability, :reject_send_public_unlisted, :reject_send_dissubscribable, :reject_send_media, :reject_send_sensitive, :reject_hashtag, :reject_straight_follow, params.permit(:domain, :severity, :reject_media, :reject_favourite, :reject_reply, :reject_reply_exclude_followers, :reject_send_sensitive, :reject_hashtag, :reject_straight_follow,
:reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden, :hidden_anonymous) :reject_new_follow, :detect_invalid_subscription, :reject_reports, :private_comment, :public_comment, :obfuscate, :hidden)
end end
end end

View file

@ -12,6 +12,10 @@ class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController
private private
def set_recently_used_tags def set_recently_used_tags
@recently_used_tags = Tag.recently_used(current_account).where.not(id: current_account.featured_tags).limit(10) @recently_used_tags = Tag.recently_used(current_account).where.not(id: featured_tag_ids).limit(10)
end
def featured_tag_ids
current_account.featured_tags.pluck(:tag_id)
end end
end end

View file

@ -25,11 +25,11 @@ class Api::V1::FollowRequestsController < Api::BaseController
private private
def account def account
Account.find(params[:id]) @account ||= Account.find(params[:id])
end end
def relationships(**options) def relationships(**options)
AccountRelationshipsPresenter.new([params[:id]], current_user.account_id, **options) AccountRelationshipsPresenter.new([account], current_user.account_id, **options)
end end
def load_accounts def load_accounts

View file

@ -26,6 +26,5 @@ class Api::V1::Instances::DomainBlocksController < Api::BaseController
def set_domain_blocks def set_domain_blocks
@domain_blocks = DomainBlock.with_user_facing_limitations.by_severity @domain_blocks = DomainBlock.with_user_facing_limitations.by_severity
@domain_blocks = @domain_blocks.filter { |block| !block.hidden_anonymous } unless user_signed_in?
end end
end end

View file

@ -6,6 +6,7 @@ class Api::V1::ScheduledStatusesController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, except: [:update, :destroy] before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, except: [:update, :destroy]
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:update, :destroy] before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:update, :destroy]
before_action :require_user!
before_action :set_statuses, only: :index before_action :set_statuses, only: :index
before_action :set_status, except: :index before_action :set_status, except: :index

View file

@ -4,6 +4,7 @@ class Api::V1::Statuses::TranslationsController < Api::BaseController
include Authorization include Authorization
before_action -> { doorkeeper_authorize! :read, :'read:statuses' } before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
before_action :require_user!
before_action :set_status before_action :set_status
before_action :set_translation before_action :set_translation

View file

@ -2,7 +2,7 @@
class Api::V1::StreamingController < Api::BaseController class Api::V1::StreamingController < Api::BaseController
def index def index
if Rails.configuration.x.streaming_api_base_url == request.host if same_host?
not_found not_found
else else
redirect_to streaming_api_url, status: 301, allow_other_host: true redirect_to streaming_api_url, status: 301, allow_other_host: true
@ -11,9 +11,16 @@ class Api::V1::StreamingController < Api::BaseController
private private
def same_host?
base_url = Addressable::URI.parse(Rails.configuration.x.streaming_api_base_url)
request.host == base_url.host && request.port == (base_url.port || 80)
end
def streaming_api_url def streaming_api_url
Addressable::URI.parse(request.url).tap do |uri| Addressable::URI.parse(request.url).tap do |uri|
uri.host = Addressable::URI.parse(Rails.configuration.x.streaming_api_base_url).host base_url = Addressable::URI.parse(Rails.configuration.x.streaming_api_base_url)
uri.host = base_url.host
uri.port = base_url.port
end.to_s end.to_s
end end
end end

View file

@ -1,6 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Timelines::PublicController < Api::BaseController class Api::V1::Timelines::PublicController < Api::BaseController
before_action -> { authorize_if_got_token! :read, :'read:statuses' }
before_action :require_user!, only: [:show], if: :require_auth? before_action :require_user!, only: [:show], if: :require_auth?
after_action :insert_pagination_headers, unless: -> { @statuses.empty? } after_action :insert_pagination_headers, unless: -> { @statuses.empty? }

View file

@ -1,7 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Timelines::TagController < Api::BaseController class Api::V1::Timelines::TagController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth? before_action -> { authorize_if_got_token! :read, :'read:statuses' }
before_action :require_user!, if: :require_auth?
before_action :load_tag before_action :load_tag
after_action :insert_pagination_headers, unless: -> { @statuses.empty? } after_action :insert_pagination_headers, unless: -> { @statuses.empty? }

View file

@ -6,7 +6,7 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def self.provides_callback_for(provider) def self.provides_callback_for(provider)
define_method provider do define_method provider do
@provider = provider @provider = provider
@user = User.find_for_oauth(request.env['omniauth.auth'], current_user) @user = User.find_for_omniauth(request.env['omniauth.auth'], current_user)
if @user.persisted? if @user.persisted?
record_login_activity record_login_activity
@ -16,6 +16,9 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController
session["devise.#{provider}_data"] = request.env['omniauth.auth'] session["devise.#{provider}_data"] = request.env['omniauth.auth']
redirect_to new_user_registration_url redirect_to new_user_registration_url
end end
rescue ActiveRecord::RecordInvalid
flash[:alert] = I18n.t('devise.failure.omniauth_user_creation_failure') if is_navigational_format?
redirect_to new_user_session_url
end end
end end

View file

@ -1,6 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
class Auth::SessionsController < Devise::SessionsController class Auth::SessionsController < Devise::SessionsController
include Redisable
MAX_2FA_ATTEMPTS_PER_HOUR = 10
layout 'auth' layout 'auth'
skip_before_action :require_no_authentication, only: [:create] skip_before_action :require_no_authentication, only: [:create]
@ -134,9 +138,23 @@ class Auth::SessionsController < Devise::SessionsController
session.delete(:attempt_user_updated_at) session.delete(:attempt_user_updated_at)
end end
def clear_2fa_attempt_from_user(user)
redis.del(second_factor_attempts_key(user))
end
def check_second_factor_rate_limits(user)
attempts, = redis.multi do |multi|
multi.incr(second_factor_attempts_key(user))
multi.expire(second_factor_attempts_key(user), 1.hour)
end
attempts >= MAX_2FA_ATTEMPTS_PER_HOUR
end
def on_authentication_success(user, security_measure) def on_authentication_success(user, security_measure)
@on_authentication_success_called = true @on_authentication_success_called = true
clear_2fa_attempt_from_user(user)
clear_attempt_from_session clear_attempt_from_session
user.update_sign_in!(new_sign_in: true) user.update_sign_in!(new_sign_in: true)
@ -168,4 +186,8 @@ class Auth::SessionsController < Devise::SessionsController
user_agent: request.user_agent user_agent: request.user_agent
) )
end end
def second_factor_attempts_key(user)
"2fa_auth_attempts:#{user.id}:#{Time.now.utc.hour}"
end
end end

View file

@ -180,6 +180,16 @@ module CacheConcern
def render_with_cache(**options) def render_with_cache(**options)
raise ArgumentError, 'Only JSON render calls are supported' unless options.key?(:json) || block_given? raise ArgumentError, 'Only JSON render calls are supported' unless options.key?(:json) || block_given?
if options.delete(:cancel_cache)
if block_given?
options[:json] = yield
elsif options[:json].is_a?(Symbol)
options[:json] = send(options[:json])
end
return render(options)
end
key = options.delete(:key) || [[params[:controller], params[:action]].join('/'), options[:json].respond_to?(:cache_key) ? options[:json].cache_key : nil, options[:fields].nil? ? nil : options[:fields].join(',')].compact.join(':') key = options.delete(:key) || [[params[:controller], params[:action]].join('/'), options[:json].respond_to?(:cache_key) ? options[:json].cache_key : nil, options[:fields].nil? ? nil : options[:fields].join(',')].compact.join(':')
expires_in = options.delete(:expires_in) || 3.minutes expires_in = options.delete(:expires_in) || 3.minutes
body = Rails.cache.read(key, raw: true) body = Rails.cache.read(key, raw: true)
@ -198,34 +208,19 @@ module CacheConcern
end end
end end
# TODO: Rename this method, as it does not perform any caching anymore.
def cache_collection(raw, klass) def cache_collection(raw, klass)
return raw unless klass.respond_to?(:with_includes) return raw unless klass.respond_to?(:preload_cacheable_associations)
raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation) records = raw.to_a
return [] if raw.empty?
cached_keys_with_value = begin klass.preload_cacheable_associations(records)
Rails.cache.read_multi(*raw).transform_keys(&:id).transform_values { |r| ActiveRecordCoder.load(r) }
rescue ActiveRecordCoder::Error
{} # The serialization format may have changed, let's pretend it's a cache miss.
end
uncached_ids = raw.map(&:id) - cached_keys_with_value.keys records
klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!)
unless uncached_ids.empty?
uncached = klass.where(id: uncached_ids).with_includes.index_by(&:id)
uncached.each_value do |item|
Rails.cache.write(item, ActiveRecordCoder.dump(item))
end
end
raw.filter_map { |item| cached_keys_with_value[item.id] || uncached[item.id] }
end end
# TODO: Rename this method, as it does not perform any caching anymore.
def cache_collection_paginated_by_id(raw, klass, limit, options) def cache_collection_paginated_by_id(raw, klass, limit, options)
cache_collection raw.cache_ids.to_a_paginated_by_id(limit, options), klass cache_collection raw.to_a_paginated_by_id(limit, options), klass
end end
end end

View file

@ -91,14 +91,23 @@ module SignatureVerification
raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if actor.nil? raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if actor.nil?
signature = Base64.decode64(signature_params['signature']) signature = Base64.decode64(signature_params['signature'])
compare_signed_string = build_signed_string compare_signed_string = build_signed_string(include_query_string: true)
return actor unless verify_signature(actor, signature, compare_signed_string).nil? return actor unless verify_signature(actor, signature, compare_signed_string).nil?
# Compatibility quirk with older Mastodon versions
compare_signed_string = build_signed_string(include_query_string: false)
return actor unless verify_signature(actor, signature, compare_signed_string).nil?
actor = stoplight_wrap_request { actor_refresh_key!(actor) } actor = stoplight_wrap_request { actor_refresh_key!(actor) }
raise SignatureVerificationError, "Could not refresh public key #{signature_params['keyId']}" if actor.nil? raise SignatureVerificationError, "Could not refresh public key #{signature_params['keyId']}" if actor.nil?
compare_signed_string = build_signed_string(include_query_string: true)
return actor unless verify_signature(actor, signature, compare_signed_string).nil?
# Compatibility quirk with older Mastodon versions
compare_signed_string = build_signed_string(include_query_string: false)
return actor unless verify_signature(actor, signature, compare_signed_string).nil? return actor unless verify_signature(actor, signature, compare_signed_string).nil?
fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)", signed_string: compare_signed_string, signature: signature_params['signature'] fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)", signed_string: compare_signed_string, signature: signature_params['signature']
@ -180,11 +189,18 @@ module SignatureVerification
nil nil
end end
def build_signed_string def build_signed_string(include_query_string: true)
signed_headers.map do |signed_header| signed_headers.map do |signed_header|
case signed_header case signed_header
when Request::REQUEST_TARGET when Request::REQUEST_TARGET
"#{Request::REQUEST_TARGET}: #{request.method.downcase} #{request.path}" if include_query_string
"#{Request::REQUEST_TARGET}: #{request.method.downcase} #{request.original_fullpath}"
else
# Current versions of Mastodon incorrectly omit the query string from the (request-target) pseudo-header.
# Therefore, temporarily support such incorrect signatures for compatibility.
# TODO: remove eventually some time after release of the fixed version
"#{Request::REQUEST_TARGET}: #{request.method.downcase} #{request.path}"
end
when '(created)' when '(created)'
raise SignatureVerificationError, 'Invalid pseudo-header (created) for rsa-sha256' unless signature_algorithm == 'hs2019' raise SignatureVerificationError, 'Invalid pseudo-header (created) for rsa-sha256' unless signature_algorithm == 'hs2019'
raise SignatureVerificationError, 'Pseudo-header (created) used but corresponding argument missing' if signature_params['created'].blank? raise SignatureVerificationError, 'Pseudo-header (created) used but corresponding argument missing' if signature_params['created'].blank?
@ -250,7 +266,7 @@ module SignatureVerification
stoplight_wrap_request { ResolveAccountService.new.call(key_id.delete_prefix('acct:'), suppress_errors: false) } stoplight_wrap_request { ResolveAccountService.new.call(key_id.delete_prefix('acct:'), suppress_errors: false) }
elsif !ActivityPub::TagManager.instance.local_uri?(key_id) elsif !ActivityPub::TagManager.instance.local_uri?(key_id)
account = ActivityPub::TagManager.instance.uri_to_actor(key_id) account = ActivityPub::TagManager.instance.uri_to_actor(key_id)
account ||= stoplight_wrap_request { ActivityPub::FetchRemoteKeyService.new.call(key_id, id: false, suppress_errors: false) } account ||= stoplight_wrap_request { ActivityPub::FetchRemoteKeyService.new.call(key_id, suppress_errors: false) }
account account
end end
rescue Mastodon::PrivateNetworkAddressError => e rescue Mastodon::PrivateNetworkAddressError => e

View file

@ -65,6 +65,11 @@ module TwoFactorAuthenticationConcern
end end
def authenticate_with_two_factor_via_otp(user) def authenticate_with_two_factor_via_otp(user)
if check_second_factor_rate_limits(user)
flash.now[:alert] = I18n.t('users.rate_limited')
return prompt_for_two_factor(user)
end
if valid_otp_attempt?(user) if valid_otp_attempt?(user)
on_authentication_success(user, :otp) on_authentication_success(user, :otp)
else else

View file

@ -17,6 +17,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
def destroy def destroy
Web::PushSubscription.unsubscribe_for(params[:id], current_resource_owner) Web::PushSubscription.unsubscribe_for(params[:id], current_resource_owner)
Doorkeeper::Application.find_by(id: params[:id])&.close_streaming_sessions(current_resource_owner)
super super
end end

View file

@ -33,7 +33,7 @@ class RelationshipsController < ApplicationController
end end
def set_relationships def set_relationships
@relationships = AccountRelationshipsPresenter.new(@accounts.pluck(:id), current_user.account_id) @relationships = AccountRelationshipsPresenter.new(@accounts, current_user.account_id)
end end
def form_account_batch_params def form_account_batch_params

View file

@ -30,15 +30,15 @@ class StatusesController < ApplicationController
end end
format.json do format.json do
expires_in 3.minutes, public: true if @status.distributable? && public_fetch_mode? expires_in 3.minutes, public: true if @status.distributable? && public_fetch_mode? && !misskey_software?
render_with_cache json: @status, content_type: 'application/activity+json', serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter render_with_cache json: @status, content_type: 'application/activity+json', serializer: status_activity_serializer, adapter: ActivityPub::Adapter, cancel_cache: misskey_software?
end end
end end
end end
def activity def activity
expires_in 3.minutes, public: @status.distributable? && public_fetch_mode? expires_in 3.minutes, public: @status.distributable? && public_fetch_mode? && !misskey_software?
render_with_cache json: ActivityPub::ActivityPresenter.from_status(@status), content_type: 'application/activity+json', serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter render_with_cache json: ActivityPub::ActivityPresenter.from_status(@status, for_misskey: misskey_software?), content_type: 'application/activity+json', serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter, cancel_cache: misskey_software?
end end
def embed def embed
@ -76,6 +76,29 @@ class StatusesController < ApplicationController
@instance_presenter = InstancePresenter.new @instance_presenter = InstancePresenter.new
end end
def misskey_software?
return @misskey_software if defined?(@misskey_software)
@misskey_software = false
return false if !@status.local? || signed_request_account&.domain.blank?
info = InstanceInfo.find_by(domain: signed_request_account.domain)
return false if info.nil?
@misskey_software = %w(misskey calckey cherrypick sharkey).include?(info.software) &&
((@status.public_unlisted_visibility? && @status.account.user&.setting_reject_public_unlisted_subscription) ||
(@status.unlisted_visibility? && @status.account.user&.setting_reject_unlisted_subscription))
end
def status_activity_serializer
if misskey_software?
ActivityPub::NoteForMisskeySerializer
else
ActivityPub::NoteSerializer
end
end
def redirect_to_original def redirect_to_original
redirect_to(ActivityPub::TagManager.instance.url_for(@status.reblog), allow_other_host: true) if @status.reblog? redirect_to(ActivityPub::TagManager.instance.url_for(@status.reblog), allow_other_host: true) if @status.reblog?
end end

View file

@ -21,7 +21,7 @@ module WellKnown
username = username_from_resource username = username_from_resource
@account = begin @account = begin
if username == Rails.configuration.x.local_domain if username == Rails.configuration.x.local_domain || username == Rails.configuration.x.web_domain
Account.representative Account.representative
else else
Account.find_local!(username) Account.find_local!(username)

View file

@ -155,8 +155,8 @@ module JsonLdHelper
end end
end end
def fetch_resource(uri, id, on_behalf_of = nil) def fetch_resource(uri, id_is_known, on_behalf_of = nil, request_options: {})
unless id unless id_is_known
json = fetch_resource_without_id_validation(uri, on_behalf_of) json = fetch_resource_without_id_validation(uri, on_behalf_of)
return if !json.is_a?(Hash) || unsupported_uri_scheme?(json['id']) return if !json.is_a?(Hash) || unsupported_uri_scheme?(json['id'])
@ -164,17 +164,29 @@ module JsonLdHelper
uri = json['id'] uri = json['id']
end end
json = fetch_resource_without_id_validation(uri, on_behalf_of) json = fetch_resource_without_id_validation(uri, on_behalf_of, request_options: request_options)
json.present? && json['id'] == uri ? json : nil json.present? && json['id'] == uri ? json : nil
end end
def fetch_resource_without_id_validation(uri, on_behalf_of = nil, raise_on_temporary_error = false) def fetch_resource_without_id_validation(uri, on_behalf_of = nil, raise_on_temporary_error = false, request_options: {})
on_behalf_of ||= Account.representative on_behalf_of ||= Account.representative
build_request(uri, on_behalf_of).perform do |response| build_request(uri, on_behalf_of, options: request_options).perform do |response|
raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response) || !raise_on_temporary_error raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response) || !raise_on_temporary_error
body_to_json(response.body_with_limit) if response.code == 200 body_to_json(response.body_with_limit) if response.code == 200 && valid_activitypub_content_type?(response)
end
end
def valid_activitypub_content_type?(response)
return true if response.mime_type == 'application/activity+json'
# When the mime type is `application/ld+json`, we need to check the profile,
# but `http.rb` does not parse it for us.
return false unless response.mime_type == 'application/ld+json'
response.headers[HTTP::Headers::CONTENT_TYPE]&.split(';')&.map(&:strip)&.any? do |str|
str.start_with?('profile="') && str[9...-1].split.include?('https://www.w3.org/ns/activitystreams')
end end
end end
@ -204,8 +216,8 @@ module JsonLdHelper
response.code == 501 || ((400...500).cover?(response.code) && ![401, 408, 429].include?(response.code)) response.code == 501 || ((400...500).cover?(response.code) && ![401, 408, 429].include?(response.code))
end end
def build_request(uri, on_behalf_of = nil) def build_request(uri, on_behalf_of = nil, options: {})
Request.new(:get, uri).tap do |request| Request.new(:get, uri, **options).tap do |request|
request.on_behalf_of(on_behalf_of) if on_behalf_of request.on_behalf_of(on_behalf_of) if on_behalf_of
request.add_headers('Accept' => 'application/activity+json, application/ld+json') request.add_headers('Accept' => 'application/activity+json, application/ld+json')
end end

View file

@ -254,6 +254,7 @@ module LanguagesHelper
def valid_locale_or_nil(str) def valid_locale_or_nil(str)
return if str.blank? return if str.blank?
return str if valid_locale?(str)
code, = str.to_s.split(/[_-]/) # Strip out the region from e.g. en_US or ja-JP code, = str.to_s.split(/[_-]/) # Strip out the region from e.g. en_US or ja-JP

View file

@ -20,7 +20,7 @@ export function changeSetting(path, value) {
} }
const debouncedSave = debounce((dispatch, getState) => { const debouncedSave = debounce((dispatch, getState) => {
if (getState().getIn(['settings', 'saved'])) { if (getState().getIn(['settings', 'saved']) || !getState().getIn(['meta', 'me'])) {
return; return;
} }

View file

@ -124,7 +124,7 @@ class ReportReasonSelector extends PureComponent {
api().put(`/api/v1/admin/reports/${id}`, { api().put(`/api/v1/admin/reports/${id}`, {
category, category,
rule_ids, rule_ids: category === 'violation' ? rule_ids : [],
}).catch(err => { }).catch(err => {
console.error(err); console.error(err);
}); });

View file

@ -1,9 +1,16 @@
/* eslint-disable @typescript-eslint/no-unsafe-call,
@typescript-eslint/no-unsafe-return,
@typescript-eslint/no-unsafe-assignment,
@typescript-eslint/no-unsafe-member-access
-- the settings store is not yet typed */
import type { PropsWithChildren } from 'react'; import type { PropsWithChildren } from 'react';
import { useCallback, useState } from 'react'; import { useCallback, useState, useEffect } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { changeSetting } from 'mastodon/actions/settings';
import { bannerSettings } from 'mastodon/settings'; import { bannerSettings } from 'mastodon/settings';
import { useAppSelector, useAppDispatch } from 'mastodon/store';
import { IconButton } from './icon_button'; import { IconButton } from './icon_button';
@ -19,13 +26,25 @@ export const DismissableBanner: React.FC<PropsWithChildren<Props>> = ({
id, id,
children, children,
}) => { }) => {
const [visible, setVisible] = useState(!bannerSettings.get(id)); const dismissed = useAppSelector((state) =>
state.settings.getIn(['dismissed_banners', id], false),
);
const dispatch = useAppDispatch();
const [visible, setVisible] = useState(!bannerSettings.get(id) && !dismissed);
const intl = useIntl(); const intl = useIntl();
const handleDismiss = useCallback(() => { const handleDismiss = useCallback(() => {
setVisible(false); setVisible(false);
bannerSettings.set(id, true); bannerSettings.set(id, true);
}, [id]); dispatch(changeSetting(['dismissed_banners', id], true));
}, [id, dispatch]);
useEffect(() => {
if (!visible && !dismissed) {
dispatch(changeSetting(['dismissed_banners', id], true));
}
}, [id, dispatch, visible, dismissed]);
if (!visible) { if (!visible) {
return null; return null;

View file

@ -40,6 +40,7 @@ class DropdownMenu extends PureComponent {
if (this.node && !this.node.contains(e.target)) { if (this.node && !this.node.contains(e.target)) {
this.props.onClose(); this.props.onClose();
e.stopPropagation(); e.stopPropagation();
e.preventDefault();
} }
}; };

View file

@ -156,6 +156,7 @@ class PrivacyDropdown extends PureComponent {
value: PropTypes.string.isRequired, value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
noDirect: PropTypes.bool, noDirect: PropTypes.bool,
noLimited: PropTypes.bool,
container: PropTypes.func, container: PropTypes.func,
disabled: PropTypes.bool, disabled: PropTypes.bool,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -249,6 +250,10 @@ class PrivacyDropdown extends PureComponent {
if (this.props.noDirect) { if (this.props.noDirect) {
this.selectableOptions = this.selectableOptions.filter((opt) => opt.value !== 'direct'); this.selectableOptions = this.selectableOptions.filter((opt) => opt.value !== 'direct');
} }
if (this.props.noLimited) {
this.selectableOptions = this.selectableOptions.filter((opt) => !['mutual', 'circle'].includes(opt.value));
}
} }
setTargetRef = c => { setTargetRef = c => {

View file

@ -277,6 +277,7 @@ class Search extends PureComponent {
} }
_calculateOptions (value) { _calculateOptions (value) {
const { signedIn } = this.context.identity;
const trimmedValue = value.trim(); const trimmedValue = value.trim();
const options = []; const options = [];
@ -301,7 +302,7 @@ class Search extends PureComponent {
const couldBeStatusSearch = searchEnabled; const couldBeStatusSearch = searchEnabled;
if (couldBeStatusSearch) { if (couldBeStatusSearch && signedIn) {
options.push({ key: 'status-search', label: <FormattedMessage id='search.quick_action.status_search' defaultMessage='Posts matching {x}' values={{ x: <mark>{trimmedValue}</mark> }} />, action: this.handleStatusSearch }); options.push({ key: 'status-search', label: <FormattedMessage id='search.quick_action.status_search' defaultMessage='Posts matching {x}' values={{ x: <mark>{trimmedValue}</mark> }} />, action: this.handleStatusSearch });
} }
@ -378,7 +379,7 @@ class Search extends PureComponent {
<h4><FormattedMessage id='search_popout.options' defaultMessage='Search options' /></h4> <h4><FormattedMessage id='search_popout.options' defaultMessage='Search options' /></h4>
{searchEnabled ? ( {searchEnabled && signedIn ? (
<div className='search__popout__menu'> <div className='search__popout__menu'>
{this.defaultOptions.map(({ key, label, action }, i) => ( {this.defaultOptions.map(({ key, label, action }, i) => (
<button key={key} onMouseDown={action} className={classNames('search__popout__menu__item', { selected: selectedOption === ((options.length || recent.size) + i) })}> <button key={key} onMouseDown={action} className={classNames('search__popout__menu__item', { selected: selectedOption === ((options.length || recent.size) + i) })}>
@ -388,7 +389,11 @@ class Search extends PureComponent {
</div> </div>
) : ( ) : (
<div className='search__popout__menu__message'> <div className='search__popout__menu__message'>
<FormattedMessage id='search_popout.full_text_search_disabled_message' defaultMessage='Not available on {domain}.' values={{ domain }} /> {searchEnabled ? (
<FormattedMessage id='search_popout.full_text_search_logged_out_message' defaultMessage='Only available when logged in.' />
) : (
<FormattedMessage id='search_popout.full_text_search_disabled_message' defaultMessage='Not available on {domain}.' values={{ domain }} />
)}
</div> </div>
)} )}
</div> </div>

View file

@ -4,7 +4,7 @@ import { PureComponent } from 'react';
const iconStyle = { const iconStyle = {
height: null, height: null,
lineHeight: '27px', lineHeight: '27px',
width: `${18 * 1.28571429}px`, minWidth: `${18 * 1.28571429}px`,
}; };
export default class TextIconButton extends PureComponent { export default class TextIconButton extends PureComponent {

View file

@ -45,24 +45,20 @@ class Statuses extends PureComponent {
const emptyMessage = <FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />; const emptyMessage = <FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />;
return ( return (
<> <StatusList
<DismissableBanner id='explore/statuses'> trackScroll
<FormattedMessage id='dismissable_banner.explore_statuses' defaultMessage='These are posts from across the social web that are gaining traction today. Newer posts with more boosts and favorites are ranked higher.' /> prepend={<DismissableBanner id='explore/statuses'><FormattedMessage id='dismissable_banner.explore_statuses' defaultMessage='These are posts from across the social web that are gaining traction today. Newer posts with more boosts and favorites are ranked higher.' /></DismissableBanner>}
</DismissableBanner> alwaysPrepend
timelineId='explore'
<StatusList statusIds={statusIds}
trackScroll scrollKey='explore-statuses'
timelineId='explore' hasMore={hasMore}
statusIds={statusIds} isLoading={isLoading}
scrollKey='explore-statuses' onLoadMore={this.handleLoadMore}
hasMore={hasMore} emptyMessage={emptyMessage}
isLoading={isLoading} bindToDocument={!multiColumn}
onLoadMore={this.handleLoadMore} withCounters
emptyMessage={emptyMessage} />
bindToDocument={!multiColumn}
withCounters
/>
</>
); );
} }

View file

@ -207,7 +207,7 @@ class ListTimeline extends PureComponent {
</div> </div>
<div className='setting-toggle'> <div className='setting-toggle'>
<Toggle id={`list-${id}-exclusive`} defaultChecked={isExclusive} onChange={this.onExclusiveToggle} /> <Toggle id={`list-${id}-exclusive`} checked={isExclusive} onChange={this.onExclusiveToggle} />
<label htmlFor={`list-${id}-exclusive`} className='setting-toggle__label'> <label htmlFor={`list-${id}-exclusive`} className='setting-toggle__label'>
<FormattedMessage id='lists.exclusive' defaultMessage='Hide these posts from home or STL' /> <FormattedMessage id='lists.exclusive' defaultMessage='Hide these posts from home or STL' />
</label> </label>

View file

@ -140,6 +140,7 @@ class BoostModal extends ImmutablePureComponent {
{status.get('visibility_ex') !== 'private' && !status.get('reblogged') && ( {status.get('visibility_ex') !== 'private' && !status.get('reblogged') && (
<PrivacyDropdown <PrivacyDropdown
noDirect noDirect
noLimited
value={privacy} value={privacy}
container={this._findContainer} container={this._findContainer}
onChange={this.props.onChangeBoostPrivacy} onChange={this.props.onChangeBoostPrivacy}

View file

@ -221,7 +221,7 @@ class FocalPointModal extends ImmutablePureComponent {
const worker = createWorker({ const worker = createWorker({
workerPath: tesseractWorkerPath, workerPath: tesseractWorkerPath,
corePath: tesseractCorePath, corePath: tesseractCorePath,
langPath: `${assetHost}/ocr/lang-data/`, langPath: `${assetHost}/ocr/lang-data`,
logger: ({ status, progress }) => { logger: ({ status, progress }) => {
if (status === 'recognizing text') { if (status === 'recognizing text') {
this.setState({ ocrStatus: 'detecting', progress }); this.setState({ ocrStatus: 'detecting', progress });

View file

@ -100,7 +100,7 @@ class LinkFooter extends PureComponent {
{DividingCircle} {DividingCircle}
<a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='footer.source_code' defaultMessage='View source code' /></a> <a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='footer.source_code' defaultMessage='View source code' /></a>
{DividingCircle} {DividingCircle}
<span class='version'>v{version}</span> <span className='version'>v{version}</span>
</p> </p>
</div> </div>
); );

View file

@ -66,25 +66,30 @@ class NavigationPanel extends Component {
) : ( ) : (
<ColumnLink transparent to='/search' icon='search' text={intl.formatMessage(messages.search)} /> <ColumnLink transparent to='/search' icon='search' text={intl.formatMessage(messages.search)} />
)); ));
let banner = undefined;
if(transientSingleColumn)
banner = (<div className='switch-to-advanced'>
{intl.formatMessage(messages.openedInClassicInterface)}
{" "}
<a href={`/deck${location.pathname}`} className='switch-to-advanced__toggle'>
{intl.formatMessage(messages.advancedInterface)}
</a>
</div>);
return ( return (
<div className='navigation-panel'> <div className='navigation-panel'>
<div className='navigation-panel__logo'> <div className='navigation-panel__logo'>
<Link to='/' className='column-link column-link--logo'><WordmarkLogo /></Link> <Link to='/' className='column-link column-link--logo'><WordmarkLogo /></Link>
{!banner && <hr />}
{transientSingleColumn ? (
<div class='switch-to-advanced'>
{intl.formatMessage(messages.openedInClassicInterface)}
{" "}
<a href={`/deck${location.pathname}`} class='switch-to-advanced__toggle'>
{intl.formatMessage(messages.advancedInterface)}
</a>
</div>
) : (
<hr />
)}
</div> </div>
{banner &&
<div class='navigation-panel__banner'>
{banner}
</div>
}
{signedIn && ( {signedIn && (
<> <>
<ColumnLink transparent to='/home' icon='home' text={intl.formatMessage(messages.home)} /> <ColumnLink transparent to='/home' icon='home' text={intl.formatMessage(messages.home)} />

View file

@ -625,6 +625,7 @@
"searchability.unlisted.short": "Followers and reactionners", "searchability.unlisted.short": "Followers and reactionners",
"search_popout.domain": "domain", "search_popout.domain": "domain",
"search_popout.full_text_search_disabled_message": "Not available on {domain}.", "search_popout.full_text_search_disabled_message": "Not available on {domain}.",
"search_popout.full_text_search_logged_out_message": "Only available when logged in.",
"search_popout.language_code": "ISO language code", "search_popout.language_code": "ISO language code",
"search_popout.options": "Search options", "search_popout.options": "Search options",
"search_popout.quick_actions": "Quick actions", "search_popout.quick_actions": "Quick actions",

View file

@ -110,6 +110,15 @@ const initialState = ImmutableMap({
body: '', body: '',
}), }),
}), }),
dismissed_banners: ImmutableMap({
'public_timeline': false,
'community_timeline': false,
'home.explore_prompt': false,
'explore/links': false,
'explore/statuses': false,
'explore/tags': false,
}),
}); });
const defaultColumns = fromJS([ const defaultColumns = fromJS([

View file

@ -155,6 +155,10 @@ delegate(document, '#form_admin_settings_enable_bootstrap_timeline_accounts', 'c
const onChangeRegistrationMode = (target) => { const onChangeRegistrationMode = (target) => {
const enabled = target.value === 'approved'; const enabled = target.value === 'approved';
[].forEach.call(document.querySelectorAll('.form_admin_settings_registrations_mode .warning-hint'), (warning_hint) => {
warning_hint.style.display = target.value === 'open' ? 'inline' : 'none';
});
[].forEach.call(document.querySelectorAll('#form_admin_settings_require_invite_text'), (input) => { [].forEach.call(document.querySelectorAll('#form_admin_settings_require_invite_text'), (input) => {
input.disabled = !enabled; input.disabled = !enabled;
if (enabled) { if (enabled) {

View file

@ -284,6 +284,7 @@
font-size: 11px; font-size: 11px;
padding: 0 3px; padding: 0 3px;
line-height: 27px; line-height: 27px;
white-space: nowrap;
&:hover, &:hover,
&:active, &:active,
@ -2303,8 +2304,7 @@ $ui-header-height: 55px;
> .scrollable { > .scrollable {
background: $ui-base-color; background: $ui-base-color;
border-bottom-left-radius: 4px; border-radius: 0 0 4px 4px;
border-bottom-right-radius: 4px;
} }
} }
@ -2530,6 +2530,7 @@ $ui-header-height: 55px;
.navigation-panel__sign-in-banner, .navigation-panel__sign-in-banner,
.navigation-panel__logo, .navigation-panel__logo,
.navigation-panel__banner,
.getting-started__trends { .getting-started__trends {
display: none; display: none;
} }
@ -4690,11 +4691,6 @@ a.status-card {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@supports (display: grid) {
// hack to fix Chrome <57
contain: strict;
}
& > span { & > span {
max-width: 500px; max-width: 500px;
} }

View file

@ -29,12 +29,12 @@ class AccountStatusesFilter
available_searchabilities = [:public, :unlisted, :private, :direct, :limited, nil] available_searchabilities = [:public, :unlisted, :private, :direct, :limited, nil]
available_visibilities = [:public, :public_unlisted, :login, :unlisted, :private, :direct, :limited] available_visibilities = [:public, :public_unlisted, :login, :unlisted, :private, :direct, :limited]
available_searchabilities = [:public] if domain_block&.reject_send_not_public_searchability available_visibilities -= [:public_unlisted] if (domain_block&.detect_invalid_subscription || misskey_software?) && @account.user&.setting_reject_public_unlisted_subscription
available_visibilities -= [:public_unlisted] if domain_block&.reject_send_public_unlisted || (domain_block&.detect_invalid_subscription && @account.user&.setting_reject_public_unlisted_subscription) available_visibilities -= [:unlisted] if (domain_block&.detect_invalid_subscription || misskey_software?) && @account.user&.setting_reject_unlisted_subscription
available_visibilities -= [:unlisted] if domain_block&.detect_invalid_subscription && @account.user&.setting_reject_unlisted_subscription
available_visibilities -= [:login] if current_account.nil? available_visibilities -= [:login] if current_account.nil?
scope.merge!(scope.where(spoiler_text: ['', nil])) if domain_block&.reject_send_sensitive scope.merge!(scope.where(sensitive: false)) if domain_block&.reject_send_sensitive
scope.merge!(scope.where(searchability: available_searchabilities)) scope.merge!(scope.where(searchability: available_searchabilities))
scope.merge!(scope.where(visibility: available_visibilities)) scope.merge!(scope.where(visibility: available_visibilities))
@ -44,7 +44,7 @@ class AccountStatusesFilter
private private
def initial_scope def initial_scope
if (suspended? || (domain_block&.reject_send_dissubscribable && @account.dissubscribable)) || domain_block&.reject_send_media || blocked? if suspended? || blocked?
Status.none Status.none
elsif anonymous? elsif anonymous?
account.statuses.where(visibility: %i(public unlisted public_unlisted)) account.statuses.where(visibility: %i(public unlisted public_unlisted))
@ -156,6 +156,21 @@ class AccountStatusesFilter
end end
def domain_block def domain_block
@domain_block = DomainBlock.find_by(domain: @account&.domain) return nil if @current_account.nil? || @current_account.local?
@domain_block = DomainBlock.find_by(domain: @current_account.domain)
end
def misskey_software?
return false if @account.nil? || @account.local?
return false if instance_info.nil?
%w(misskey cherrypick).include?(instance_info.software)
end
def instance_info
return @instance_info if defined?(@instance_info)
@instance_info = InstanceInfo.find_by(domain: @account.domain)
end end
end end

View file

@ -154,7 +154,7 @@ class ActivityPub::Activity
if object_uri.start_with?('http') if object_uri.start_with?('http')
return if ActivityPub::TagManager.instance.local_uri?(object_uri) return if ActivityPub::TagManager.instance.local_uri?(object_uri)
ActivityPub::FetchRemoteStatusService.new.call(object_uri, id: true, on_behalf_of: @account.followers.local.first, request_id: @options[:request_id]) ActivityPub::FetchRemoteStatusService.new.call(object_uri, on_behalf_of: @account.followers.local.first, request_id: @options[:request_id])
elsif @object['url'].present? elsif @object['url'].present?
::FetchRemoteStatusService.new.call(@object['url'], request_id: @options[:request_id]) ::FetchRemoteStatusService.new.call(@object['url'], request_id: @options[:request_id])
end end

View file

@ -116,7 +116,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
def find_existing_status def find_existing_status
status = status_from_uri(object_uri) status = status_from_uri(object_uri)
status ||= Status.find_by(uri: @object['atomUri']) if @object['atomUri'].present? status ||= Status.find_by(uri: @object['atomUri']) if @object['atomUri'].present?
status status if status&.account_id == @account.id
end end
def process_status_params def process_status_params
@ -365,13 +365,15 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
def fetch_replies(status) def fetch_replies(status)
collection = @object['replies'] collection = @object['replies']
return if collection.nil? return if collection.blank?
replies = ActivityPub::FetchRepliesService.new.call(status, collection, allow_synchronous_requests: false, request_id: @options[:request_id]) replies = ActivityPub::FetchRepliesService.new.call(status, collection, allow_synchronous_requests: false, request_id: @options[:request_id])
return unless replies.nil? return unless replies.nil?
uri = value_or_id(collection) uri = value_or_id(collection)
ActivityPub::FetchRepliesWorker.perform_async(status.id, uri, { 'request_id' => @options[:request_id] }) unless uri.nil? ActivityPub::FetchRepliesWorker.perform_async(status.id, uri, { 'request_id' => @options[:request_id] }) unless uri.nil?
rescue => e
Rails.logger.warn "Error fetching replies: #{e}"
end end
def conversation_from_uri(uri) def conversation_from_uri(uri)
@ -505,15 +507,15 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end end
def searchability_from_audience def searchability_from_audience
if audience_searchable_by.nil? return nil if audience_searchable_by.blank?
nil
elsif audience_searchable_by.any? { |uri| ActivityPub::TagManager.instance.public_collection?(uri) } if audience_searchable_by.any? { |uri| ActivityPub::TagManager.instance.public_collection?(uri) }
:public :public
elsif audience_searchable_by.include?('kmyblue:Limited') || audience_searchable_by.include?('as:Limited') elsif audience_searchable_by.include?('kmyblue:Limited') || audience_searchable_by.include?('as:Limited')
:limited :limited
elsif audience_searchable_by.include?(@account.followers_url) elsif audience_searchable_by.include?(@account.followers_url)
:private :private
else elsif audience_searchable_by.include?(@account.uri) || audience_searchable_by.include?(@account.url)
:direct :direct
end end
end end

View file

@ -20,6 +20,6 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base
serialized_hash = serialized_hash.select { |k, _| options[:fields].include?(k) } if options[:fields] serialized_hash = serialized_hash.select { |k, _| options[:fields].include?(k) } if options[:fields]
serialized_hash = self.class.transform_key_casing!(serialized_hash, instance_options) serialized_hash = self.class.transform_key_casing!(serialized_hash, instance_options)
{ '@context' => serialized_context(named_contexts, context_extensions) }.merge(serialized_hash) { '@context': serialized_context(named_contexts, context_extensions) }.merge(serialized_hash)
end end
end end

View file

@ -21,6 +21,8 @@ module ActivityPub::CaseTransform
value value
elsif value.start_with?('_:') elsif value.start_with?('_:')
"_:#{value.delete_prefix('_:').underscore.camelize(:lower)}" "_:#{value.delete_prefix('_:').underscore.camelize(:lower)}"
elsif LanguagesHelper::ISO_639_1_REGIONAL.key?(value.to_sym) # rubocop:disable Lint/DuplicateBranch
value
else else
value.underscore.camelize(:lower) value.underscore.camelize(:lower)
end end

View file

@ -4,6 +4,7 @@ class ActivityPub::LinkedDataSignature
include JsonLdHelper include JsonLdHelper
CONTEXT = 'https://w3id.org/identity/v1' CONTEXT = 'https://w3id.org/identity/v1'
SIGNATURE_CONTEXT = 'https://w3id.org/security/v1'
def initialize(json) def initialize(json)
@json = json.with_indifferent_access @json = json.with_indifferent_access
@ -18,8 +19,8 @@ class ActivityPub::LinkedDataSignature
return unless type == 'RsaSignature2017' return unless type == 'RsaSignature2017'
creator = ActivityPub::TagManager.instance.uri_to_actor(creator_uri) creator = ActivityPub::TagManager.instance.uri_to_actor(creator_uri)
creator ||= ActivityPub::FetchRemoteKeyService.new.call(creator_uri, id: false) creator = ActivityPub::FetchRemoteKeyService.new.call(creator_uri) if creator&.public_key.blank?
return if creator.nil? return if creator.nil?
@ -28,6 +29,8 @@ class ActivityPub::LinkedDataSignature
to_be_verified = options_hash + document_hash to_be_verified = options_hash + document_hash
creator if creator.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), Base64.decode64(signature), to_be_verified) creator if creator.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), Base64.decode64(signature), to_be_verified)
rescue OpenSSL::PKey::RSAError
false
end end
def sign!(creator, sign_with: nil) def sign!(creator, sign_with: nil)
@ -44,7 +47,13 @@ class ActivityPub::LinkedDataSignature
signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), to_be_signed)) signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), to_be_signed))
@json.merge('signature' => options.merge('signatureValue' => signature)) # Mastodon's context is either an array or a single URL
context_with_security = Array(@json['@context'])
context_with_security << 'https://w3id.org/security/v1'
context_with_security.uniq!
context_with_security = context_with_security.first if context_with_security.size == 1
@json.merge('signature' => options.merge('signatureValue' => signature), '@context' => context_with_security)
end end
private private

View file

@ -3,6 +3,8 @@
class ActivityPub::Parser::StatusParser class ActivityPub::Parser::StatusParser
include JsonLdHelper include JsonLdHelper
NORMALIZED_LOCALE_NAMES = LanguagesHelper::SUPPORTED_LOCALES.keys.index_by(&:downcase).freeze
# @param [Hash] json # @param [Hash] json
# @param [Hash] magic_values # @param [Hash] magic_values
# @option magic_values [String] :followers_collection # @option magic_values [String] :followers_collection
@ -53,7 +55,8 @@ class ActivityPub::Parser::StatusParser
end end
def created_at def created_at
@object['published']&.to_datetime datetime = @object['published']&.to_datetime
datetime if datetime.present? && (0..9999).cover?(datetime.year)
rescue ArgumentError rescue ArgumentError
nil nil
end end
@ -98,6 +101,13 @@ class ActivityPub::Parser::StatusParser
end end
def language def language
lang = raw_language_code
lang.presence && NORMALIZED_LOCALE_NAMES.fetch(lang.downcase.to_sym, lang)
end
private
def raw_language_code
if content_language_map? if content_language_map?
@object['contentMap'].keys.first @object['contentMap'].keys.first
elsif name_language_map? elsif name_language_map?
@ -107,8 +117,6 @@ class ActivityPub::Parser::StatusParser
end end
end end
private
def audience_to def audience_to
as_array(@object['to'] || @json['to']).map { |x| value_or_id(x) } as_array(@object['to'] || @json['to']).map { |x| value_or_id(x) }
end end

View file

@ -119,10 +119,7 @@ class ActivityPub::TagManager
end.compact end.compact
end end
when 'limited' when 'limited'
status.mentions.each_with_object([]) do |mention, result| ['kmyblue:Limited'] # to avoid Fedibird personal visibility
result << uri_for(mention.account)
result << followers_uri_for(mention.account) if mention.account.group?
end.compact
end end
end end
@ -240,12 +237,10 @@ class ActivityPub::TagManager
[COLLECTIONS[:public]] [COLLECTIONS[:public]]
when 'private' when 'private'
[account_followers_url(status.account)] [account_followers_url(status.account)]
when 'direct'
status.conversation_id.present? ? [uri_for(status.conversation)] : []
when 'limited' when 'limited'
['as:Limited', 'kmyblue:Limited'] ['as:Limited', 'kmyblue:Limited']
else else
[] status.conversation_id.present? ? [uri_for(status.conversation), account_url(status.account)] : [account_url(status.account)]
end end
searchable_by.concat(mentions_uris(status)).compact searchable_by.concat(mentions_uris(status)).compact
@ -260,7 +255,7 @@ class ActivityPub::TagManager
when 'limited' when 'limited'
['as:Limited', 'kmyblue:Limited'] ['as:Limited', 'kmyblue:Limited']
else else
[] [account_url(account)]
end end
end end

View file

@ -14,14 +14,16 @@ class Admin::SystemCheck::SoftwareVersionCheck < Admin::SystemCheck::BaseCheck
def message def message
if software_updates.any?(&:urgent?) if software_updates.any?(&:urgent?)
Admin::SystemCheck::Message.new(:software_version_critical_check, nil, admin_software_updates_path, true) Admin::SystemCheck::Message.new(:software_version_critical_check, nil, admin_software_updates_path, true)
else elsif software_updates.any?(&:patch_type?)
Admin::SystemCheck::Message.new(:software_version_patch_check, nil, admin_software_updates_path) Admin::SystemCheck::Message.new(:software_version_patch_check, nil, admin_software_updates_path)
else
Admin::SystemCheck::Message.new(:software_version_check, nil, admin_software_updates_path)
end end
end end
private private
def software_updates def software_updates
@software_updates ||= SoftwareUpdate.pending_to_a.filter { |update| update.urgent? || update.patch_type? } @software_updates ||= SoftwareUpdate.pending_to_a
end end
end end

View file

@ -4,14 +4,36 @@ module ApplicationExtension
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
include Redisable
has_many :created_users, class_name: 'User', foreign_key: 'created_by_application_id', inverse_of: :created_by_application has_many :created_users, class_name: 'User', foreign_key: 'created_by_application_id', inverse_of: :created_by_application
validates :name, length: { maximum: 60 } validates :name, length: { maximum: 60 }
validates :website, url: true, length: { maximum: 2_000 }, if: :website? validates :website, url: true, length: { maximum: 2_000 }, if: :website?
validates :redirect_uri, length: { maximum: 2_000 } validates :redirect_uri, length: { maximum: 2_000 }
# The relationship used between Applications and AccessTokens is using
# dependent: delete_all, which means the ActiveRecord callback in
# AccessTokenExtension is not run, so instead we manually announce to
# streaming that these tokens are being deleted.
before_destroy :close_streaming_sessions, prepend: true
end end
def confirmation_redirect_uri def confirmation_redirect_uri
redirect_uri.lines.first.strip redirect_uri.lines.first.strip
end end
def close_streaming_sessions(resource_owner = nil)
# TODO: #28793 Combine into a single topic
payload = Oj.dump(event: :kill)
scope = access_tokens
scope = scope.where(resource_owner_id: resource_owner.id) unless resource_owner.nil?
scope.in_batches do |tokens|
redis.pipelined do |pipeline|
tokens.ids.each do |id|
pipeline.publish("timeline:access_token:#{id}", payload)
end
end
end
end
end end

View file

@ -75,7 +75,12 @@ class AttachmentBatch
end end
when :fog when :fog
logger.debug { "Deleting #{attachment.path(style)}" } logger.debug { "Deleting #{attachment.path(style)}" }
attachment.directory.files.new(key: attachment.path(style)).destroy
begin
attachment.send(:directory).files.new(key: attachment.path(style)).destroy
rescue Fog::Storage::OpenStack::NotFound
# Ignore failure to delete a file that has already been deleted
end
when :azure when :azure
logger.debug { "Deleting #{attachment.path(style)}" } logger.debug { "Deleting #{attachment.path(style)}" }
attachment.destroy attachment.destroy

View file

@ -594,7 +594,7 @@ class FeedManager
arr = crutches[:active_mentions][s.id] || [] arr = crutches[:active_mentions][s.id] || []
arr.push(s.account_id) arr.push(s.account_id)
if s.reblog? if s.reblog? && s.reblog.present?
arr.push(s.reblog.account_id) arr.push(s.reblog.account_id)
arr.concat(crutches[:active_mentions][s.reblog_of_id] || []) arr.concat(crutches[:active_mentions][s.reblog_of_id] || [])
end end

View file

@ -42,13 +42,13 @@ class InlineRenderer
private private
def preload_associations_for_status def preload_associations_for_status
ActiveRecord::Associations::Preloader.new(records: @object, associations: { ActiveRecord::Associations::Preloader.new(records: [@object], associations: {
active_mentions: :account, active_mentions: :account,
reblog: { reblog: {
active_mentions: :account, active_mentions: :account,
}, },
}) }).call
end end
def current_user def current_user

View file

@ -36,7 +36,9 @@ class LinkDetailsExtractor
end end
def language def language
json['inLanguage'] lang = json['inLanguage']
lang = lang.first if lang.is_a?(Array)
lang.is_a?(Hash) ? (lang['alternateName'] || lang['name']) : lang
end end
def type def type
@ -263,16 +265,21 @@ class LinkDetailsExtractor
end end
def document def document
@document ||= Nokogiri::HTML(@html, nil, encoding) @document ||= detect_encoding_and_parse_document
end end
def encoding def detect_encoding_and_parse_document
@encoding ||= begin [detect_encoding, nil, @html_charset, 'UTF-8'].uniq.each do |encoding|
guess = detector.detect(@html, @html_charset) document = Nokogiri::HTML(@html, nil, encoding)
guess&.fetch(:confidence, 0).to_i > 60 ? guess&.fetch(:encoding, nil) : nil return document if document.to_s.valid_encoding?
end end
end end
def detect_encoding
guess = detector.detect(@html, @html_charset)
guess&.fetch(:confidence, 0).to_i > 60 ? guess&.fetch(:encoding, nil) : nil
end
def detector def detector
@detector ||= CharlockHolmes::EncodingDetector.new.tap do |detector| @detector ||= CharlockHolmes::EncodingDetector.new.tap do |detector|
detector.strip_tags = true detector.strip_tags = true

View file

@ -77,6 +77,7 @@ class Request
@url = Addressable::URI.parse(url).normalize @url = Addressable::URI.parse(url).normalize
@http_client = options.delete(:http_client) @http_client = options.delete(:http_client)
@allow_local = options.delete(:allow_local) @allow_local = options.delete(:allow_local)
@full_path = !options.delete(:omit_query_string)
@options = options.merge(socket_class: use_proxy? || @allow_local ? ProxySocket : Socket) @options = options.merge(socket_class: use_proxy? || @allow_local ? ProxySocket : Socket)
@options = @options.merge(timeout_class: PerOperationWithDeadline, timeout_options: TIMEOUT) @options = @options.merge(timeout_class: PerOperationWithDeadline, timeout_options: TIMEOUT)
@options = @options.merge(proxy_url) if use_proxy? @options = @options.merge(proxy_url) if use_proxy?
@ -146,7 +147,7 @@ class Request
private private
def set_common_headers! def set_common_headers!
@headers[REQUEST_TARGET] = "#{@verb} #{@url.path}" @headers[REQUEST_TARGET] = request_target
@headers['User-Agent'] = Mastodon::Version.user_agent @headers['User-Agent'] = Mastodon::Version.user_agent
@headers['Host'] = @url.host @headers['Host'] = @url.host
@headers['Date'] = Time.now.utc.httpdate @headers['Date'] = Time.now.utc.httpdate
@ -157,6 +158,14 @@ class Request
@headers['Digest'] = "SHA-256=#{Digest::SHA256.base64digest(@options[:body])}" @headers['Digest'] = "SHA-256=#{Digest::SHA256.base64digest(@options[:body])}"
end end
def request_target
if @url.query.nil? || !@full_path
"#{@verb} #{@url.path}"
else
"#{@verb} #{@url.path}?#{@url.query}"
end
end
def signature def signature
algorithm = 'rsa-sha256' algorithm = 'rsa-sha256'
signature = Base64.strict_encode64(@keypair.sign(OpenSSL::Digest.new('SHA256'), signed_string)) signature = Base64.strict_encode64(@keypair.sign(OpenSSL::Digest.new('SHA256'), signed_string))

View file

@ -380,7 +380,7 @@ class SearchQueryTransformer < Parslet::Transform
end end
rule(clause: subtree(:clause)) do rule(clause: subtree(:clause)) do
prefix = clause[:prefix][:term].to_s if clause[:prefix] prefix = clause[:prefix][:term].to_s.downcase if clause[:prefix]
operator = clause[:operator]&.to_s operator = clause[:operator]&.to_s
term = clause[:phrase] ? clause[:phrase].map { |term| term[:term].to_s }.join(' ') : clause[:term].to_s term = clause[:phrase] ? clause[:phrase].map { |term| term[:term].to_s }.join(' ') : clause[:term].to_s

View file

@ -21,43 +21,43 @@ class StatusReachFinder
end end
end end
def inboxes_for_limited
DeliveryFailureTracker.without_unavailable(
@status.mentioned_accounts.where.not(domain: nil).pluck(:inbox_url).compact.uniq
)
end
private private
def reached_account_inboxes def reached_account_inboxes
Account.where(id: reached_account_ids).where.not(domain: banned_domains).inboxes
end
def reached_account_inboxes_for_misskey
Account.where(id: reached_account_ids).where(domain: banned_domains_for_misskey).inboxes
end
def reached_account_ids
# When the status is a reblog, there are no interactions with it # When the status is a reblog, there are no interactions with it
# directly, we assume all interactions are with the original one # directly, we assume all interactions are with the original one
if @status.reblog? if @status.reblog?
[] [reblog_of_account_id]
elsif @status.limited_visibility? elsif @status.limited_visibility?
Account.where(id: mentioned_account_ids).where.not(domain: banned_domains).inboxes [mentioned_account_ids]
else else
Account.where(id: reached_account_ids).where.not(domain: banned_domains).inboxes [
end replied_to_account_id,
end reblog_of_account_id,
mentioned_account_ids,
def reached_account_inboxes_for_misskey reblogs_account_ids,
if @status.reblog? favourites_account_ids,
[] replies_account_ids,
elsif @status.limited_visibility? ].tap do |arr|
Account.where(id: mentioned_account_ids).where(domain: banned_domains_for_misskey).inboxes arr.flatten!
else arr.compact!
Account.where(id: reached_account_ids).where(domain: banned_domains_for_misskey).inboxes arr.uniq!
end end
end
def reached_account_ids
[
replied_to_account_id,
reblog_of_account_id,
mentioned_account_ids,
reblogs_account_ids,
favourites_account_ids,
replies_account_ids,
].tap do |arr|
arr.flatten!
arr.compact!
arr.uniq!
end end
end end
@ -137,10 +137,6 @@ class StatusReachFinder
[] []
else else
blocks = DomainBlock.where(domain: nil) blocks = DomainBlock.where(domain: nil)
blocks = blocks.or(DomainBlock.where(reject_send_not_public_searchability: true)) if status.compute_searchability != 'public'
blocks = blocks.or(DomainBlock.where(reject_send_public_unlisted: true)) if status.public_unlisted_visibility?
blocks = blocks.or(DomainBlock.where(reject_send_dissubscribable: true)) if status.account.dissubscribable
blocks = blocks.or(DomainBlock.where(reject_send_media: true)) if status.with_media?
blocks = blocks.or(DomainBlock.where(reject_send_sensitive: true)) if (status.with_media? && status.sensitive) || status.spoiler_text? blocks = blocks.or(DomainBlock.where(reject_send_sensitive: true)) if (status.with_media? && status.sensitive) || status.spoiler_text?
blocks.pluck(:domain).uniq blocks.pluck(:domain).uniq
end end

View file

@ -1,10 +0,0 @@
# frozen_string_literal: true
class Vacuum::ApplicationsVacuum
def perform
Doorkeeper::Application.where(owner_id: nil)
.where.missing(:created_users, :access_tokens, :access_grants)
.where(created_at: ...1.day.ago)
.in_batches.delete_all
end
end

View file

@ -4,6 +4,7 @@ class Vacuum::FeedsVacuum
def perform def perform
vacuum_inactive_home_feeds! vacuum_inactive_home_feeds!
vacuum_inactive_list_feeds! vacuum_inactive_list_feeds!
vacuum_inactive_antenna_feeds!
end end
private private
@ -20,6 +21,12 @@ class Vacuum::FeedsVacuum
end end
end end
def vacuum_inactive_antenna_feeds!
inactive_users_antennas.select(:id).in_batches do |antennas|
feed_manager.clean_feeds!(:antenna, antennas.ids)
end
end
def inactive_users def inactive_users
User.confirmed.inactive User.confirmed.inactive
end end
@ -28,6 +35,10 @@ class Vacuum::FeedsVacuum
List.where(account_id: inactive_users.select(:account_id)) List.where(account_id: inactive_users.select(:account_id))
end end
def inactive_users_antennas
Antenna.where(account_id: inactive_users.select(:account_id))
end
def feed_manager def feed_manager
FeedManager.instance FeedManager.instance
end end

View file

@ -22,7 +22,7 @@ class VideoMetadataExtractor
private private
def ffmpeg_command_output def ffmpeg_command_output
command = Terrapin::CommandLine.new('ffprobe', '-i :path -print_format :format -show_format -show_streams -show_error -loglevel :loglevel') command = Terrapin::CommandLine.new(Rails.configuration.x.ffprobe_binary, '-i :path -print_format :format -show_format -show_streams -show_error -loglevel :loglevel')
command.run(path: @path, format: 'json', loglevel: 'fatal') command.run(path: @path, format: 'json', loglevel: 'fatal')
end end
@ -41,8 +41,8 @@ class VideoMetadataExtractor
@colorspace = video_stream[:pix_fmt] @colorspace = video_stream[:pix_fmt]
@width = video_stream[:width] @width = video_stream[:width]
@height = video_stream[:height] @height = video_stream[:height]
@frame_rate = video_stream[:avg_frame_rate] == '0/0' ? nil : Rational(video_stream[:avg_frame_rate]) @frame_rate = parse_framerate(video_stream[:avg_frame_rate])
@r_frame_rate = video_stream[:r_frame_rate] == '0/0' ? nil : Rational(video_stream[:r_frame_rate]) @r_frame_rate = parse_framerate(video_stream[:r_frame_rate])
# For some video streams the frame_rate reported by `ffprobe` will be 0/0, but for these streams we # For some video streams the frame_rate reported by `ffprobe` will be 0/0, but for these streams we
# should use `r_frame_rate` instead. Video screencast generated by Gnome Screencast have this issue. # should use `r_frame_rate` instead. Video screencast generated by Gnome Screencast have this issue.
@frame_rate ||= @r_frame_rate @frame_rate ||= @r_frame_rate
@ -55,4 +55,10 @@ class VideoMetadataExtractor
@invalid = true if @metadata.key?(:error) @invalid = true if @metadata.key?(:error)
end end
def parse_framerate(raw)
Rational(raw)
rescue ZeroDivisionError
nil
end
end end

View file

@ -6,6 +6,8 @@ class Webfinger
class RedirectError < Error; end class RedirectError < Error; end
class Response class Response
ACTIVITYPUB_READY_TYPE = ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].freeze
attr_reader :uri attr_reader :uri
def initialize(uri, body) def initialize(uri, body)
@ -20,17 +22,28 @@ class Webfinger
end end
def link(rel, attribute) def link(rel, attribute)
links.dig(rel, attribute) links.dig(rel, 0, attribute)
end
def self_link_href
self_link.fetch('href')
end end
private private
def links def links
@links ||= @json['links'].index_by { |link| link['rel'] } @links ||= @json.fetch('links', []).group_by { |link| link['rel'] }
end
def self_link
links.fetch('self', []).find do |link|
ACTIVITYPUB_READY_TYPE.include?(link['type'])
end
end end
def validate_response! def validate_response!
raise Webfinger::Error, "Missing subject in response for #{@uri}" if subject.blank? raise Webfinger::Error, "Missing subject in response for #{@uri}" if subject.blank?
raise Webfinger::Error, "Missing self link in response for #{@uri}" if self_link.blank?
end end
end end

View file

@ -61,6 +61,12 @@ class AdminMailer < ApplicationMailer
end end
end end
def auto_close_registrations
locale_for_account(@me) do
mail subject: default_i18n_subject(instance: @instance)
end
end
private private
def process_params def process_params

View file

@ -69,8 +69,8 @@ class Account < ApplicationRecord
BACKGROUND_REFRESH_INTERVAL = 1.week.freeze BACKGROUND_REFRESH_INTERVAL = 1.week.freeze
USERNAME_RE = /[a-z0-9_]+([a-z0-9_.-]+[a-z0-9_]+)?/i USERNAME_RE = /[a-z0-9_]+([.-]+[a-z0-9_]+)*/i
MENTION_RE = %r{(?<![=/[:word:]])@((#{USERNAME_RE})(?:@[[:word:].-]+[[:word:]]+)?)}i MENTION_RE = %r{(?<![=/[:word:]])@((#{USERNAME_RE})(?:@[[:word:]]+([.-]+[[:word:]]+)*)?)}
URL_PREFIX_RE = %r{\Ahttp(s?)://[^/]+} URL_PREFIX_RE = %r{\Ahttp(s?)://[^/]+}
USERNAME_ONLY_RE = /\A#{USERNAME_RE}\z/i USERNAME_ONLY_RE = /\A#{USERNAME_RE}\z/i
@ -323,13 +323,6 @@ class Account < ApplicationRecord
user&.setting_translatable_private || (settings.present? && settings['translatable_private']) || false user&.setting_translatable_private || (settings.present? && settings['translatable_private']) || false
end end
def link_preview?
return user.setting_link_preview if local? && user.present?
return settings['link_preview'] if settings.present? && settings.key?('link_preview')
true
end
def public_statuses_count def public_statuses_count
hide_statuses_count? ? 0 : statuses_count hide_statuses_count? ? 0 : statuses_count
end end
@ -406,7 +399,6 @@ class Account < ApplicationRecord
'hide_following_count' => hide_following_count?, 'hide_following_count' => hide_following_count?,
'hide_followers_count' => hide_followers_count?, 'hide_followers_count' => hide_followers_count?,
'translatable_private' => translatable_private?, 'translatable_private' => translatable_private?,
'link_preview' => link_preview?,
} }
if Setting.enable_emoji_reaction if Setting.enable_emoji_reaction
config = config.merge({ config = config.merge({

View file

@ -18,16 +18,12 @@ class AccountDomainBlock < ApplicationRecord
belongs_to :account belongs_to :account
validates :domain, presence: true, uniqueness: { scope: :account_id }, domain: true validates :domain, presence: true, uniqueness: { scope: :account_id }, domain: true
after_commit :remove_blocking_cache after_commit :invalidate_domain_blocking_cache
after_commit :remove_relationship_cache
private private
def remove_blocking_cache def invalidate_domain_blocking_cache
Rails.cache.delete("exclude_domains_for:#{account_id}") Rails.cache.delete("exclude_domains_for:#{account_id}")
end Rails.cache.delete(['exclude_domains', account_id, domain])
def remove_relationship_cache
Rails.cache.delete_matched("relationship:#{account_id}:*")
end end
end end

View file

@ -78,9 +78,9 @@ class Announcement < ApplicationRecord
else else
scope.select("name, custom_emoji_id, count(*) as count, exists(select 1 from announcement_reactions r where r.account_id = #{account.id} and r.announcement_id = announcement_reactions.announcement_id and r.name = announcement_reactions.name) as me") scope.select("name, custom_emoji_id, count(*) as count, exists(select 1 from announcement_reactions r where r.account_id = #{account.id} and r.announcement_id = announcement_reactions.announcement_id and r.name = announcement_reactions.name) as me")
end end
end end.to_a
ActiveRecord::Associations::Preloader.new(records: records, associations: :custom_emoji) ActiveRecord::Associations::Preloader.new(records: records, associations: :custom_emoji).call
records records
end end

View file

@ -55,11 +55,15 @@ class Antenna < ApplicationRecord
scope :available_stls, -> { where(available: true, stl: true) } scope :available_stls, -> { where(available: true, stl: true) }
scope :available_ltls, -> { where(available: true, stl: false, ltl: true) } scope :available_ltls, -> { where(available: true, stl: false, ltl: true) }
validates :title, presence: true
validate :list_owner validate :list_owner
validate :validate_limit validate :validate_limit
validate :validate_stl_limit validate :validate_stl_limit
validate :validate_ltl_limit validate :validate_ltl_limit
before_destroy :clean_feed_manager
def list_owner def list_owner
raise Mastodon::ValidationError, I18n.t('antennas.errors.invalid_list_owner') if !list_id.zero? && list.present? && list.account != account raise Mastodon::ValidationError, I18n.t('antennas.errors.invalid_list_owner') if !list_id.zero? && list.present? && list.account != account
end end
@ -121,4 +125,8 @@ class Antenna < ApplicationRecord
ltls.any? { |tl| !tl.insert_feeds } ltls.any? { |tl| !tl.insert_feeds }
end end
end end
def clean_feed_manager
FeedManager.instance.clean_feeds!(:antenna, [id])
end
end end

View file

@ -60,12 +60,6 @@ module AccountInteractions
end end
end end
def domain_blocking_map(target_account_ids, account_id)
accounts_map = Account.where(id: target_account_ids).select('id, domain').each_with_object({}) { |a, h| h[a.id] = a.domain }
blocked_domains = domain_blocking_map_by_domain(accounts_map.values.compact, account_id)
accounts_map.reduce({}) { |h, (id, domain)| h.merge(id => blocked_domains[domain]) }
end
def domain_blocking_map_by_domain(target_domains, account_id) def domain_blocking_map_by_domain(target_domains, account_id)
follow_mapping(AccountDomainBlock.where(account_id: account_id, domain: target_domains), :domain) follow_mapping(AccountDomainBlock.where(account_id: account_id, domain: target_domains), :domain)
end end
@ -191,7 +185,7 @@ module AccountInteractions
end end
def unblock_domain!(other_domain) def unblock_domain!(other_domain)
block = domain_blocks.find_by(domain: other_domain) block = domain_blocks.find_by(domain: normalized_domain(other_domain))
block&.destroy block&.destroy
end end
@ -339,4 +333,8 @@ module AccountInteractions
def remove_potential_friendship(other_account) def remove_potential_friendship(other_account)
PotentialFriendshipTracker.remove(id, other_account.id) PotentialFriendshipTracker.remove(id, other_account.id)
end end
def normalized_domain(domain)
TagManager.instance.normalize_domain(domain)
end
end end

View file

@ -145,7 +145,7 @@ module AccountSearch
tsquery = generate_query_for_search(terms) tsquery = generate_query_for_search(terms)
find_by_sql([BASIC_SEARCH_SQL, { limit: limit, offset: offset, tsquery: tsquery }]).tap do |records| find_by_sql([BASIC_SEARCH_SQL, { limit: limit, offset: offset, tsquery: tsquery }]).tap do |records|
ActiveRecord::Associations::Preloader.new(records: records, associations: :account_stat) ActiveRecord::Associations::Preloader.new(records: records, associations: [:account_stat, { user: :role }]).call
end end
end end
@ -158,7 +158,7 @@ module AccountSearch
end end
find_by_sql([sql_template, { id: account.id, limit: limit, offset: offset, tsquery: tsquery }]).tap do |records| find_by_sql([sql_template, { id: account.id, limit: limit, offset: offset, tsquery: tsquery }]).tap do |records|
ActiveRecord::Associations::Preloader.new(records: records, associations: :account_stat) ActiveRecord::Associations::Preloader.new(records: records, associations: [:account_stat, { user: :role }]).call
end end
end end

View file

@ -52,9 +52,13 @@ module Attachmentable
return if attachment.blank? || !/image.*/.match?(attachment.content_type) || attachment.queued_for_write[:original].blank? return if attachment.blank? || !/image.*/.match?(attachment.content_type) || attachment.queued_for_write[:original].blank?
width, height = FastImage.size(attachment.queued_for_write[:original].path) width, height = FastImage.size(attachment.queued_for_write[:original].path)
matrix_limit = attachment.content_type == 'image/gif' ? GIF_MATRIX_LIMIT : MAX_MATRIX_LIMIT return unless width.present? && height.present?
raise Mastodon::DimensionsValidationError, "#{width}x#{height} images are not supported" if width.present? && height.present? && (width * height > matrix_limit) if attachment.content_type == 'image/gif' && width * height > GIF_MATRIX_LIMIT
raise Mastodon::DimensionsValidationError, "#{width}x#{height} GIF files are not supported"
elsif width * height > MAX_MATRIX_LIMIT
raise Mastodon::DimensionsValidationError, "#{width}x#{height} images are not supported"
end
end end
def appropriate_extension(attachment) def appropriate_extension(attachment)

View file

@ -14,6 +14,10 @@ module Cacheable
includes(@cache_associated) includes(@cache_associated)
end end
def preload_cacheable_associations(records)
ActiveRecord::Associations::Preloader.new(records: records, associations: @cache_associated).call
end
def cache_ids def cache_ids
select(:id, :updated_at) select(:id, :updated_at)
end end

View file

@ -123,10 +123,6 @@ module HasUserSettings
settings['translatable_private'] settings['translatable_private']
end end
def setting_link_preview
settings['link_preview']
end
def setting_single_ref_to_quote def setting_single_ref_to_quote
settings['single_ref_to_quote'] settings['single_ref_to_quote']
end end

View file

@ -22,7 +22,7 @@ module LdapAuthenticable
safe_username = safe_username.gsub(keys, replacement) safe_username = safe_username.gsub(keys, replacement)
end end
resource = joins(:account).find_by(accounts: { username: safe_username }) resource = joins(:account).merge(Account.where(Account.arel_table[:username].lower.eq safe_username.downcase)).take
if resource.blank? if resource.blank?
resource = new(email: attributes[Devise.ldap_mail.to_sym].first, agreement: true, account_attributes: { username: safe_username }, admin: false, external: true, confirmed_at: Time.now.utc) resource = new(email: attributes[Devise.ldap_mail.to_sym].first, agreement: true, account_attributes: { username: safe_username }, admin: false, external: true, confirmed_at: Time.now.utc)

View file

@ -19,17 +19,18 @@ module Omniauthable
end end
class_methods do class_methods do
def find_for_oauth(auth, signed_in_resource = nil) def find_for_omniauth(auth, signed_in_resource = nil)
# EOLE-SSO Patch # EOLE-SSO Patch
auth.uid = (auth.uid[0][:uid] || auth.uid[0][:user]) if auth.uid.is_a? Hashie::Array auth.uid = (auth.uid[0][:uid] || auth.uid[0][:user]) if auth.uid.is_a? Hashie::Array
identity = Identity.find_for_oauth(auth) identity = Identity.find_for_omniauth(auth)
# If a signed_in_resource is provided it always overrides the existing user # If a signed_in_resource is provided it always overrides the existing user
# to prevent the identity being locked with accidentally created accounts. # to prevent the identity being locked with accidentally created accounts.
# Note that this may leave zombie accounts (with no associated identity) which # Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date. # can be cleaned up at a later date.
user = signed_in_resource || identity.user user = signed_in_resource || identity.user
user ||= create_for_oauth(auth) user ||= reattach_for_auth(auth)
user ||= create_for_auth(auth)
if identity.user.nil? if identity.user.nil?
identity.user = user identity.user = user
@ -39,19 +40,35 @@ module Omniauthable
user user
end end
def create_for_oauth(auth) private
# Check if the user exists with provided email. If no email was provided,
def reattach_for_auth(auth)
# If allowed, check if a user exists with the provided email address,
# and return it if they does not have an associated identity with the
# current authentication provider.
# This can be used to provide a choice of alternative auth providers
# or provide smooth gradual transition between multiple auth providers,
# but this is discouraged because any insecure provider will put *all*
# local users at risk, regardless of which provider they registered with.
return unless ENV['ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH'] == 'true'
email, email_is_verified = email_from_auth(auth)
return unless email_is_verified
user = User.find_by(email: email)
return if user.nil? || Identity.exists?(provider: auth.provider, user_id: user.id)
user
end
def create_for_auth(auth)
# Create a user for the given auth params. If no email was provided,
# we assign a temporary email and ask the user to verify it on # we assign a temporary email and ask the user to verify it on
# the next step via Auth::SetupController.show # the next step via Auth::SetupController.show
strategy = Devise.omniauth_configs[auth.provider.to_sym].strategy email, email_is_verified = email_from_auth(auth)
assume_verified = strategy&.security&.assume_email_is_verified
email_is_verified = auth.info.verified || auth.info.verified_email || auth.info.email_verified || assume_verified
email = auth.info.verified_email || auth.info.email
user = User.find_by(email: email) if email_is_verified
return user unless user.nil?
user = User.new(user_params_from_auth(email, auth)) user = User.new(user_params_from_auth(email, auth))
@ -66,7 +83,14 @@ module Omniauthable
user user
end end
private def email_from_auth(auth)
strategy = Devise.omniauth_configs[auth.provider.to_sym].strategy
assume_verified = strategy&.security&.assume_email_is_verified
email_is_verified = auth.info.verified || auth.info.verified_email || auth.info.email_verified || assume_verified
email = auth.info.verified_email || auth.info.email
[email, email_is_verified]
end
def user_params_from_auth(email, auth) def user_params_from_auth(email, auth)
{ {

View file

@ -10,7 +10,7 @@ module RelationshipCacheable
private private
def remove_relationship_cache def remove_relationship_cache
Rails.cache.delete("relationship:#{account_id}:#{target_account_id}") Rails.cache.delete(['relationship', account_id, target_account_id])
Rails.cache.delete("relationship:#{target_account_id}:#{account_id}") Rails.cache.delete(['relationship', target_account_id, account_id])
end end
end end

View file

@ -25,7 +25,6 @@
# reject_straight_follow :boolean default(FALSE), not null # reject_straight_follow :boolean default(FALSE), not null
# reject_new_follow :boolean default(FALSE), not null # reject_new_follow :boolean default(FALSE), not null
# hidden :boolean default(FALSE), not null # hidden :boolean default(FALSE), not null
# hidden_anonymous :boolean default(FALSE), not null
# detect_invalid_subscription :boolean default(FALSE), not null # detect_invalid_subscription :boolean default(FALSE), not null
# reject_reply_exclude_followers :boolean default(FALSE), not null # reject_reply_exclude_followers :boolean default(FALSE), not null
# #
@ -60,10 +59,6 @@ class DomainBlock < ApplicationRecord
reject_favourite? ? :reject_favourite : nil, reject_favourite? ? :reject_favourite : nil,
reject_reply? ? :reject_reply : nil, reject_reply? ? :reject_reply : nil,
reject_reply_exclude_followers? ? :reject_reply_exclude_followers : nil, reject_reply_exclude_followers? ? :reject_reply_exclude_followers : nil,
reject_send_not_public_searchability? ? :reject_send_not_public_searchability : nil,
reject_send_public_unlisted? ? :reject_send_public_unlisted : nil,
reject_send_dissubscribable? ? :reject_send_dissubscribable : nil,
reject_send_media? ? :reject_send_media : nil,
reject_send_sensitive? ? :reject_send_sensitive : nil, reject_send_sensitive? ? :reject_send_sensitive : nil,
reject_hashtag? ? :reject_hashtag : nil, reject_hashtag? ? :reject_hashtag : nil,
reject_straight_follow? ? :reject_straight_follow : nil, reject_straight_follow? ? :reject_straight_follow : nil,

View file

@ -28,7 +28,7 @@ class Feed
unhydrated = redis.zrangebyscore(key, "(#{min_id}", "(#{max_id}", limit: [0, limit], with_scores: true).map(&:first).map(&:to_i) unhydrated = redis.zrangebyscore(key, "(#{min_id}", "(#{max_id}", limit: [0, limit], with_scores: true).map(&:first).map(&:to_i)
end end
Status.where(id: unhydrated).cache_ids Status.where(id: unhydrated)
end end
def key def key

View file

@ -17,7 +17,7 @@ class Identity < ApplicationRecord
validates :uid, presence: true, uniqueness: { scope: :provider } validates :uid, presence: true, uniqueness: { scope: :provider }
validates :provider, presence: true validates :provider, presence: true
def self.find_for_oauth(auth) def self.find_for_omniauth(auth)
find_or_create_by(uid: auth.uid, provider: auth.provider) find_or_create_by(uid: auth.uid, provider: auth.provider)
end end
end end

View file

@ -128,7 +128,7 @@ class Notification < ApplicationRecord
# Instead of using the usual `includes`, manually preload each type. # Instead of using the usual `includes`, manually preload each type.
# If polymorphic associations are loaded with the usual `includes`, other types of associations will be loaded more. # If polymorphic associations are loaded with the usual `includes`, other types of associations will be loaded more.
ActiveRecord::Associations::Preloader.new(records: grouped_notifications, associations: associations) ActiveRecord::Associations::Preloader.new(records: grouped_notifications, associations: associations).call
end end
unique_target_statuses = notifications.filter_map(&:target_status).uniq unique_target_statuses = notifications.filter_map(&:target_status).uniq

View file

@ -31,7 +31,7 @@ class PublicFeed
# scope.merge!(anonymous_scope) unless account? # scope.merge!(anonymous_scope) unless account?
scope = to_anonymous_scope(scope) unless account? scope = to_anonymous_scope(scope) unless account?
scope.cache_ids.to_a_paginated_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id) scope.to_a_paginated_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id)
end end
private private

View file

@ -44,9 +44,9 @@ class Report < ApplicationRecord
delegate :local?, to: :account delegate :local?, to: :account
validates :comment, length: { maximum: 1_000 }, if: :local? validates :comment, length: { maximum: 1_000 }, if: :local?
validates :rule_ids, absence: true, unless: :violation? validates :rule_ids, absence: true, if: -> { (category_changed? || rule_ids_changed?) && !violation? }
validate :validate_rule_ids validate :validate_rule_ids, if: -> { (category_changed? || rule_ids_changed?) && violation? }
# entries here need to be kept in sync with the front-end: # entries here need to be kept in sync with the front-end:
# - app/javascript/mastodon/features/notifications/components/report.jsx # - app/javascript/mastodon/features/notifications/components/report.jsx
@ -154,8 +154,6 @@ class Report < ApplicationRecord
end end
def validate_rule_ids def validate_rule_ids
return unless violation?
errors.add(:rule_ids, I18n.t('reports.errors.invalid_rules')) unless rules.size == rule_ids&.size errors.add(:rule_ids, I18n.t('reports.errors.invalid_rules')) unless rules.size == rule_ids&.size
end end

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