Compare commits

..

20 Commits

Author SHA1 Message Date
Eugen Rochko
a583e54023 Bump version to 3.2.1 2020-10-19 16:07:06 +02:00
ThibG
4ea7193f0a Add support for latest HTTP Signatures spec draft (#14556)
* Add support for latest HTTP Signatures spec draft

https://www.ietf.org/id/draft-ietf-httpbis-message-signatures-00.html

- add support for the “hs2019” signature algorithm (assumed to be equivalent
  to RSA-SHA256, since we do not have a mechanism to specify the algorithm
  within the key metadata yet)
- add support for (created) and (expires) pseudo-headers and related
  signature parameters, when using the hs2019 signature algorithm
- adjust default “headers” parameter while being backwards-compatible with
  previous implementation
- change the acceptable time window logic from 12 hours surrounding the “date”
  header to accepting signatures created up to 1 hour in the future and
  expiring up to 1 hour in the past (but only allowing expiration dates up to
  12 hours after the creation date)
  This doesn't conform with the current draft, as it doesn't permit accounting
  for clock skew.
  This, however, should be addressed in a next version of the draft:
  https://github.com/httpwg/http-extensions/pull/1235

* Add additional signature requirements

* Rewrite signature params parsing using Parslet

* Make apparent which signature algorithm Mastodon on verification failure

Mastodon uses RSASSA-PKCS1-v1_5, which is not recommended for new applications,
and new implementers may thus unknowingly use RSASSA-PSS.

* Add workaround for PeerTube's invalid signature header

The previous parser allowed incorrect Signature headers, such as
those produced by old versions of the `http-signature` node.js package,
and seemingly used by PeerTube.

This commit adds a workaround for that.

* Fix `signature_key_id` raising an exception

Previously, parsing failures would result in `signature_key_id` being nil,
but the parser changes made that result in an exception.

This commit changes the `signature_key_id` method to return `nil` in case
of parsing failures.

* Move extra HTTP signature helper methods to private methods

* Relax (request-target) requirement to (request-target) || digest

This lets requests from Plume work without lowering security significantly.
2020-10-19 15:41:25 +02:00
ThibG
aa98655cf6 Fix dereferencing remote statuses not using the correct account (#14656)
Follow-up to #14359

In the case of limited toots, the receiver may not be explicitly part of the
audience. If a specific user's inbox URI was specified, it makes sense to
dereference the toot from the corresponding user, instead of trying to find
someone in the explicit audience.
2020-10-19 15:41:11 +02:00
Tdxdxoz
dd3a86eb04 Fix: also use custom private boost icon for detailed status (#14471)
* use custom private boost icon for detail status

* only use className
2020-10-19 15:40:51 +02:00
ThibG
aea0161e83 Add support for inlined objects in activity audience (#14514)
* Add support for inlined objects in activity audience

* Add tests
2020-10-19 15:40:42 +02:00
ThibG
8b448aecef Fix tootctl media commands not handling snowflake ids for media_attachments (#14536) 2020-10-19 15:40:34 +02:00
ThibG
6db143e424 Fix crash when failing to load emoji picker (#14525)
Fixes #14523
2020-10-19 15:40:22 +02:00
ThibG
3b699f1732 Fix thumbnail color extraction (#14464)
* Fix contrast calculation for thumbnail color extraction

Luminance calculation was using 0-255 RGB values instead of 0-1 sRGB values,
leading to incorrectly-computed contrast values.

Since we use ColorDiff already, just use its XYZ colorspace conversion code
to get the value.

* Require at least 3:1 contrast for both accent and foreground colors

* Lower required contrast for the accent color
2020-10-19 15:40:14 +02:00
ThibG
1995a5cb34 Fix audio/video player not using CDN_HOST in media paths on public pages (#14486) 2020-10-19 15:40:03 +02:00
ThibG
469c4c78a3 Fix audio player on Safari (#14485) 2020-10-19 15:39:48 +02:00
ThibG
399c5f0900 Change content-type to be always computed from file data (#14452)
* Change content-type to be always computed from file data

Restore previous behavior, detecting the content-type isn't very
expensive, and some instances may serve files as application/octet-stream
regardless of their true type, making fetching media from them fail, while
it used to work pre-3.2.0.

* Add test
2020-10-19 15:39:28 +02:00
ThibG
856cb96a2b Fix new audio player features not working on Safari (#14465)
Fixes #14462
2020-10-19 15:39:18 +02:00
Takeshi Umeda
58c59af573 Fix an error when file_file_size is nil in tootctl media remove (#14657) 2020-10-19 14:49:31 +02:00
Eugen Rochko
3f4cceebd6 Fix videos with near-60 fps being rejected (#14684)
Fix #14668
2020-10-19 14:49:14 +02:00
Eugen Rochko
ce6aaed432 Remove dependency on goldfinger gem (#14919)
There are edge cases where requests to certain hosts timeout when
using the vanilla HTTP.rb gem, which the goldfinger gem uses. Now
that we no longer need to support OStatus servers, webfinger logic
is so simple that there is no point encapsulating it in a gem, so
we can just use our own Request class. With that, we benefit from
more robust timeout code and IPv4/IPv6 resolution.

Fix #14091
2020-10-19 14:48:54 +02:00
Eugen Rochko
8f79ed0487 Fix reported statuses not being included in warning e-mail (#14778) 2020-10-19 14:46:35 +02:00
ThibG
4acfc3ce83 Fix handling of Reject Follow when a matching follow relationship exists (#14479)
* Add tests

* Fix handling of Reject Follow when a matching follow relationship exists

Regression from #12199
2020-10-19 14:46:10 +02:00
Takeshi Umeda
c98b7751ca Fix limited follower id in fan-out-on-write service (#14709) 2020-10-19 14:45:44 +02:00
Eugen Rochko
0abfa06b2f Fix inefficiencies in fan-out-on-write service (#14682) 2020-10-19 14:45:30 +02:00
ThibG
aecdaf5a8c Do not serve account actors at all in limited federation mode (#14800)
* Do not serve account actors at all in limited federation mode

When an account is fetched without a signature from an allowed instance,
return an error.

This isn't really an improvement in security, as the only information that was
previously returned was required protocol-level info, and the only personal bit
was the existence of the account. The existence of the account can still be
checked by issuing a webfinger query, as those are accepted without signatures.

However, this change makes it so that unallowed instances won't create account
records on their end when they find a reference to an unknown account.

The previous behavior of rendering a limited list of fields, instead of not
rendering the actor at all, was in order to prevent situations in which two
instances in Authorized Fetch mode or Limited Federation mode would fail to
reach each other because resolving an account would require a signed query…
from an account which can only be fetched with a signed query itself. However,
this should now be fine as fetching accounts is done by signing on behalf of
the special instance actor, which does not require any kind of valid signature
to be fetched.

* Fix tests
2020-10-19 14:45:12 +02:00
876 changed files with 11021 additions and 35362 deletions

View File

@@ -27,10 +27,10 @@ plugins:
enabled: true enabled: true
eslint: eslint:
enabled: true enabled: true
channel: eslint-7 channel: eslint-6
rubocop: rubocop:
enabled: true enabled: true
channel: rubocop-0-92 channel: rubocop-0-82
sass-lint: sass-lint:
enabled: true enabled: true
exclude_patterns: exclude_patterns:

View File

@@ -1,7 +1,7 @@
--- ---
name: Bug Report name: Bug Report
about: If something isn't working as expected about: If something isn't working as expected
labels: bug
--- ---
<!-- Make sure that you are submitting a new bug that was not previously reported or already fixed --> <!-- Make sure that you are submitting a new bug that was not previously reported or already fixed -->

View File

@@ -1,6 +1,7 @@
--- ---
name: Feature Request name: Feature Request
about: I have a suggestion about: I have a suggestion
--- ---
<!-- Please use a concise and distinct title for the issue --> <!-- Please use a concise and distinct title for the issue -->

View File

@@ -11,7 +11,7 @@ updates:
interval: weekly interval: weekly
open-pull-requests-limit: 99 open-pull-requests-limit: 99
allow: allow:
- dependency-type: direct - dependency-type: all
- package-ecosystem: bundler - package-ecosystem: bundler
directory: "/" directory: "/"
@@ -19,4 +19,4 @@ updates:
interval: weekly interval: weekly
open-pull-requests-limit: 99 open-pull-requests-limit: 99
allow: allow:
- dependency-type: direct - dependency-type: all

View File

@@ -25,68 +25,30 @@ Layout/AccessModifierIndentation:
Layout/EmptyLineAfterMagicComment: Layout/EmptyLineAfterMagicComment:
Enabled: false Enabled: false
Layout/EmptyLineAfterGuardClause:
Enabled: false
Layout/EmptyLinesAroundAttributeAccessor:
Enabled: true
Layout/HashAlignment:
Enabled: false
# EnforcedHashRocketStyle: table
# EnforcedColonStyle: table
Layout/SpaceAroundMethodCallOperator:
Enabled: true
Layout/SpaceInsideHashLiteralBraces: Layout/SpaceInsideHashLiteralBraces:
EnforcedStyle: space EnforcedStyle: space
Lint/DeprecatedOpenSSLConstant:
Enabled: true
Lint/DuplicateElsifCondition:
Enabled: true
Lint/MixedRegexpCaptureTypes:
Enabled: true
Lint/RaiseException:
Enabled: true
Lint/StructNewOverride:
Enabled: true
Lint/UselessAccessModifier: Lint/UselessAccessModifier:
ContextCreatingMethods: ContextCreatingMethods:
- class_methods - class_methods
Metrics/AbcSize: Metrics/AbcSize:
Max: 100 Max: 100
Exclude:
- 'lib/mastodon/*_cli.rb'
Metrics/BlockLength: Metrics/BlockLength:
Max: 55 Max: 35
Exclude: Exclude:
- 'lib/tasks/**/*' - 'lib/tasks/**/*'
- 'lib/mastodon/*_cli.rb'
Metrics/BlockNesting: Metrics/BlockNesting:
Max: 3 Max: 3
Exclude:
- 'lib/mastodon/*_cli.rb'
Metrics/ClassLength: Metrics/ClassLength:
CountComments: false CountComments: false
Max: 400 Max: 300
Exclude:
- 'lib/mastodon/*_cli.rb'
Metrics/CyclomaticComplexity: Metrics/CyclomaticComplexity:
Max: 25 Max: 25
Exclude:
- 'lib/mastodon/*_cli.rb'
Layout/LineLength: Layout/LineLength:
AllowURI: true AllowURI: true
@@ -94,9 +56,7 @@ Layout/LineLength:
Metrics/MethodLength: Metrics/MethodLength:
CountComments: false CountComments: false
Max: 65 Max: 55
Exclude:
- 'lib/mastodon/*_cli.rb'
Metrics/ModuleLength: Metrics/ModuleLength:
CountComments: false CountComments: false
@@ -107,90 +67,34 @@ Metrics/ParameterLists:
CountKeywordArgs: true CountKeywordArgs: true
Metrics/PerceivedComplexity: Metrics/PerceivedComplexity:
Max: 25 Max: 20
Naming/MemoizedInstanceVariableName: Naming/MemoizedInstanceVariableName:
Enabled: false Enabled: false
Naming/MethodParameterName:
Enabled: true
Rails: Rails:
Enabled: true Enabled: true
Rails/ApplicationController:
Enabled: false
Exclude:
- 'app/controllers/well_known/**/*.rb'
Rails/BelongsTo:
Enabled: false
Rails/ContentTag:
Enabled: false
Rails/EnumHash: Rails/EnumHash:
Enabled: false Enabled: false
Rails/HasAndBelongsToMany:
Enabled: false
Rails/SkipsModelValidations:
Enabled: false
Rails/HttpStatus:
Enabled: false
Rails/Exit: Rails/Exit:
Exclude: Exclude:
- 'lib/mastodon/*' - 'lib/mastodon/*'
- 'lib/cli.rb' - 'lib/cli.rb'
Rails/FilePath:
Enabled: false
Rails/HasAndBelongsToMany:
Enabled: false
Rails/HasManyOrHasOneDependent:
Enabled: false
Rails/HelperInstanceVariable: Rails/HelperInstanceVariable:
Enabled: false Enabled: false
Rails/HttpStatus:
Enabled: false
Rails/IndexBy:
Enabled: false
Rails/InverseOf:
Enabled: false
Rails/LexicallyScopedActionFilter:
Enabled: false
Rails/OutputSafety:
Enabled: true
Rails/RakeEnvironment:
Enabled: false
Rails/RedundantForeignKey:
Enabled: false
Rails/SkipsModelValidations:
Enabled: false
Rails/UniqueValidationWithoutIndex:
Enabled: false
Style/AccessorGrouping:
Enabled: true
Style/AccessModifierDeclarations:
Enabled: false
Style/ArrayCoercion:
Enabled: true
Style/BisectedAttrAccessor:
Enabled: true
Style/CaseLikeIf:
Enabled: false
Style/ClassAndModuleChildren: Style/ClassAndModuleChildren:
Enabled: false Enabled: false
@@ -205,15 +109,6 @@ Style/Documentation:
Style/DoubleNegation: Style/DoubleNegation:
Enabled: true Enabled: true
Style/ExpandPathArguments:
Enabled: false
Style/ExponentialNotation:
Enabled: true
Style/FormatString:
Enabled: false
Style/FormatStringToken: Style/FormatStringToken:
Enabled: false Enabled: false
@@ -223,33 +118,9 @@ Style/FrozenStringLiteralComment:
Style/GuardClause: Style/GuardClause:
Enabled: false Enabled: false
Style/HashAsLastArrayItem:
Enabled: false
Style/HashEachMethods:
Enabled: true
Style/HashLikeCase:
Enabled: true
Style/HashTransformKeys:
Enabled: true
Style/HashTransformValues:
Enabled: false
Style/IfUnlessModifier:
Enabled: false
Style/InverseMethods:
Enabled: false
Style/Lambda: Style/Lambda:
Enabled: false Enabled: false
Style/MutableConstant:
Enabled: false
Style/PercentLiteralDelimiters: Style/PercentLiteralDelimiters:
PreferredDelimiters: PreferredDelimiters:
'%i': '()' '%i': '()'
@@ -258,36 +129,9 @@ Style/PercentLiteralDelimiters:
Style/PerlBackrefs: Style/PerlBackrefs:
AutoCorrect: false AutoCorrect: false
Style/RedundantAssignment:
Enabled: false
Style/RedundantFetchBlock:
Enabled: true
Style/RedundantFileExtensionInRequire:
Enabled: true
Style/RedundantRegexpCharacterClass:
Enabled: false
Style/RedundantRegexpEscape:
Enabled: false
Style/RedundantReturn:
Enabled: true
Style/RegexpLiteral: Style/RegexpLiteral:
Enabled: false Enabled: false
Style/RescueStandardError:
Enabled: false
Style/SignalException:
Enabled: false
Style/SlicingWithRange:
Enabled: true
Style/SymbolArray: Style/SymbolArray:
Enabled: false Enabled: false
@@ -296,6 +140,3 @@ Style/TrailingCommaInArrayLiteral:
Style/TrailingCommaInHashLiteral: Style/TrailingCommaInHashLiteral:
EnforcedStyleForMultiline: 'comma' EnforcedStyleForMultiline: 'comma'
Style/UnpackFirst:
Enabled: false

View File

@@ -1 +1 @@
2.7.2 2.6.6

View File

@@ -5,39 +5,38 @@ Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon)
and provided thanks to the work of the following contributors: and provided thanks to the work of the following contributors:
* [Gargron](https://github.com/Gargron) * [Gargron](https://github.com/Gargron)
* [ThibG](https://github.com/ThibG)
* [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) * [dependabot-preview[bot]](https://github.com/apps/dependabot-preview)
* [dependabot[bot]](https://github.com/apps/dependabot) * [ThibG](https://github.com/ThibG)
* [ykzts](https://github.com/ykzts) * [ykzts](https://github.com/ykzts)
* [dependabot[bot]](https://github.com/apps/dependabot)
* [akihikodaki](https://github.com/akihikodaki) * [akihikodaki](https://github.com/akihikodaki)
* [mjankowski](https://github.com/mjankowski) * [mjankowski](https://github.com/mjankowski)
* [unarist](https://github.com/unarist) * [unarist](https://github.com/unarist)
* [yiskah](https://github.com/yiskah) * [yiskah](https://github.com/yiskah)
* [nolanlawson](https://github.com/nolanlawson) * [nolanlawson](https://github.com/nolanlawson)
* [abcang](https://github.com/abcang) * [abcang](https://github.com/abcang)
* [mayaeh](https://github.com/mayaeh)
* [ysksn](https://github.com/ysksn) * [ysksn](https://github.com/ysksn)
* [mayaeh](https://github.com/mayaeh)
* [sorin-davidoi](https://github.com/sorin-davidoi) * [sorin-davidoi](https://github.com/sorin-davidoi)
* [noellabo](https://github.com/noellabo)
* [lynlynlynx](https://github.com/lynlynlynx) * [lynlynlynx](https://github.com/lynlynlynx)
* [m4sk1n](mailto:me@m4sk.in) * [m4sk1n](mailto:me@m4sk.in)
* [Marcin Mikołajczak](mailto:me@m4sk.in) * [Marcin Mikołajczak](mailto:me@m4sk.in)
* [Kjwon15](https://github.com/Kjwon15) * [Kjwon15](https://github.com/Kjwon15)
* [noellabo](https://github.com/noellabo)
* [renatolond](https://github.com/renatolond) * [renatolond](https://github.com/renatolond)
* [alpaca-tc](https://github.com/alpaca-tc) * [alpaca-tc](https://github.com/alpaca-tc)
* [jeroenpraat](https://github.com/jeroenpraat) * [jeroenpraat](https://github.com/jeroenpraat)
* [nclm](https://github.com/nclm) * [nclm](https://github.com/nclm)
* [ineffyble](https://github.com/ineffyble) * [ineffyble](https://github.com/ineffyble)
* [zunda](https://github.com/zunda)
* [shleeable](https://github.com/shleeable) * [shleeable](https://github.com/shleeable)
* [zunda](https://github.com/zunda)
* [Masoud Abkenar](mailto:ampbox@gmail.com) * [Masoud Abkenar](mailto:ampbox@gmail.com)
* [blackle](https://github.com/blackle) * [blackle](https://github.com/blackle)
* [Quent-in](https://github.com/Quent-in) * [Quent-in](https://github.com/Quent-in)
* [JantsoP](https://github.com/JantsoP) * [JantsoP](https://github.com/JantsoP)
* [nullkal](https://github.com/nullkal) * [nullkal](https://github.com/nullkal)
* [yookoala](https://github.com/yookoala) * [yookoala](https://github.com/yookoala)
* [Brawaru](https://github.com/Brawaru) * [Sasha-Sorokin](https://github.com/Sasha-Sorokin)
* [ariasuni](https://github.com/ariasuni)
* [Aditoo17](https://github.com/Aditoo17) * [Aditoo17](https://github.com/Aditoo17)
* [Quenty31](https://github.com/Quenty31) * [Quenty31](https://github.com/Quenty31)
* [marek-lach](https://github.com/marek-lach) * [marek-lach](https://github.com/marek-lach)
@@ -46,9 +45,9 @@ and provided thanks to the work of the following contributors:
* [danhunsaker](https://github.com/danhunsaker) * [danhunsaker](https://github.com/danhunsaker)
* [eramdam](https://github.com/eramdam) * [eramdam](https://github.com/eramdam)
* [takayamaki](https://github.com/takayamaki) * [takayamaki](https://github.com/takayamaki)
* [ariasuni](https://github.com/ariasuni)
* [masarakki](https://github.com/masarakki) * [masarakki](https://github.com/masarakki)
* [ticky](https://github.com/ticky) * [ticky](https://github.com/ticky)
* [trwnh](https://github.com/trwnh)
* [ThisIsMissEm](https://github.com/ThisIsMissEm) * [ThisIsMissEm](https://github.com/ThisIsMissEm)
* [hinaloe](https://github.com/hinaloe) * [hinaloe](https://github.com/hinaloe)
* [hcmiya](https://github.com/hcmiya) * [hcmiya](https://github.com/hcmiya)
@@ -58,10 +57,10 @@ and provided thanks to the work of the following contributors:
* [yukimochi](https://github.com/yukimochi) * [yukimochi](https://github.com/yukimochi)
* [palindromordnilap](https://github.com/palindromordnilap) * [palindromordnilap](https://github.com/palindromordnilap)
* [rkarabut](https://github.com/rkarabut) * [rkarabut](https://github.com/rkarabut)
* [trwnh](https://github.com/trwnh)
* [nightpool](https://github.com/nightpool) * [nightpool](https://github.com/nightpool)
* [Artoria2e5](https://github.com/Artoria2e5) * [Artoria2e5](https://github.com/Artoria2e5)
* [marrus-sh](https://github.com/marrus-sh) * [marrus-sh](https://github.com/marrus-sh)
* [dunn](https://github.com/dunn)
* [krainboltgreene](https://github.com/krainboltgreene) * [krainboltgreene](https://github.com/krainboltgreene)
* [pfigel](https://github.com/pfigel) * [pfigel](https://github.com/pfigel)
* [BoFFire](https://github.com/BoFFire) * [BoFFire](https://github.com/BoFFire)
@@ -85,25 +84,25 @@ and provided thanks to the work of the following contributors:
* [ashleyhull-versent](https://github.com/ashleyhull-versent) * [ashleyhull-versent](https://github.com/ashleyhull-versent)
* [yhirano55](https://github.com/yhirano55) * [yhirano55](https://github.com/yhirano55)
* [rinsuki](https://github.com/rinsuki) * [rinsuki](https://github.com/rinsuki)
* [dunn](https://github.com/dunn)
* [devkral](https://github.com/devkral) * [devkral](https://github.com/devkral)
* [camponez](https://github.com/camponez) * [camponez](https://github.com/camponez)
* [hugogameiro](https://github.com/hugogameiro) * [hugogameiro](https://github.com/hugogameiro)
* [SerCom_KC](mailto:szescxz@gmail.com) * [SerCom_KC](mailto:szescxz@gmail.com)
* [aschmitz](https://github.com/aschmitz) * [aschmitz](https://github.com/aschmitz)
* [mfmfuyu](https://github.com/mfmfuyu)
* [kedamaDQ](https://github.com/kedamaDQ)
* [fpiesche](https://github.com/fpiesche) * [fpiesche](https://github.com/fpiesche)
* [gandaro](https://github.com/gandaro) * [gandaro](https://github.com/gandaro)
* [johnsudaar](https://github.com/johnsudaar) * [johnsudaar](https://github.com/johnsudaar)
* [trebmuh](https://github.com/trebmuh) * [trebmuh](https://github.com/trebmuh)
* [rmhasan](https://github.com/rmhasan) * [rmhasan](https://github.com/rmhasan)
* [kedamaDQ](https://github.com/kedamaDQ)
* [lindwurm](https://github.com/lindwurm) * [lindwurm](https://github.com/lindwurm)
* [victorhck](mailto:victorhck@geeko.site) * [victorhck](mailto:victorhck@geeko.site)
* [voidsatisfaction](https://github.com/voidsatisfaction) * [voidsatisfaction](https://github.com/voidsatisfaction)
* [mkljczk](https://github.com/mkljczk)
* [hikari-no-yume](https://github.com/hikari-no-yume) * [hikari-no-yume](https://github.com/hikari-no-yume)
* [seefood](https://github.com/seefood) * [seefood](https://github.com/seefood)
* [jackjennings](https://github.com/jackjennings) * [jackjennings](https://github.com/jackjennings)
* [mfmfuyu](https://github.com/mfmfuyu)
* [puckipedia](https://github.com/puckipedia) * [puckipedia](https://github.com/puckipedia)
* [spla](mailto:spla@mastodont.cat) * [spla](mailto:spla@mastodont.cat)
* [walf443](https://github.com/walf443) * [walf443](https://github.com/walf443)
@@ -112,15 +111,14 @@ and provided thanks to the work of the following contributors:
* [Ashley](mailto:expenses@airmail.cc) * [Ashley](mailto:expenses@airmail.cc)
* [xqus](https://github.com/xqus) * [xqus](https://github.com/xqus)
* [pfm-eyesightjp](https://github.com/pfm-eyesightjp) * [pfm-eyesightjp](https://github.com/pfm-eyesightjp)
* [fakenine](https://github.com/fakenine) * [Samy KACIMI](mailto:samy.kacimi@gmail.com)
* [tsuwatch](https://github.com/tsuwatch) * [tsuwatch](https://github.com/tsuwatch)
* [victorhck](https://github.com/victorhck) * [victorhck](https://github.com/victorhck)
* [mkljczk](https://github.com/mkljczk)
* [manuelviens](https://github.com/manuelviens) * [manuelviens](https://github.com/manuelviens)
* [tateisu](https://github.com/tateisu)
* [fvh-P](https://github.com/fvh-P) * [fvh-P](https://github.com/fvh-P)
* [rtucker](https://github.com/rtucker) * [rtucker](https://github.com/rtucker)
* [Anna e só](mailto:contraexemplos@gmail.com) * [Anna e só](mailto:contraexemplos@gmail.com)
* [dariusk](https://github.com/dariusk)
* [kazu9su](https://github.com/kazu9su) * [kazu9su](https://github.com/kazu9su)
* [Komic](https://github.com/Komic) * [Komic](https://github.com/Komic)
* [lmorchard](https://github.com/lmorchard) * [lmorchard](https://github.com/lmorchard)
@@ -147,9 +145,9 @@ and provided thanks to the work of the following contributors:
* [fhemberger](https://github.com/fhemberger) * [fhemberger](https://github.com/fhemberger)
* [Gomasy](https://github.com/Gomasy) * [Gomasy](https://github.com/Gomasy)
* [greysteil](https://github.com/greysteil) * [greysteil](https://github.com/greysteil)
* [hendotcat](https://github.com/hendotcat) * [hencatsmith](https://github.com/hencatsmith)
* [d6rkaiz](https://github.com/d6rkaiz) * [d6rkaiz](https://github.com/d6rkaiz)
* [ladyisatis](https://github.com/ladyisatis) * [Reverite](https://github.com/Reverite)
* [JohnD28](https://github.com/JohnD28) * [JohnD28](https://github.com/JohnD28)
* [znz](https://github.com/znz) * [znz](https://github.com/znz)
* [saper](https://github.com/saper) * [saper](https://github.com/saper)
@@ -162,14 +160,14 @@ and provided thanks to the work of the following contributors:
* [leopku](https://github.com/leopku) * [leopku](https://github.com/leopku)
* [SansPseudoFix](https://github.com/SansPseudoFix) * [SansPseudoFix](https://github.com/SansPseudoFix)
* [spla](mailto:sp@mastodont.cat) * [spla](mailto:sp@mastodont.cat)
* [tateisu](https://github.com/tateisu)
* [tomfhowe](https://github.com/tomfhowe) * [tomfhowe](https://github.com/tomfhowe)
* [noraworld](https://github.com/noraworld) * [noraworld](https://github.com/noraworld)
* [lfuelling](https://github.com/lfuelling) * [lfuelling](https://github.com/lfuelling)
* [aji-su](https://github.com/aji-su) * [theboss](https://github.com/theboss)
* [nzws](https://github.com/nzws) * [nzws](https://github.com/nzws)
* [duxovni](https://github.com/duxovni) * [duxovni](https://github.com/duxovni)
* [smorimoto](https://github.com/smorimoto) * [smorimoto](https://github.com/smorimoto)
* [mashirozx](https://github.com/mashirozx)
* [178inaba](https://github.com/178inaba) * [178inaba](https://github.com/178inaba)
* [acid-chicken](https://github.com/acid-chicken) * [acid-chicken](https://github.com/acid-chicken)
* [xgess](https://github.com/xgess) * [xgess](https://github.com/xgess)
@@ -177,6 +175,7 @@ and provided thanks to the work of the following contributors:
* [aablinov](https://github.com/aablinov) * [aablinov](https://github.com/aablinov)
* [stalker314314](https://github.com/stalker314314) * [stalker314314](https://github.com/stalker314314)
* [cutls](https://github.com/cutls) * [cutls](https://github.com/cutls)
* [dariusk](https://github.com/dariusk)
* [huertanix](https://github.com/huertanix) * [huertanix](https://github.com/huertanix)
* [eleboucher](https://github.com/eleboucher) * [eleboucher](https://github.com/eleboucher)
* [halkeye](https://github.com/halkeye) * [halkeye](https://github.com/halkeye)
@@ -184,7 +183,7 @@ and provided thanks to the work of the following contributors:
* [treby](https://github.com/treby) * [treby](https://github.com/treby)
* [jpdevries](https://github.com/jpdevries) * [jpdevries](https://github.com/jpdevries)
* [gdpelican](https://github.com/gdpelican) * [gdpelican](https://github.com/gdpelican)
* [Korbinian](mailto:kontakt@korbinian-michl.de) * [kmichl](https://github.com/kmichl)
* [Kurtis Rainbolt-Greene](mailto:me@kurtisrainboltgreene.name) * [Kurtis Rainbolt-Greene](mailto:me@kurtisrainboltgreene.name)
* [panarom](https://github.com/panarom) * [panarom](https://github.com/panarom)
* [Dar13](https://github.com/Dar13) * [Dar13](https://github.com/Dar13)
@@ -226,7 +225,6 @@ and provided thanks to the work of the following contributors:
* [aaribaud](https://github.com/aaribaud) * [aaribaud](https://github.com/aaribaud)
* [pointlessone](https://github.com/pointlessone) * [pointlessone](https://github.com/pointlessone)
* [Andrew](mailto:andrewlchronister@gmail.com) * [Andrew](mailto:andrewlchronister@gmail.com)
* [arielrodrigues](https://github.com/arielrodrigues)
* [aurelien-reeves](https://github.com/aurelien-reeves) * [aurelien-reeves](https://github.com/aurelien-reeves)
* [elegaanz](https://github.com/elegaanz) * [elegaanz](https://github.com/elegaanz)
* [estuans](https://github.com/estuans) * [estuans](https://github.com/estuans)
@@ -240,7 +238,6 @@ and provided thanks to the work of the following contributors:
* [muffinista](https://github.com/muffinista) * [muffinista](https://github.com/muffinista)
* [cdutson](https://github.com/cdutson) * [cdutson](https://github.com/cdutson)
* [farlistener](https://github.com/farlistener) * [farlistener](https://github.com/farlistener)
* [divergentdave](https://github.com/divergentdave)
* [DavidLibeau](https://github.com/DavidLibeau) * [DavidLibeau](https://github.com/DavidLibeau)
* [dmerejkowsky](https://github.com/dmerejkowsky) * [dmerejkowsky](https://github.com/dmerejkowsky)
* [ddevault](https://github.com/ddevault) * [ddevault](https://github.com/ddevault)
@@ -279,7 +276,7 @@ and provided thanks to the work of the following contributors:
* [xPaw](https://github.com/xPaw) * [xPaw](https://github.com/xPaw)
* [petzah](https://github.com/petzah) * [petzah](https://github.com/petzah)
* [ignisf](https://github.com/ignisf) * [ignisf](https://github.com/ignisf)
* [lumenwrites](https://github.com/lumenwrites) * [raymestalez](https://github.com/raymestalez)
* [remram44](https://github.com/remram44) * [remram44](https://github.com/remram44)
* [sts10](https://github.com/sts10) * [sts10](https://github.com/sts10)
* [SuperSandro2000](https://github.com/SuperSandro2000) * [SuperSandro2000](https://github.com/SuperSandro2000)
@@ -289,9 +286,8 @@ and provided thanks to the work of the following contributors:
* [Sir-Boops](https://github.com/Sir-Boops) * [Sir-Boops](https://github.com/Sir-Boops)
* [stemid](https://github.com/stemid) * [stemid](https://github.com/stemid)
* [sumdog](https://github.com/sumdog) * [sumdog](https://github.com/sumdog)
* [OmmyZhang](https://github.com/OmmyZhang)
* [ThomasLeister](https://github.com/ThomasLeister) * [ThomasLeister](https://github.com/ThomasLeister)
* [Tom McAtee](mailto:a1608768@student.adelaide.edu.au) * [mcat-ee](https://github.com/mcat-ee)
* [tototoshi](https://github.com/tototoshi) * [tototoshi](https://github.com/tototoshi)
* [TrashMacNugget](https://github.com/TrashMacNugget) * [TrashMacNugget](https://github.com/TrashMacNugget)
* [VirtuBox](https://github.com/VirtuBox) * [VirtuBox](https://github.com/VirtuBox)
@@ -318,13 +314,11 @@ and provided thanks to the work of the following contributors:
* [matsurai25](https://github.com/matsurai25) * [matsurai25](https://github.com/matsurai25)
* [mecab](https://github.com/mecab) * [mecab](https://github.com/mecab)
* [nicobz25](https://github.com/nicobz25) * [nicobz25](https://github.com/nicobz25)
* [niwatori24](https://github.com/niwatori24)
* [oliverkeeble](https://github.com/oliverkeeble) * [oliverkeeble](https://github.com/oliverkeeble)
* [partev](https://github.com/partev) * [partev](https://github.com/partev)
* [pinfort](https://github.com/pinfort) * [pinfort](https://github.com/pinfort)
* [rbaumert](https://github.com/rbaumert) * [rbaumert](https://github.com/rbaumert)
* [rhoio](https://github.com/rhoio) * [rhoio](https://github.com/rhoio)
* [santiagorodriguez96](https://github.com/santiagorodriguez96)
* [sclaire-1](https://github.com/sclaire-1) * [sclaire-1](https://github.com/sclaire-1)
* [umonaca](https://github.com/umonaca) * [umonaca](https://github.com/umonaca)
* [usagi-f](https://github.com/usagi-f) * [usagi-f](https://github.com/usagi-f)
@@ -333,7 +327,7 @@ and provided thanks to the work of the following contributors:
* [wxcafe](https://github.com/wxcafe) * [wxcafe](https://github.com/wxcafe)
* [Grawl](https://github.com/Grawl) * [Grawl](https://github.com/Grawl)
* [新都心(Neet Shin)](mailto:nucx@dio-vox.com) * [新都心(Neet Shin)](mailto:nucx@dio-vox.com)
* [clarfonthey](https://github.com/clarfonthey) * [clarfon](https://github.com/clarfon)
* [cygnan](https://github.com/cygnan) * [cygnan](https://github.com/cygnan)
* [Awea](https://github.com/Awea) * [Awea](https://github.com/Awea)
* [eai04191](https://github.com/eai04191) * [eai04191](https://github.com/eai04191)
@@ -364,11 +358,11 @@ and provided thanks to the work of the following contributors:
* [schas002](https://github.com/schas002) * [schas002](https://github.com/schas002)
* [contraexemplo](https://github.com/contraexemplo) * [contraexemplo](https://github.com/contraexemplo)
* [abackstrom](https://github.com/abackstrom) * [abackstrom](https://github.com/abackstrom)
* [arielrodrigues](https://github.com/arielrodrigues)
* [orlea](https://github.com/orlea) * [orlea](https://github.com/orlea)
* [armandfardeau](https://github.com/armandfardeau) * [armandfardeau](https://github.com/armandfardeau)
* [raboof](https://github.com/raboof) * [raboof](https://github.com/raboof)
* [jumbosushi](https://github.com/jumbosushi) * [jumbosushi](https://github.com/jumbosushi)
* [acuteaura](https://github.com/acuteaura)
* [ayumin](https://github.com/ayumin) * [ayumin](https://github.com/ayumin)
* [bzg](https://github.com/bzg) * [bzg](https://github.com/bzg)
* [BastienDurel](https://github.com/BastienDurel) * [BastienDurel](https://github.com/BastienDurel)
@@ -395,7 +389,7 @@ and provided thanks to the work of the following contributors:
* [colindean](https://github.com/colindean) * [colindean](https://github.com/colindean)
* [DeeUnderscore](https://github.com/DeeUnderscore) * [DeeUnderscore](https://github.com/DeeUnderscore)
* [dachinat](https://github.com/dachinat) * [dachinat](https://github.com/dachinat)
* [monsterpit-firedemon](https://github.com/monsterpit-firedemon) * [shapeshifter-system](https://github.com/shapeshifter-system)
* [watilde](https://github.com/watilde) * [watilde](https://github.com/watilde)
* [daprice](https://github.com/daprice) * [daprice](https://github.com/daprice)
* [da2x](https://github.com/da2x) * [da2x](https://github.com/da2x)
@@ -406,13 +400,14 @@ and provided thanks to the work of the following contributors:
* [singingwolfboy](https://github.com/singingwolfboy) * [singingwolfboy](https://github.com/singingwolfboy)
* [caldwell](https://github.com/caldwell) * [caldwell](https://github.com/caldwell)
* [davidcelis](https://github.com/davidcelis) * [davidcelis](https://github.com/davidcelis)
* [divergentdave](https://github.com/divergentdave)
* [davefp](https://github.com/davefp) * [davefp](https://github.com/davefp)
* [yipdw](https://github.com/yipdw) * [yipdw](https://github.com/yipdw)
* [debanshuk](https://github.com/debanshuk) * [debanshuk](https://github.com/debanshuk)
* [mascali33](https://github.com/mascali33) * [mascali33](https://github.com/mascali33)
* [DerekNonGeneric](https://github.com/DerekNonGeneric) * [DerekNonGeneric](https://github.com/DerekNonGeneric)
* [dblandin](https://github.com/dblandin) * [dblandin](https://github.com/dblandin)
* [Aranaur](https://github.com/Aranaur) * [Drew Gates](mailto:aranaur@users.noreply.github.com)
* [dtschust](https://github.com/dtschust) * [dtschust](https://github.com/dtschust)
* [Dryusdan](https://github.com/Dryusdan) * [Dryusdan](https://github.com/Dryusdan)
* [d3vgru](https://github.com/d3vgru) * [d3vgru](https://github.com/d3vgru)
@@ -456,25 +451,22 @@ and provided thanks to the work of the following contributors:
* [J Yeary](mailto:usbsnowcrash@users.noreply.github.com) * [J Yeary](mailto:usbsnowcrash@users.noreply.github.com)
* [jack-michaud](https://github.com/jack-michaud) * [jack-michaud](https://github.com/jack-michaud)
* [Floppy](https://github.com/Floppy) * [Floppy](https://github.com/Floppy)
* [Jarek Lipski](mailto:pub@loomchild.net) * [loomchild](https://github.com/loomchild)
* [Jennifer Glauche](mailto:=^.^=@github19.jglauche.de) * [jglauche](https://github.com/jglauche)
* [Jennifer Kruse](mailto:jenkr55@gmail.com) * [jenkr55](https://github.com/jenkr55)
* [Jeremy Rose](mailto:nornagon@nornagon.net) * [hyenagirl64](https://github.com/hyenagirl64)
* [Jessica](mailto:46502909+hyenagirl64@users.noreply.github.com) * [press5](https://github.com/press5)
* [Jessica K. Litwin](mailto:jessica@litw.in) * [TrollDecker](https://github.com/TrollDecker)
* [Jo Decker](mailto:trolldecker@users.noreply.github.com) * [jmontane](https://github.com/jmontane)
* [Joan Montané](mailto:jmontane@users.noreply.github.com)
* [Jonathan Klee](mailto:klee.jonathan@gmail.com) * [Jonathan Klee](mailto:klee.jonathan@gmail.com)
* [Jordan Guerder](mailto:jguerder@fr.pulseheberg.net) * [Jordan Guerder](mailto:jguerder@fr.pulseheberg.net)
* [Joseph Mingrone](mailto:jehops@users.noreply.github.com) * [Joseph Mingrone](mailto:jehops@users.noreply.github.com)
* [Josh Leeb-du Toit](mailto:mail@joshleeb.com)
* [Joshua Wood](mailto:josh@joshuawood.net) * [Joshua Wood](mailto:josh@joshuawood.net)
* [Julien](mailto:tiwy57@users.noreply.github.com) * [Julien](mailto:tiwy57@users.noreply.github.com)
* [Julien Deswaef](mailto:juego@requiem4tv.com) * [Julien Deswaef](mailto:juego@requiem4tv.com)
* [June Sallou](mailto:jnsll@users.noreply.github.com) * [June Sallou](mailto:jnsll@users.noreply.github.com)
* [Jérémy Benoist](mailto:j0k3r@users.noreply.github.com) * [Jérémy Benoist](mailto:j0k3r@users.noreply.github.com)
* [KEINOS](mailto:github@keinos.com) * [KEINOS](mailto:github@keinos.com)
* [Kairui Song | 宋恺睿](mailto:ryncsn@gmail.com)
* [Keiji Matsuzaki](mailto:futoase@gmail.com) * [Keiji Matsuzaki](mailto:futoase@gmail.com)
* [Kevin Liu](mailto:kevin@potatofrom.space) * [Kevin Liu](mailto:kevin@potatofrom.space)
* [Kit Redgrave](mailto:qwertyitis@gmail.com) * [Kit Redgrave](mailto:qwertyitis@gmail.com)
@@ -490,6 +482,7 @@ and provided thanks to the work of the following contributors:
* [Lukas Burk](mailto:jemus42@users.noreply.github.com) * [Lukas Burk](mailto:jemus42@users.noreply.github.com)
* [Manato Kameya](mailto:grabacr07+github@gmail.com) * [Manato Kameya](mailto:grabacr07+github@gmail.com)
* [Mantas](mailto:mistermantas@users.noreply.github.com) * [Mantas](mailto:mistermantas@users.noreply.github.com)
* [Marcin Mikołajczak](mailto:me@mkljczk.pl)
* [Mareena Kunjachan](mailto:mareenakunjachan@gmail.com) * [Mareena Kunjachan](mailto:mareenakunjachan@gmail.com)
* [Marek Lach](mailto:marek.brohatwack.lach@gmail.com) * [Marek Lach](mailto:marek.brohatwack.lach@gmail.com)
* [Markus R](mailto:wirehack7@users.noreply.github.com) * [Markus R](mailto:wirehack7@users.noreply.github.com)
@@ -536,12 +529,10 @@ and provided thanks to the work of the following contributors:
* [Norayr Chilingarian](mailto:norayr@arnet.am) * [Norayr Chilingarian](mailto:norayr@arnet.am)
* [Noëlle Anthony](mailto:noelle.d.anthony@gmail.com) * [Noëlle Anthony](mailto:noelle.d.anthony@gmail.com)
* [N氏](mailto:uenok.htc@gmail.com) * [N氏](mailto:uenok.htc@gmail.com)
* [OSAMU SATO](mailto:satosamu@gmail.com)
* [Olivier Nicole](mailto:olivierthnicole@gmail.com) * [Olivier Nicole](mailto:olivierthnicole@gmail.com)
* [Oskari Noppa](mailto:noppa@users.noreply.github.com) * [Oskari Noppa](mailto:noppa@users.noreply.github.com)
* [Otakan](mailto:otakan951@gmail.com) * [Otakan](mailto:otakan951@gmail.com)
* [Padraig Fahy](mailto:tech@padraigfahy.com) * [Padraig Fahy](mailto:tech@padraigfahy.com)
* [Patrice Ferlet](mailto:metal3d@gmail.com)
* [PatrickRWells](mailto:32802366+patrickrwells@users.noreply.github.com) * [PatrickRWells](mailto:32802366+patrickrwells@users.noreply.github.com)
* [Paul](mailto:naydex.mc+github@gmail.com) * [Paul](mailto:naydex.mc+github@gmail.com)
* [Pete Keen](mailto:pete@petekeen.net) * [Pete Keen](mailto:pete@petekeen.net)
@@ -583,6 +574,7 @@ and provided thanks to the work of the following contributors:
* [TakesxiSximada](mailto:takesxi.sximada@gmail.com) * [TakesxiSximada](mailto:takesxi.sximada@gmail.com)
* [Tao Bror Bojlén](mailto:brortao@users.noreply.github.com) * [Tao Bror Bojlén](mailto:brortao@users.noreply.github.com)
* [Taras Gogol](mailto:taras2358@gmail.com) * [Taras Gogol](mailto:taras2358@gmail.com)
* [Tdxdxoz](mailto:tdxdxoz@gmail.com)
* [TheInventrix](mailto:theinventrix@users.noreply.github.com) * [TheInventrix](mailto:theinventrix@users.noreply.github.com)
* [TheMainOne](mailto:50847364+theevilskeleton@users.noreply.github.com) * [TheMainOne](mailto:50847364+theevilskeleton@users.noreply.github.com)
* [Thomas Alberola](mailto:thomas@needacoffee.fr) * [Thomas Alberola](mailto:thomas@needacoffee.fr)
@@ -602,7 +594,6 @@ and provided thanks to the work of the following contributors:
* [Wesley Ellis](mailto:tahnok@gmail.com) * [Wesley Ellis](mailto:tahnok@gmail.com)
* [Wiktor](mailto:wiktor@metacode.biz) * [Wiktor](mailto:wiktor@metacode.biz)
* [Wonderfall](mailto:wonderfall@schrodinger.io) * [Wonderfall](mailto:wonderfall@schrodinger.io)
* [Y.Yamashiro](mailto:shukukei@mojizuri.jp)
* [YDrogen](mailto:ydrogen45@gmail.com) * [YDrogen](mailto:ydrogen45@gmail.com)
* [YMHuang](mailto:ymhuang@fmbase.tw) * [YMHuang](mailto:ymhuang@fmbase.tw)
* [YOSHIOKA Eiichiro](mailto:yoshioka.eiichiro@gmail.com) * [YOSHIOKA Eiichiro](mailto:yoshioka.eiichiro@gmail.com)
@@ -647,7 +638,6 @@ and provided thanks to the work of the following contributors:
* [jumoru](mailto:jumoru@mailbox.org) * [jumoru](mailto:jumoru@mailbox.org)
* [kaiyou](mailto:pierre@jaury.eu) * [kaiyou](mailto:pierre@jaury.eu)
* [karlyeurl](mailto:karl.yeurl@gmail.com) * [karlyeurl](mailto:karl.yeurl@gmail.com)
* [kawaguchi](mailto:jiikko@users.noreply.github.com)
* [kedama](mailto:32974885+kedamadq@users.noreply.github.com) * [kedama](mailto:32974885+kedamadq@users.noreply.github.com)
* [kuro5hin](mailto:rusty@kuro5hin.org) * [kuro5hin](mailto:rusty@kuro5hin.org)
* [leo60228](mailto:leo@60228.dev) * [leo60228](mailto:leo@60228.dev)
@@ -665,7 +655,6 @@ and provided thanks to the work of the following contributors:
* [notozeki](mailto:notozeki@users.noreply.github.com) * [notozeki](mailto:notozeki@users.noreply.github.com)
* [ntl-purism](mailto:57806346+ntl-purism@users.noreply.github.com) * [ntl-purism](mailto:57806346+ntl-purism@users.noreply.github.com)
* [nzws](mailto:git-yuzu@svk.jp) * [nzws](mailto:git-yuzu@svk.jp)
* [proxy](mailto:51172302+3n-k1@users.noreply.github.com)
* [rch850](mailto:rich850@gmail.com) * [rch850](mailto:rich850@gmail.com)
* [roikale](mailto:roikale@users.noreply.github.com) * [roikale](mailto:roikale@users.noreply.github.com)
* [rysiekpl](mailto:rysiek@hackerspace.pl) * [rysiekpl](mailto:rysiek@hackerspace.pl)
@@ -705,414 +694,308 @@ This document is provided for informational purposes only. Since it is only upda
Following people have contributed to translation of Mastodon: Following people have contributed to translation of Mastodon:
- ᏦᏁᎢᎵᏫ 😷 (KNTRO) (*Spanish, Argentina*) - ᏦᏁᎢᎵᏫ 😷 (*Spanish, Argentina*)
- Sveinn í Felli (sveinki) (*Icelandic*) - Sveinn í Felli (*Icelandic*)
- qezwan (*Persian, Sorani (Kurdish)*)
- Hồ Nhất Duy (kantcer) (*Vietnamese*)
- taicv (*Vietnamese*) - taicv (*Vietnamese*)
- Zoltán Gera (gerazo) (*Hungarian*) - ButterflyOfFire (*Arabic; French; Kabyle*)
- ButterflyOfFire (BoFFire) (*French, Arabic, Kabyle*) - Duy (*Vietnamese*)
- adrmzz (*Sardinian*) - Evert Prants (*Estonian*)
- Ramdziana F Y (rafeyu) (*Indonesian*) - Zoltán Gera (*Hungarian*)
- Evert Prants (IcyDiamond) (*Estonian*) - Daniele Lira Mereb (*Portuguese, Brazilian*)
- Daniele Lira Mereb (danilmereb) (*Portuguese, Brazilian*) - Kristijan Tkalec (*Slovenian*)
- Xosé M. (XoseM) (*Spanish, Galician*) - stan ionut (*Romanian*)
- Kristijan Tkalec (lapor) (*Slovenian*) - Ramdziana F Y (*Indonesian*)
- stan ionut (stanionut12) (*Romanian*) - Michal Stanke (*Czech*)
- Besnik_b (*Albanian*) - Xosé M. (*Galician; Spanish*)
- Emanuel Pina (emanuelpina) (*Portuguese*) - 奈卜拉 (*Chinese Simplified*)
- Thai Localization (thl10n) (*Thai*)
- 奈卜拉 (nebula_moe) (*Chinese Simplified*)
- Jeong Arm (Kjwon15) (*Japanese, Korean, Esperanto*)
- Michal Stanke (mstanke) (*Czech*)
- Alix Rossi (palindromordnilap) (*French, Corsican*)
- spla (*Spanish, Catalan*)
- Imre Kristoffer Eilertsen (DandelionSprout) (*Norwegian*)
- Jeroen (jeroenpraat) (*Dutch*)
- borys_sh (*Ukrainian*) - borys_sh (*Ukrainian*)
- Miguel Mayol (mitcoes) (*Spanish, Catalan*) - Miguel Mayol (*Spanish; Catalan*)
- Danial Behzadi (danialbehzadi) (*Persian*) - Besnik_b (*Albanian*)
- yeft (*Chinese Traditional, Chinese Traditional, Hong Kong*) - Thai Localization (*Thai*)
- koyu (*German*) - Emanuel Pina (*Portuguese*)
- Koala Yeung (yookoala) (*Chinese Traditional, Hong Kong*) - Jeong Arm (*Korean; Esperanto; Japanese*)
- Imre Kristoffer Eilertsen (*Norwegian*)
- Danial Behzadi (*Persian*)
- Osoitz (*Basque*) - Osoitz (*Basque*)
- Peterandre (*Norwegian, Norwegian Nynorsk*) - Peterandre (*Norwegian Nynorsk; Norwegian*)
- tzium (*Sardinian*) - Jeroen (*Dutch*)
- Iváns (Ivans_translator) (*Galician*) - spla (*Catalan; Spanish*)
- Sasha Sorokin (Sasha-Sorokin) (*French, Catalan, Danish, German, Greek, Hungarian, Armenian, Korean, Russian, Albanian, Swedish, Ukrainian, Vietnamese, Galician*) - Iváns (*Galician*)
- kamee (*Armenian*) - koyu (*German*)
- tolstoevsky (*Russian*) - Sasha Sorokin (*Russian; Vietnamese; Swedish; Catalan; Greek; Hungarian; Armenian; Albanian; Galician; French; Danish; German; Korean; Ukrainian*)
- enolp (*Asturian*) - enolp (*Asturian*)
- FédiQuébec (manuelviens) (*French*) - Masoud Abkenar (*Persian*)
- lamnatos (*Greek*) - lamnatos (*Greek*)
- Maya Minatsuki (mayaeh) (*Japanese*) - Alix Rossi (*Corsican; French*)
- Masoud Abkenar (mabkenar) (*Persian*)
- Alessandro Levati (Oct326) (*Italian*)
- arshat (*Kazakh*) - arshat (*Kazakh*)
- Roboron (*Spanish*) - FédiQuébec (*French*)
- ariasuni (*French, Arabic, Czech, German, Greek, Hungarian, Slovenian, Ukrainian, Chinese Simplified, Portuguese, Brazilian, Persian, Norwegian Nynorsk, Esperanto, Breton, Corsican, Sardinian, Kabyle*) - Marek Ľach (*Slovak; Polish*)
- Ali Demirtaş (alidemirtas) (*Turkish*) - Muha Aliss (*Turkish*)
- Em St Cenydd (cancennau) (*Welsh*) - tolstoevsky (*Russian*)
- Marek Ľach (mareklach) (*Polish, Slovak*) - Emyn-Russell Nt Nefydd (*Welsh*)
- Muha Aliss (muhaaliss) (*Turkish*)
- Jurica (ahjk) (*Croatian*)
- Aditoo17 (*Czech*) - Aditoo17 (*Czech*)
- Maya Minatsuki (*Japanese*)
- ariasuni (*French; Esperanto*)
- Roboron (*Spanish*)
- Alessandro Levati (*Italian*)
- Diluns (*Occitan*) - Diluns (*Occitan*)
- gagik_ (*Armenian*)
- vishnuvaratharajan (*Tamil*)
- Marcin Mikołajczak (mkljczkk) (*Czech, Polish, Russian*)
- regulartranslator (*Portuguese, Brazilian*) - regulartranslator (*Portuguese, Brazilian*)
- Akarshan Biswas (biswasab) (*Bengali, Sanskrit*) - vishnuvaratharajan (*Tamil*)
- Yi-Jyun Pan (pan93412) (*Chinese Traditional*) - Marcin Mikołajczak (*Polish*)
- Yi-Jyun Pan (*Chinese Traditional*)
- adrmzz (*Sardinian*)
- d5Ziif3K (*Ukrainian*) - d5Ziif3K (*Ukrainian*)
- GiorgioHerbie (*Italian*) - GiorgioHerbie (*Italian*)
- Rafael H L Moretti (Moretti) (*Portuguese, Brazilian*)
- Saederup92 (*Danish*)
- christalleras (*Norwegian Nynorsk*) - christalleras (*Norwegian Nynorsk*)
- cybergene (cyber-gene) (*Japanese*)
- Taloran (*Norwegian Nynorsk*) - Taloran (*Norwegian Nynorsk*)
- ThibG (*French, Icelandic*) - ThibG (*French; Icelandic*)
- xatier (*Chinese Traditional*) - Akarshan Biswas (*Bengali*)
- otrapersona (*Spanish, Spanish, Mexico*)
- atarashiako (*Chinese Simplified*) - atarashiako (*Chinese Simplified*)
- 101010 (101010pl) (*Polish*) - 101010 (*Polish*)
- silkevicious (*Italian*) - silkevicious (*Italian*)
- Floxu (fredrikdim1) (*Norwegian Nynorsk*) - Bertil Hedkvist (*Swedish*)
- Bertil Hedkvist (Berrahed) (*Swedish*) - cybergene (*Japanese*)
- William(ѕ)ⁿ (wmlgr) (*Spanish*)
- norayr (*Armenian*) - norayr (*Armenian*)
- Tiago Epifânio (tfve) (*Portuguese*) - William(ѕ)ⁿ (*Spanish*)
- Ryo (DrRyo) (*Korean*) - Tiago Epifânio (*Portuguese*)
- Mentor Gashi (mentorgashi.com) (*Albanian*) - Mentor Gashi (*Albanian*)
- Jaz-Michael King (jazmichaelking) (*Welsh*) - Jaz-Michael King (*Welsh*)
- carolinagiorno (*Portuguese, Brazilian*) - carolinagiorno (*Portuguese, Brazilian*)
- Roby Thomas (roby.thomas) (*Malayalam*) - Roby Thomas (*Malayalam*)
- Bharat Kumar (Marwari) (*Hindi*) - Bharat Kumar (*Hindi*)
- ThonyVezbe (*Breton*)
- dkdarshan760 (*Sanskrit*)
- Tagomago (tagomago) (*French, Spanish*)
- tykayn (*French*) - tykayn (*French*)
- axi (*Finnish*) - axi (*Finnish*)
- Selyan Slimane AMIRI (slimane_AMIRI) (*Kabyle*) - Selyan Slimane AMIRI (*Kabyle*)
- Balázs Meskó (mesko.balazs) (*Hungarian*)
- taoxvx (*Danish*) - taoxvx (*Danish*)
- Hrach Mkrtchyan (mhrach87) (*Armenian*) - Hrach Mkrtchyan (*Armenian*)
- sabri (thetomatoisavegetable) (*Spanish, Spanish, Argentina*) - sabri (*Spanish; Spanish, Argentina*)
- Dewi (Unkorneg) (*French, Breton*) - Dewi (*Breton; French*)
- Coelacanthus (*Chinese Simplified*)
- syncopams (*Chinese Simplified, Chinese Traditional, Chinese Traditional, Hong Kong*)
- SteinarK (*Norwegian Nynorsk*) - SteinarK (*Norwegian Nynorsk*)
- Sokratis Alichanidis (alichani) (*Greek*) - Mathias B. Vagnes (*Norwegian*)
- Mathias B. Vagnes (vagnes) (*Norwegian*) - dashersyed (*Urdu*)
- dashersyed (*Urdu (Pakistan)*) - ThonyVezbe (*Breton*)
- Acolyte (666noob404) (*Ukrainian*) - Acolyte (*Ukrainian*)
- Conight Wang (xfddwhh) (*Chinese Simplified*) - Conight Wang (*Chinese Simplified*)
- liffon (*Swedish*) - Damjan Dimitrioski (*Macedonian*)
- Damjan Dimitrioski (gnud) (*Macedonian*)
- PPNplus (*Thai*) - PPNplus (*Thai*)
- Tagomago (*Spanish; French*)
- shioko (*Chinese Simplified*) - shioko (*Chinese Simplified*)
- v4vachan (*Malayalam*) - Balázs Meskó (*Hungarian*)
- Hakim Oubouali (zenata1) (*Standard Moroccan Tamazight*) - Evgeny Petrov (*Russian*)
- Evgeny Petrov (kondra007) (*Russian*) - Gwenn (*Breton*)
- Gwenn (Belvar) (*Breton*) - Ryo (*Korean*)
- StanleyFrew (*French*) - Rafael H L Moretti (*Portuguese, Brazilian*)
- Hayk Khachatryan (brutusromanus123) (*Armenian*)
- jaranta (*Finnish*) - jaranta (*Finnish*)
- Felicia (midsommar) (*Swedish*) - gagik_ (*Armenian*)
- Denys (dector) (*Ukrainian*) - Felicia (*Swedish*)
- Pukima (pukimaaa) (*German*) - Jess Rafn (*Danish*)
- Vanege (*Esperanto*) - Stasiek Michalski (*Polish*)
- Jess Rafn (therealyez) (*Danish*) - liffon (*Swedish*)
- strubbl (*German*)
- Stasiek Michalski (hellcp) (*Polish*)
- dxwc (*Bengali*) - dxwc (*Bengali*)
- Saederup92 (*Danish*)
- Vanege (*Esperanto*)
- jmontane (*Catalan*) - jmontane (*Catalan*)
- Liboide (*Spanish*) - Johan Schiff (*Swedish*)
- Johan Schiff (schyffel) (*Swedish*) - Arunmozhi (*Tamil*)
- Arunmozhi (tecoholic) (*Tamil*) - kat (*Ukrainian; Russian*)
- kat (katktv) (*Russian, Ukrainian*) - Laura (*Polish*)
- Rikard Linde (rikardlinde) (*Swedish*) - oti4500 (*Hungarian; Ukrainian*)
- oti4500 (*Hungarian, Ukrainian*) - diazepan (*Spanish; Spanish, Argentina*)
- Laura (selfisekai) (*Polish*) - Sokratis Alichanidis (*Greek*)
- Rachida S. (ZiriSut) (*Kabyle*) - Rikard Linde (*Swedish*)
- diazepan (*Spanish, Spanish, Argentina*) - Juan José Salvador Piedra (*Spanish*)
- marzuquccen (*Kabyle*) - marzuquccen (*Kabyle*)
- Juan José Salvador Piedra (JuanjoSalvador) (*Spanish*) - BurekzFinezt (*Serbian*)
- Tigran (tigransimonyan) (*Armenian*)
- BurekzFinezt (*Serbian (Cyrillic)*)
- SHeija (*Finnish*) - SHeija (*Finnish*)
- atriix (*Swedish*) - Jack R (*Spanish*)
- Jack R (isaac.97_WT) (*Spanish*) - andruhov (*Ukrainian; Russian*)
- antonyho (*Chinese Traditional, Hong Kong*) - 森の子リスのミーコの大冒険 (*Japanese*)
- andruhov (*Russian, Ukrainian*) - るいーね (*Japanese*)
- Aryamik Sharma (Aryamik) (*Swedish, Hindi*) - Sam Tux (*Bengali*)
- phena109 (*Chinese Traditional, Hong Kong*)
- 森の子リスのミーコの大冒険 (Phroneris) (*Japanese*)
- るいーね (ruine) (*Japanese*)
- ahangarha (*Persian*)
- Sam Tux (imahbub) (*Bengali*)
- igordrozniak (*Polish*)
- Unmual (*Spanish*) - Unmual (*Spanish*)
- Isaac Huang (caasih) (*Chinese Traditional*) - AW Unad (*Indonesian*)
- AW Unad (awcodify) (*Indonesian*) - Cutls (*Japanese*)
- Allen Zhong (AstroProfundis) (*Chinese Simplified*) - Ray (*Spanish*)
- Cutls (cutls) (*Japanese*) - Falling Snowdin (*Vietnamese*)
- Ray (Ipsumry) (*Spanish*) - Andrea Lo Iacono (*Italian*)
- Falling Snowdin (tghgg) (*Vietnamese*) - EPEMA (*German*)
- coxde (*Chinese Simplified*) - Kinshuk Sunil (*Hindi*)
- Rasmus Lindroth (RasmusLindroth) (*Swedish*) - Ullas Joseph (*Malayalam*)
- Andrea Lo Iacono (niels0n) (*Italian*) - Yu-Pai Liu (*Chinese Traditional*)
- Kinshuk Sunil (kinshuksunil) (*Hindi*) - Amarin Cemthong (*Thai*)
- Ullas Joseph (ullasjoseph) (*Malayalam*) - juanda097 (*Spanish*)
- Goudarz Jafari (Goudarz) (*Persian*)
- Yu-Pai Liu (tedliou) (*Chinese Traditional*)
- Amarin Cemthong (acitmaster) (*Thai*)
- juanda097 (juanda-097) (*Spanish*)
- Anunnakey (*Macedonian*) - Anunnakey (*Macedonian*)
- fragola (*Italian*) - StanleyFrew (*French*)
- erikstl (*Esperanto*) - erikstl (*Esperanto*)
- twpenguin (*Chinese Traditional*)
- bobchao (*Chinese Traditional*)
- Esther (esthermations) (*Portuguese*)
- MadeInSteak (*Finnish*) - MadeInSteak (*Finnish*)
- Heimen Stoffels (vistausss) (*Dutch*) - Heimen Stoffels (*Dutch*)
- Rajarshi Guha (rajarshiguha) (*Bengali*) - Rajarshi Guha (*Bengali*)
- Andrew (iAndrew3) (*Romanian*) - Andrew (*Romanian*)
- Gopal Sharma (gopalvirat) (*Hindi*) - Goudarz Jafari (*Persian*)
- arethsu (*Swedish*) - arethsu (*Swedish*)
- Tofiq Abdula (Xwla) (*Sorani (Kurdish)*) - Carlos Solís (*Esperanto*)
- Carlos Solís (csolisr) (*Esperanto*) - Parthan S Ramanujam (*Tamil*)
- Parthan S Ramanujam (parthan) (*Tamil*) - Ali Demirtaş (*Turkish*)
- Kasper Nymand (KasperNymand) (*Danish*) - Kasper Nymand (*Danish*)
- TS (morte) (*Finnish*) - TS (*Finnish*)
- subram (*Turkish*)
- SensDeViata (*Ukrainian*) - SensDeViata (*Ukrainian*)
- Ptrcmd (ptrcmd) (*Chinese Traditional*)
- SergioFMiranda (*Portuguese, Brazilian*) - SergioFMiranda (*Portuguese, Brazilian*)
- Scvoet (scvoet) (*Chinese Simplified*) - OctolinGamer (*Portuguese, Brazilian*)
- hiroTS (*Chinese Traditional*)
- johne32rus23 (*Russian*)
- AzureNya (*Chinese Simplified*) - AzureNya (*Chinese Simplified*)
- OctolinGamer (octolingamer) (*Portuguese, Brazilian*) - Ram varma (*Tamil*)
- Ram varma (ram4varma) (*Tamil*) - 北䑓如法 (*Japanese*)
- Hexandcube (hexandcube) (*Polish*)
- 北䑓如法 (Nyoho) (*Japanese*)
- frumble (*German*) - frumble (*German*)
- kekkepikkuni (*Tamil*) - kekkepikkuni (*Tamil*)
- Neo_Chen (NeoChen1024) (*Chinese Traditional*)
- oorsutri (*Tamil*) - oorsutri (*Tamil*)
- Rhys Harrison (rhedders) (*Esperanto*) - Nithin V (*Tamil*)
- Nithin V (Nithin896) (*Tamil*) - Miro Rauhala (*Finnish*)
- Miro Rauhala (mirorauhala) (*Finnish*)
- diorama (*Italian*) - diorama (*Italian*)
- AlexKoala (alexkoala) (*Korean*) - Rhys Harrison (*Esperanto*)
- Aswin C (officialcjunior) (*Malayalam*) - Guillaume Turchini (*French*)
- Guillaume Turchini (orion78fr) (*French*) - Ganesh D (*Marathi*)
- Ganesh D (auntgd) (*Marathi*)
- dragnucs2 (*Arabic*) - dragnucs2 (*Arabic*)
- Ryan Ho (koungho) (*Chinese Traditional*) - Pedro Henrique (*Portuguese, Brazilian*)
- Pedro Henrique (exploronauta) (*Portuguese, Brazilian*) - Tejas Harad (*Marathi*)
- Tejas Harad (h_tejas) (*Marathi*) - Vasanthan (*Tamil*)
- Vasanthan (vasanthan) (*Tamil*) - 硫酸鶏 (*Japanese*)
- 硫酸鶏 (acid_chicken) (*Japanese*)
- clarmin b8 (clarminb8) (*Sorani (Kurdish)*)
- manukp (*Malayalam*) - manukp (*Malayalam*)
- psymyn (*Hebrew*) - psymyn (*Hebrew*)
- earth dweller (sanethoughtyt) (*Marathi*) - earth dweller (*Marathi*)
- meijerivoi (toilet) (*Finnish*) - meijerivoi (*Finnish*)
- essaar (*Tamil*) - essaar (*Tamil*)
- serubeena (*Swedish*) - serubeena (*Swedish*)
- Karol Kosek (krkkPL) (*Polish*)
- Rintan (*Japanese*) - Rintan (*Japanese*)
- Karol Kosek (*Polish*)
- valarivan (*Tamil*) - valarivan (*Tamil*)
- Hernik (hernik27) (*Czech*) - Sebastián Andil (*Slovak*)
- Sebastián Andil (Selrond) (*Slovak*) - v4vachan (*Malayalam*)
- Hinaloe (hinaloe) (*Japanese*)
- filippodb (*Italian*)
- KEINOS (*Japanese*) - KEINOS (*Japanese*)
- Balázs Meskó (meskobalazs) (*Hungarian*) - Ivan T. (*Chinese Traditional, Hong Kong*)
- Bottle (suryasalem2010) (*Tamil*) - filippodb (*Italian*)
- Balázs Meskó (*Hungarian*)
- JzshAC (*Chinese Simplified*) - JzshAC (*Chinese Simplified*)
- Wrya ali (John12) (*Sorani (Kurdish)*) - Bottle (*Tamil*)
- Khóo (khootiatling) (*Chinese Traditional*) - Khóo (*Chinese Traditional*)
- Steven Tappert (sammy8806) (*German*) - Steven Tappert (*German*)
- Antillion (antillion99) (*Spanish*) - Antillion (*Spanish*)
- Pukima (Pukimaa) (*German*) - ZiriSut (*Kabyle*)
- Reg3xp (*Persian*)
- hiphipvargas (*Portuguese*)
- gowthamanb (*Tamil*) - gowthamanb (*Tamil*)
- Ch. (sftblw) (*Korean*) - hiphipvargas (*Portuguese*)
- Jeff Huang (s8321414) (*Chinese Traditional*) - Arttu Ylhävuori (*Finnish*)
- Arttu Ylhävuori (arttu.ylhavuori) (*Finnish*) - Ch. (*Korean*)
- tctovsli (*Norwegian Nynorsk*) - tctovsli (*Norwegian Nynorsk*)
- Timo Tijhof (Krinkle) (*Dutch*) - Hinaloe (*Japanese*)
- Yamagishi Kazutoshi (ykzts) (*Japanese, Icelandic, Sorani (Kurdish)*) - strubbl (*German*)
- vjasiegd (*Polish*) - vjasiegd (*Polish*)
- SamitiMed (samiti3d) (*Thai*) - SamitiMed (*Thai*)
- Rekan Adl (rekan-adl1) (*Sorani (Kurdish)*) - Reg3xp (*Persian*)
- AlexKoala (*Korean*)
- umelard (*Hebrew*) - umelard (*Hebrew*)
- Antara2Cinta (Se7enTime) (*Indonesian*)
- VSx86 (*Russian*) - VSx86 (*Russian*)
- Daniel Dimitrov (danny-dimitrov) (*Bulgarian*) - Daniel Dimitrov (*Bulgarian*)
- parnikkapore (*Thai*)
- mynameismonkey (*Welsh*) - mynameismonkey (*Welsh*)
- Sherwan Othman (sherwanothman11) (*Sorani (Kurdish)*) - parnikkapore (*Thai*)
- Yassine Aït-El-Mouden (yaitelmouden) (*Standard Moroccan Tamazight*) - Mo_der Steven (*Chinese Simplified*)
- SKELET (*Danish*) - SKELET (*Danish*)
- Mo_der Steven (SakuraPuare) (*Chinese Simplified*) - Renato "Lond" Cerqueira (*Portuguese, Brazilian*)
- Fei Yang (Fei1Yang) (*Chinese Traditional*)
- ALEM FARID (faridatcemlulaqbayli) (*Kabyle*)
- enipra (*Armenian*) - enipra (*Armenian*)
- musix (*Persian*) - musix (*Persian*)
- Renato "Lond" Cerqueira (renatolond) (*Portuguese, Brazilian*) - ギャラ (*Chinese Simplified; Japanese*)
- ギャラ (gyara) (*Japanese, Chinese Simplified*) - ALEM FARID (*Kabyle*)
- Hougo (hougo) (*French*)
- ybardapurkar (*Marathi*) - ybardapurkar (*Marathi*)
- Adrián Lattes (haztecaso) (*Spanish*) - Adrián Lattes (*Spanish*)
- TracyJacks (*Chinese Simplified*)
- rasheedgm (*Kannada*) - rasheedgm (*Kannada*)
- GatoOscuro (*Spanish*)
- mecqor labi (mecqorlabi) (*Persian*)
- Belkacem Mohammed (belkacem77) (*Kabyle*)
- Navjot Singh (nspeaks) (*Hindi*)
- omquylzu (*Latvian*) - omquylzu (*Latvian*)
- Belkacem Mohammed (*Kabyle*)
- Navjot Singh (*Hindi*)
- Ozai (*German*) - Ozai (*German*)
- Sahak Petrosyan (petrosyan) (*Armenian*) - Sahak Petrosyan (*Armenian*)
- siamano (*Thai, Esperanto*) - siamano (*Thai; Esperanto*)
- Viorel-Cătălin Răpițeanu (rapiteanu) (*Romanian*) - se7entime (*Indonesian*)
- Siddhartha Sarathi Basu (quinoa_biryani) (*Bengali*) - Viorel-Cătălin Răpițeanu (*Romanian*)
- Pachara Chantawong (pachara2202) (*Thai*) - Siddhartha Sarathi Basu (*Bengali*)
- mkljczk (*Polish*) - Pachara Chantawong (*Thai*)
- Skew (noan.perrot) (*French*) - Skew (*French*)
- Zijian Zhao (jobs2512821228) (*Chinese Simplified*) - Zijian Zhao (*Chinese Simplified*)
- Guru Prasath Anandapadmanaban (*Tamil*)
- turtle836 (*German*) - turtle836 (*German*)
- Guru Prasath Anandapadmanaban (guruprasath) (*Tamil*) - GatoOscuro (*Spanish*)
- Lamin (laminne) (*Japanese*) - Lamin (*Japanese*)
- Marcepanek_ (thekingmarcepan) (*Polish*) - Marcepanek_ (*Polish*)
- Feruz Oripov (FeruzOripov) (*Russian*) - Yann Aguettaz (*French*)
- Yann Aguettaz (yann-a) (*French*) - Feruz Oripov (*Russian*)
- Mick Onio (xgc.redes) (*Asturian*) - Mick Onio (*Asturian*)
- Tianqi Zhang (tina.zhang040609) (*Chinese Simplified*)
- Malik Mann (dermalikmann) (*German*)
- dadosch (*German*)
- r3dsp1 (*Chinese Traditional, Hong Kong*)
- padulafacundo (*Spanish*)
- hg6 (*Hindi*) - hg6 (*Hindi*)
- Orlando Murcio (Atos20) (*Spanish, Mexico*) - Malik Mann (*German*)
- piupiupiudiu (*Chinese Simplified*) - padulafacundo (*Spanish*)
- shdy (*German*) - r3dsp1 (*Chinese Traditional, Hong Kong*)
- Padraic Calpin (padraic-padraic) (*Slovenian*) - Tianqi Zhang (*Chinese Simplified*)
- Ильзира Рахматуллина (rahmatullinailzira53) (*Tatar*) - Padraic Calpin (*Slovenian*)
- cenegd (*Chinese Simplified*) - cenegd (*Chinese Simplified*)
- Hugh Liu (youloveonlymeh) (*Chinese Simplified*) - piupiupiudiu (*Chinese Simplified*)
- Pixelcode (realpixelcode) (*German*) - Hugh Liu (*Chinese Simplified*)
- Yogesh K S (yogi) (*Kannada*) - Rakino (*Chinese Simplified*)
- Rakino (rakino) (*Chinese Simplified*) - Jothipazhani Nagarajan (*Tamil*)
- Miquel Sabaté Solà (mssola) (*Catalan*) - Miquel Sabaté Solà (*Catalan*)
- AmazighNM (*Kabyle*) - AmazighNM (*Kabyle*)
- Jothipazhani Nagarajan (jothipazhani.n) (*Tamil*) - Solid Rhino (*Dutch*)
- Clash Clans (KURD12345) (*Sorani (Kurdish)*)
- hallomaurits (*Dutch*) - hallomaurits (*Dutch*)
- alnd hezh (alndhezh) (*Sorani (Kurdish)*)
- Solid Rhino (SolidRhino) (*Dutch*)
- k_taka (peaceroad) (*Japanese*)
- Hallo Abdullah (hallo_hamza12) (*Sorani (Kurdish)*)
- hussama (*Portuguese, Brazilian*) - hussama (*Portuguese, Brazilian*)
- Sébastien Feugère (smonff) (*French*)
- 林水溶 (shuiRong) (*Chinese Simplified*)
- eichkat3r (*German*)
- OminousCry (*Russian*)
- SnDer (*Dutch*)
- PifyZ (*French*)
- Tom_ (*Czech*)
- Tagada (Tagadda) (*French*)
- shafouz (*Portuguese, Brazilian*) - shafouz (*Portuguese, Brazilian*)
- Kahina Mess (K_hina) (*Kabyle*) - Tagada (*French*)
- Nathaël Noguès (NatNgs) (*French*) - Tom_ (*Czech*)
- Kk (kishorkumara3) (*Kannada*) - SnDer (*Dutch*)
- Swati Sani (swatisani) (*Urdu (Pakistan)*) - eichkat3r (*German*)
- Shrinivasan T (tshrinivasan) (*Tamil*) - PifyZ (*French*)
- さっかりんにーさん (saccharin23) (*Japanese*) - OminousCry (*Russian*)
- 夜楓Yoka (Yoka2627) (*Chinese Simplified*) - Shrinivasan T (*Tamil*)
- Daniel M. (daniconil) (*Catalan*) - Nathaël Noguès (*French*)
- Vikatakavi (*Kannada*) - Daniel M. (*Catalan*)
- Swati Sani (*Urdu*)
- Kk (*Kannada*)
- SusVersiva (*Catalan*) - SusVersiva (*Catalan*)
- Tradjincal (tradjincal) (*French*) - Robin van der Vliet (*Esperanto*)
- pullopen (*Chinese Simplified*)
- Robin van der Vliet (RobinvanderVliet) (*Esperanto*)
- Zinkokooo (*Basque*) - Zinkokooo (*Basque*)
- mmokhi (*Persian*) - Tradjincal (*French*)
- Livingston Samuel (livingston) (*Tamil*) - Vikatakavi (*Kannada*)
- prabhjot (*Hindi*) - prabhjot (*Hindi*)
- twpenguin (*Chinese Traditional*)
- mmokhi (*Persian*)
- sergioaraujo1 (*Portuguese, Brazilian*) - sergioaraujo1 (*Portuguese, Brazilian*)
- CyberAmoeba (pseudoobscura) (*Chinese Simplified*) - Livingston Samuel (*Tamil*)
- tsundoker (*Malayalam*) - tsundoker (*Malayalam*)
- skaaarrr (*German*) - skaaarrr (*German*)
- Ricardo Colin (rysard) (*Spanish*) - 夜楓Yoka (*Chinese Simplified*)
- mkljczk (mykylyjczyk) (*Polish*) - kiwi0 (*Italian*)
- Philipp Fischbeck (PFischbeck) (*German*)
- fedot (*Russian*) - fedot (*Russian*)
- Paz Galindo (paz.almendra.g) (*Spanish*) - mkljczk (*Polish*)
- GaggiX (*Italian*) - igordrozniak (*Polish*)
- Ricardo Colin (*Spanish*)
- Esther (*Portuguese*)
- Paz Galindo (*Spanish*)
- Philipp Fischbeck (*German*)
- ralozkolya (*Georgian*) - ralozkolya (*Georgian*)
- Zoé Bőle (zoe1337) (*German*) - JackXu (*Chinese Simplified*)
- Lukas Fülling (lfuelling) (*German*) - Allen Zhong (*Chinese Simplified*)
- JackXu (Merman-Jack) (*Chinese Simplified*) - Zoé Bőle (*German*)
- Aymeric (AymBroussier) (*French*) - Lukas Fülling (*German*)
- Anoop (anoopp) (*Malayalam*) - Albatroz Jeremias (*Portuguese*)
- Samir Tighzert (*Kabyle*)
- Nocta (*French*)
- Anoop (*Malayalam*)
- pezcurrel (*Italian*) - pezcurrel (*Italian*)
- Dremski (*Bulgarian*) - Dremski (*Bulgarian*)
- Xurxo Guerra (xguerrap) (*Galician*) - Aymeric (*French*)
- mashirozx (*Chinese Simplified*)
- Albatroz Jeremias (albjeremias) (*Portuguese*)
- Samir Tighzert (samir_t7) (*Kabyle*)
- Apple (blackteaovo) (*Chinese Simplified*)
- Nocta (*French*)
- OpenAlgeria (*Arabic*)
- tamaina (*Japanese*) - tamaina (*Japanese*)
- abidin toumi (Zet24) (*Arabic*) - Doug (*Portuguese, Brazilian*)
- xpac1985 (xpac) (*German*) - Matias Lavik (*Norwegian Nynorsk*)
- Kaede (kaedech) (*Japanese*)
- ÀŘǾŚ PÀŚĦÀÍ (arospashai) (*Sorani (Kurdish)*)
- Matias Lavik (matiaslavik) (*Norwegian Nynorsk*)
- smedvedev (*Russian*)
- mikel (mikelalas) (*Spanish*)
- Doug (douglasalvespe) (*Portuguese, Brazilian*)
- Trond Boksasp (boksasp) (*Norwegian*)
- Fleva (*Sardinian*) - Fleva (*Sardinian*)
- Mohammad Adnan Mahmood (adnanmig) (*Arabic*) - OpenAlgeria (*Arabic*)
- Sais Lakshmanan (Saislakshmanan) (*Tamil*) - koppe-pan (*Japanese*)
- Amith Raj Shetty (amithraj1989) (*Kannada*) - Amith Raj Shetty (*Kannada*)
- smedvedev (*Russian*)
- Trond Boksasp (*Norwegian*)
- random_person (*Spanish*) - random_person (*Spanish*)
- djoerd (*Dutch*) - Sais Lakshmanan (*Tamil*)
- Baban Abdulrahman (baban.abdulrehman) (*Sorani (Kurdish)*) - mikel (*Spanish*)
- ebrezhoneg (*Breton*) - Mohammad Adnan Mahmood (*Arabic*)
- dashty (*Sorani (Kurdish)*)
- Salh_haji6 (*Sorani (Kurdish)*)
- Amir Kurdo (kuraking202) (*Sorani (Kurdish)*)
- おさ (osapon) (*Japanese*)
- Ranj A Abdulqadir (RanjAhmed) (*Sorani (Kurdish)*)
- umonaca (*Chinese Simplified*)
- Bartek Fijałkowski (brateq) (*Polish*)
- tateisu (*Japanese*)
- centumix (*Japanese*)
- Jari Ronkainen (ronchaine) (*Finnish*)
- Savarín Electrográfico Marmota Intergalactica (herrero.maty) (*Spanish*)
- Torsten Högel (torstenhoegel) (*German*)
- Abijeet Patro (Abijeet) (*Basque*)
- Ács Zoltán (acszoltan111) (*Hungarian*)
- Benjamin Cobb (benjamincobb) (*German*)
- waweic (*German*)
- Aries (orlea) (*Japanese*)
- silverscat_3 (SilversCat) (*Japanese*)
- kavitha129 (*Tamil*)
- dcapillae (*Spanish*)
- SamOak (*Portuguese, Brazilian*)
- capiscuas (*Spanish*)
- NeverMine17 (*Russian*)
- Nithya Mary (nithyamary25) (*Tamil*)
- t_aus_m (*German*)
- dobrado (*Portuguese, Brazilian*)
- Hannah (Aniqueper1) (*Chinese Simplified*)
- Jiniux (*Italian*)
- 于晚霞 (xissshawww) (*Chinese Simplified*)

View File

@@ -5,6 +5,7 @@ libidn11
libidn11-dev libidn11-dev
libpq-dev libpq-dev
libprotobuf-dev libprotobuf-dev
libssl-dev
libxdamage1 libxdamage1
libxfixes3 libxfixes3
protobuf-compiler protobuf-compiler

View File

@@ -3,208 +3,6 @@ Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [3.3.0] - 2020-12-27
### Added
- **Add hotkeys for audio/video control in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/15158), [Gargron](https://github.com/tootsuite/mastodon/pull/15198))
- `Space` and `k` to toggle playback
- `m` to toggle mute
- `f` to toggle fullscreen
- `j` and `l` to go back and forward by 10 seconds
- `.` and `,` to go back and forward by a frame (video only)
- Add expand/compress button on media modal in web UI ([mashirozx](https://github.com/tootsuite/mastodon/pull/15068), [mashirozx](https://github.com/tootsuite/mastodon/pull/15088), [mashirozx](https://github.com/tootsuite/mastodon/pull/15094))
- Add border around 🕺 emoji in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14769))
- Add border around 🐞 emoji in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14712))
- Add home link to the getting started column when home isn't mounted ([ThibG](https://github.com/tootsuite/mastodon/pull/14707))
- Add option to disable swiping motions across the web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13885))
- **Add pop-out player for audio/video in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/14870), [Gargron](https://github.com/tootsuite/mastodon/pull/15157), [Gargron](https://github.com/tootsuite/mastodon/pull/14915), [noellabo](https://github.com/tootsuite/mastodon/pull/15309))
- Continue watching/listening when you scroll away
- Action bar to interact with/open toot from the pop-out player
- Add unread notification markers in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14818), [ThibG](https://github.com/tootsuite/mastodon/pull/14960), [ThibG](https://github.com/tootsuite/mastodon/pull/14954), [noellabo](https://github.com/tootsuite/mastodon/pull/14897), [noellabo](https://github.com/tootsuite/mastodon/pull/14907))
- Add paragraph about browser add-ons when encountering errors in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14801))
- Add import and export for bookmarks ([ThibG](https://github.com/tootsuite/mastodon/pull/14956))
- Add cache buster feature for media files ([Gargron](https://github.com/tootsuite/mastodon/pull/15155))
- If you have a proxy cache in front of object storage, deleted files will persist until the cache expires
- If enabled, cache buster will make a special request to the proxy to signal a cache reset
- Add duration option to the mute function ([aquarla](https://github.com/tootsuite/mastodon/pull/13831))
- Add replies policy option to the list function ([ThibG](https://github.com/tootsuite/mastodon/pull/9205), [trwnh](https://github.com/tootsuite/mastodon/pull/15304))
- Add `og:published_time` OpenGraph tags on toots ([nornagon](https://github.com/tootsuite/mastodon/pull/14865))
- **Add option to be notified when a followed user posts** ([Gargron](https://github.com/tootsuite/mastodon/pull/13546), [ThibG](https://github.com/tootsuite/mastodon/pull/14896), [Gargron](https://github.com/tootsuite/mastodon/pull/14822))
- If you don't want to miss a toot, click the bell button!
- Add client-side validation in password change forms ([ThibG](https://github.com/tootsuite/mastodon/pull/14564))
- Add client-side validation in the registration form ([ThibG](https://github.com/tootsuite/mastodon/pull/14560), [ThibG](https://github.com/tootsuite/mastodon/pull/14599))
- Add support for Gemini URLs ([joshleeb](https://github.com/tootsuite/mastodon/pull/15013))
- Add app shortcuts to web app manifest ([mkljczk](https://github.com/tootsuite/mastodon/pull/15234))
- Add WebAuthn as an alternative 2FA method ([santiagorodriguez96](https://github.com/tootsuite/mastodon/pull/14466), [jiikko](https://github.com/tootsuite/mastodon/pull/14806))
- Add honeypot fields and minimum fill-out time for sign-up form ([ThibG](https://github.com/tootsuite/mastodon/pull/15276))
- Add icon for mutual relationships in relationship manager ([noellabo](https://github.com/tootsuite/mastodon/pull/15149))
- Add follow selected followers button in relationship manager ([noellabo](https://github.com/tootsuite/mastodon/pull/15148))
- **Add subresource integrity for JS and CSS assets** ([Gargron](https://github.com/tootsuite/mastodon/pull/15096))
- If you use a CDN for static assets (JavaScript, CSS, and so on), you have to trust that the CDN does not modify the assets maliciously
- Subresource integrity compares server-generated asset digests with what's actually served from the CDN and prevents such attacks
- Add `ku`, `sa`, `sc`, `zgh` to available locales ([ykzts](https://github.com/tootsuite/mastodon/pull/15138))
- Add ability to force an account to mark media as sensitive ([noellabo](https://github.com/tootsuite/mastodon/pull/14361))
- **Add ability to block access or limit sign-ups from chosen IPs** ([Gargron](https://github.com/tootsuite/mastodon/pull/14963), [ThibG](https://github.com/tootsuite/mastodon/pull/15263))
- Add rules for IPs or CIDR ranges that automatically expire after a configurable amount of time
- Choose the severity of the rule, either blocking all access or merely limiting sign-ups
- **Add support for reversible suspensions through ActivityPub** ([Gargron](https://github.com/tootsuite/mastodon/pull/14989))
- Servers can signal that one of their accounts has been suspended
- During suspension, the account can only delete its own content
- A reversal of the suspension can be signalled the same way
- A local suspension always overrides a remote one
- Add indication to admin UI of whether a report has been forwarded ([ThibG](https://github.com/tootsuite/mastodon/pull/13237))
- Add display of reasons for joining of an account in admin UI ([mashirozx](https://github.com/tootsuite/mastodon/pull/15265))
- Add option to obfuscate domain name in public list of domain blocks ([Gargron](https://github.com/tootsuite/mastodon/pull/15355))
- Add option to make reasons for joining required on sign-up ([ThibG](https://github.com/tootsuite/mastodon/pull/15326), [ThibG](https://github.com/tootsuite/mastodon/pull/15358), [ThibG](https://github.com/tootsuite/mastodon/pull/15385), [ThibG](https://github.com/tootsuite/mastodon/pull/15405))
- Add ActivityPub follower synchronization mechanism ([ThibG](https://github.com/tootsuite/mastodon/pull/14510), [ThibG](https://github.com/tootsuite/mastodon/pull/15026))
- Add outbox attribute to instance actor ([ThibG](https://github.com/tootsuite/mastodon/pull/14721))
- Add featured hashtags as an ActivityPub collection ([Gargron](https://github.com/tootsuite/mastodon/pull/11595), [noellabo](https://github.com/tootsuite/mastodon/pull/15277))
- Add support for dereferencing objects through bearcaps ([Gargron](https://github.com/tootsuite/mastodon/pull/14683), [noellabo](https://github.com/tootsuite/mastodon/pull/14981))
- Add `S3_READ_TIMEOUT` environment variable ([tateisu](https://github.com/tootsuite/mastodon/pull/14952))
- Add `ALLOWED_PRIVATE_ADDRESSES` environment variable ([ThibG](https://github.com/tootsuite/mastodon/pull/14722))
- Add `--fix-permissions` option to `tootctl media remove-orphans` ([Gargron](https://github.com/tootsuite/mastodon/pull/14383), [uist1idrju3i](https://github.com/tootsuite/mastodon/pull/14715))
- Add `tootctl accounts merge` ([Gargron](https://github.com/tootsuite/mastodon/pull/15201), [ThibG](https://github.com/tootsuite/mastodon/pull/15264), [ThibG](https://github.com/tootsuite/mastodon/pull/15256))
- Has someone changed their domain or subdomain thereby creating two accounts where there should be one?
- This command will fix it on your end
- Add `tootctl maintenance fix-duplicates` ([ThibG](https://github.com/tootsuite/mastodon/pull/14860), [Gargron](https://github.com/tootsuite/mastodon/pull/15223), [ThibG](https://github.com/tootsuite/mastodon/pull/15373))
- Index corruption in the database?
- This command is for you
- **Add support for managing multiple stream subscriptions in a single connection** ([Gargron](https://github.com/tootsuite/mastodon/pull/14524), [Gargron](https://github.com/tootsuite/mastodon/pull/14566), [mfmfuyu](https://github.com/tootsuite/mastodon/pull/14859), [zunda](https://github.com/tootsuite/mastodon/pull/14608))
- Previously, getting live updates for multiple timelines required opening a HTTP or WebSocket connection for each
- More connections means more resource consumption on both ends, not to mention the (ever so slight) delay when establishing a new connection
- Now, with just a single WebSocket connection you can subscribe and unsubscribe to and from multiple streams
- Add support for limiting results by both `min_id` and `max_id` at the same time in REST API ([tateisu](https://github.com/tootsuite/mastodon/pull/14776))
- Add `GET /api/v1/accounts/:id/featured_tags` to REST API ([noellabo](https://github.com/tootsuite/mastodon/pull/11817), [noellabo](https://github.com/tootsuite/mastodon/pull/15270))
- Add stoplight for object storage failures, return HTTP 503 in REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/13043))
- Add optional `tootctl remove media` cronjob in Helm chart ([dunn](https://github.com/tootsuite/mastodon/pull/14396))
- Add clean error message when `RAILS_ENV` is unset ([ThibG](https://github.com/tootsuite/mastodon/pull/15381))
### Changed
- **Change media modals look in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/15217), [Gargron](https://github.com/tootsuite/mastodon/pull/15221), [Gargron](https://github.com/tootsuite/mastodon/pull/15284), [Gargron](https://github.com/tootsuite/mastodon/pull/15283), [Kjwon15](https://github.com/tootsuite/mastodon/pull/15308), [noellabo](https://github.com/tootsuite/mastodon/pull/15305), [ThibG](https://github.com/tootsuite/mastodon/pull/15417))
- Background of the overlay matches the color of the image
- Action bar to interact with or open the toot from the modal
- Change order of announcements in admin UI to be newest-first ([ThibG](https://github.com/tootsuite/mastodon/pull/15091))
- **Change account suspensions to be reversible by default** ([Gargron](https://github.com/tootsuite/mastodon/pull/14726), [ThibG](https://github.com/tootsuite/mastodon/pull/15152), [ThibG](https://github.com/tootsuite/mastodon/pull/15106), [ThibG](https://github.com/tootsuite/mastodon/pull/15100), [ThibG](https://github.com/tootsuite/mastodon/pull/15099), [noellabo](https://github.com/tootsuite/mastodon/pull/14855), [ThibG](https://github.com/tootsuite/mastodon/pull/15380), [Gargron](https://github.com/tootsuite/mastodon/pull/15420), [Gargron](https://github.com/tootsuite/mastodon/pull/15414))
- Suspensions no longer equal deletions
- A suspended account can be unsuspended with minimal consequences for 30 days
- Immediate deletion of data is still available as an explicit option
- Suspended accounts can request an archive of their data through the UI
- Change REST API to return empty data for suspended accounts (14765)
- Change web UI to show empty profile for suspended accounts ([Gargron](https://github.com/tootsuite/mastodon/pull/14766), [Gargron](https://github.com/tootsuite/mastodon/pull/15345))
- Change featured hashtag suggestions to be recently used instead of most used ([abcang](https://github.com/tootsuite/mastodon/pull/14760))
- Change direct toots to appear in the home feed again ([Gargron](https://github.com/tootsuite/mastodon/pull/14711), [ThibG](https://github.com/tootsuite/mastodon/pull/15182), [noellabo](https://github.com/tootsuite/mastodon/pull/14727))
- Return to treating all toots the same instead of trying to retrofit direct visibility into an instant messaging model
- Change email address validation to return more specific errors ([ThibG](https://github.com/tootsuite/mastodon/pull/14565))
- Change HTTP signature requirements to include `Digest` header on `POST` requests ([ThibG](https://github.com/tootsuite/mastodon/pull/15069))
- Change click area of video/audio player buttons to be bigger in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/15049))
- Change order of filters by alphabetic by "keyword or phrase" ([ariasuni](https://github.com/tootsuite/mastodon/pull/15050))
- Change suspension of remote accounts to also undo outgoing follows ([ThibG](https://github.com/tootsuite/mastodon/pull/15188))
- Change string "Home" to "Home and lists" in the filter creation screen ([ariasuni](https://github.com/tootsuite/mastodon/pull/15139))
- Change string "Boost to original audience" to "Boost with original visibility" in web UI ([3n-k1](https://github.com/tootsuite/mastodon/pull/14598))
- Change string "Show more" to "Show newer" and "Show older" on public pages ([ariasuni](https://github.com/tootsuite/mastodon/pull/15052))
- Change order of announcements to be reverse chronological in web UI ([dariusk](https://github.com/tootsuite/mastodon/pull/15065), [dariusk](https://github.com/tootsuite/mastodon/pull/15070))
- Change RTL detection to rely on unicode-bidi paragraph by paragraph in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14573))
- Change visibility icon next to timestamp to be clickable in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/15053), [mayaeh](https://github.com/tootsuite/mastodon/pull/15055))
- Change public thread view to hide "Show thread" link ([ThibG](https://github.com/tootsuite/mastodon/pull/15266))
- Change number format on about page from full to shortened ([Gargron](https://github.com/tootsuite/mastodon/pull/15327))
- Change how scheduled tasks run in multi-process environments ([noellabo](https://github.com/tootsuite/mastodon/pull/15314))
- New dedicated queue `scheduler`
- Runs by default when Sidekiq is executed with no options
- Has to be added manually in a multi-process environment
### Removed
- Remove fade-in animation from modals in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15199))
- Remove auto-redirect to direct messages in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15142))
- Remove obsolete IndexedDB operations from web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14730))
- Remove dependency on unused and unmaintained http_parser.rb gem ([ThibG](https://github.com/tootsuite/mastodon/pull/14574))
### Fixed
- Fix layout on about page when contact account has a long username ([ThibG](https://github.com/tootsuite/mastodon/pull/15357))
- Fix follow limit preventing re-following of a moved account ([Gargron](https://github.com/tootsuite/mastodon/pull/14207), [ThibG](https://github.com/tootsuite/mastodon/pull/15384))
- **Fix deletes not reaching every server that interacted with toot** ([Gargron](https://github.com/tootsuite/mastodon/pull/15200))
- Previously, delete of a toot would be primarily sent to the followers of its author, people mentioned in the toot, and people who reblogged the toot
- Now, additionally, it is ensured that it is sent to people who replied to it, favourited it, and to the person it replies to even if that person is not mentioned
- Fix resolving an account through its non-canonical form (i.e. alternate domain) ([ThibG](https://github.com/tootsuite/mastodon/pull/15187))
- Fix sending redundant ActivityPub events when processing remote account deletion ([ThibG](https://github.com/tootsuite/mastodon/pull/15104))
- Fix Move handler not being triggered when failing to fetch target account ([ThibG](https://github.com/tootsuite/mastodon/pull/15107))
- Fix downloading remote media files when server returns empty filename ([ThibG](https://github.com/tootsuite/mastodon/pull/14867))
- Fix account processing failing because of large collections ([ThibG](https://github.com/tootsuite/mastodon/pull/15027))
- Fix not being able to unfavorite toots one has lost access to ([ThibG](https://github.com/tootsuite/mastodon/pull/15192))
- Fix not being able to unbookmark toots one has lost access to ([ThibG](https://github.com/tootsuite/mastodon/pull/14604))
- Fix possible casing inconsistencies in hashtag search ([ThibG](https://github.com/tootsuite/mastodon/pull/14906))
- Fix updating account counters when association is not yet created ([Gargron](https://github.com/tootsuite/mastodon/pull/15108))
- Fix cookies not having a SameSite attribute ([Gargron](https://github.com/tootsuite/mastodon/pull/15098))
- Fix poll ending notifications being created for each vote ([ThibG](https://github.com/tootsuite/mastodon/pull/15071))
- Fix multiple boosts of a same toot erroneously appearing in TL ([ThibG](https://github.com/tootsuite/mastodon/pull/14759))
- Fix asset builds not picking up `CDN_HOST` change ([ThibG](https://github.com/tootsuite/mastodon/pull/14381))
- Fix desktop notifications permission prompt in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14985), [Gargron](https://github.com/tootsuite/mastodon/pull/15141), [ThibG](https://github.com/tootsuite/mastodon/pull/13543), [ThibG](https://github.com/tootsuite/mastodon/pull/15176))
- Some time ago, browsers added a requirement that desktop notification prompts could only be displayed in response to a user-generated event (such as a click)
- This means that for some time, users who haven't already given the permission before were not getting a prompt and as such were not receiving desktop notifications
- Fix "Mark media as sensitive" string not supporting pluralizations in other languages in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/15051))
- Fix glitched image uploads when canvas read access is blocked in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15180))
- Fix some account gallery items having empty labels in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15073))
- Fix alt-key hotkeys activating while typing in a text field in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14942))
- Fix wrong seek bar width on media player in web UI ([mfmfuyu](https://github.com/tootsuite/mastodon/pull/15060))
- Fix logging out on mobile in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14901))
- Fix wrong click area for GIFVs in media modal in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/14615))
- Fix unreadable placeholder text color in high contrast theme in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14803))
- Fix scrolling issues when closing some dropdown menus in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14606))
- Fix notification filter bar incorrectly filtering gaps in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14808))
- Fix disabled boost icon being replaced by private boost icon on hover in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14456))
- Fix hashtag detection in compose form being different to server-side in web UI ([kedamaDQ](https://github.com/tootsuite/mastodon/pull/14484), [ThibG](https://github.com/tootsuite/mastodon/pull/14513))
- Fix home last read marker mishandling gaps in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14809))
- Fix unnecessary re-rendering of various components when typing in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15286))
- Fix notifications being unnecessarily re-rendered in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15312))
- Fix column swiping animation logic in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15301))
- Fix inefficiency when fetching hashtag timeline ([noellabo](https://github.com/tootsuite/mastodon/pull/14861), [akihikodaki](https://github.com/tootsuite/mastodon/pull/14662))
- Fix inefficiency when fetching bookmarks ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14674))
- Fix inefficiency when fetching favourites ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14673))
- Fix inefficiency when fetching media-only account timeline ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14675))
- Fix inefficieny when deleting accounts ([Gargron](https://github.com/tootsuite/mastodon/pull/15387), [ThibG](https://github.com/tootsuite/mastodon/pull/15409), [ThibG](https://github.com/tootsuite/mastodon/pull/15407), [ThibG](https://github.com/tootsuite/mastodon/pull/15408), [ThibG](https://github.com/tootsuite/mastodon/pull/15402), [ThibG](https://github.com/tootsuite/mastodon/pull/15416), [Gargron](https://github.com/tootsuite/mastodon/pull/15421))
- Fix redundant query when processing batch actions on custom emojis ([niwatori24](https://github.com/tootsuite/mastodon/pull/14534))
- Fix slow distinct queries where grouped queries are faster ([Gargron](https://github.com/tootsuite/mastodon/pull/15287))
- Fix performance on instances list in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15282))
- Fix server actor appearing in list of accounts in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14567))
- Fix "bootstrap timeline accounts" toggle in site settings in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15325))
- Fix PostgreSQL secret name for cronjob in Helm chart ([metal3d](https://github.com/tootsuite/mastodon/pull/15072))
- Fix Procfile not being compatible with herokuish ([acuteaura](https://github.com/tootsuite/mastodon/pull/12685))
- Fix installation of tini being split into multiple steps in Dockerfile ([ryncsn](https://github.com/tootsuite/mastodon/pull/14686))
### Security
- Fix streaming API allowing connections to persist after access token invalidation ([Gargron](https://github.com/tootsuite/mastodon/pull/15111))
- Fix 2FA/sign-in token sessions being valid after password change ([Gargron](https://github.com/tootsuite/mastodon/pull/14802))
- Fix resolving accounts sometimes creating duplicate records for a given ActivityPub identifier ([ThibG](https://github.com/tootsuite/mastodon/pull/15364))
## [3.2.2] - 2020-12-19
### Added
- Add `tootctl maintenance fix-duplicates` ([ThibG](https://github.com/tootsuite/mastodon/pull/14860), [Gargron](https://github.com/tootsuite/mastodon/pull/15223))
- Index corruption in the database?
- This command is for you
### Removed
- Remove dependency on unused and unmaintained http_parser.rb gem ([ThibG](https://github.com/tootsuite/mastodon/pull/14574))
### Fixed
- Fix Move handler not being triggered when failing to fetch target account ([ThibG](https://github.com/tootsuite/mastodon/pull/15107))
- Fix downloading remote media files when server returns empty filename ([ThibG](https://github.com/tootsuite/mastodon/pull/14867))
- Fix possible casing inconsistencies in hashtag search ([ThibG](https://github.com/tootsuite/mastodon/pull/14906))
- Fix updating account counters when association is not yet created ([Gargron](https://github.com/tootsuite/mastodon/pull/15108))
- Fix account processing failing because of large collections ([ThibG](https://github.com/tootsuite/mastodon/pull/15027))
- Fix resolving an account through its non-canonical form (i.e. alternate domain) ([ThibG](https://github.com/tootsuite/mastodon/pull/15187))
- Fix slow distinct queries where grouped queries are faster ([Gargron](https://github.com/tootsuite/mastodon/pull/15287))
### Security
- Fix 2FA/sign-in token sessions being valid after password change ([Gargron](https://github.com/tootsuite/mastodon/pull/14802))
- Fix resolving accounts sometimes creating duplicate records for a given ActivityPub identifier ([ThibG](https://github.com/tootsuite/mastodon/pull/15364))
## [3.2.1] - 2020-10-19 ## [3.2.1] - 2020-10-19
### Added ### Added
@@ -387,14 +185,14 @@ All notable changes to this project will be documented in this file.
- Only then proceed to start removing their data (slow) - Only then proceed to start removing their data (slow)
- Clear out media attachments in a separate worker (slow) - Clear out media attachments in a separate worker (slow)
## [3.1.5] - 2020-07-07 ## [v3.1.5] - 2020-07-07
### Security ### Security
- Fix media attachment enumeration ([ThibG](https://github.com/tootsuite/mastodon/pull/14254)) - Fix media attachment enumeration ([ThibG](https://github.com/tootsuite/mastodon/pull/14254))
- Change rate limits for various paths ([Gargron](https://github.com/tootsuite/mastodon/pull/14253)) - Change rate limits for various paths ([Gargron](https://github.com/tootsuite/mastodon/pull/14253))
- Fix other sessions not being logged out on password change ([Gargron](https://github.com/tootsuite/mastodon/pull/14252)) - Fix other sessions not being logged out on password change ([Gargron](https://github.com/tootsuite/mastodon/pull/14252))
## [3.1.4] - 2020-05-14 ## [v3.1.4] - 2020-05-14
### Added ### Added
- Add `vi` to available locales ([taicv](https://github.com/tootsuite/mastodon/pull/13542)) - Add `vi` to available locales ([taicv](https://github.com/tootsuite/mastodon/pull/13542))
@@ -461,7 +259,7 @@ All notable changes to this project will be documented in this file.
- For apps that self-register on behalf of every individual user (such as most mobile apps), this is a non-issue - For apps that self-register on behalf of every individual user (such as most mobile apps), this is a non-issue
- The issue only affects developers of apps who are shared between multiple users, such as server-side apps like cross-posters - The issue only affects developers of apps who are shared between multiple users, such as server-side apps like cross-posters
## [3.1.3] - 2020-04-05 ## [v3.1.3] - 2020-04-05
### Added ### Added
- Add ability to filter audit log in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/13381)) - Add ability to filter audit log in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/13381))

View File

@@ -4,7 +4,7 @@ FROM ubuntu:20.04 as build-dep
SHELL ["bash", "-c"] SHELL ["bash", "-c"]
# Install Node v12 (LTS) # Install Node v12 (LTS)
ENV NODE_VER="12.20.0" ENV NODE_VER="12.16.3"
RUN ARCH= && \ RUN ARCH= && \
dpkgArch="$(dpkg --print-architecture)" && \ dpkgArch="$(dpkg --print-architecture)" && \
case "${dpkgArch##*-}" in \ case "${dpkgArch##*-}" in \
@@ -36,11 +36,10 @@ RUN apt update && \
./autogen.sh && \ ./autogen.sh && \
./configure --prefix=/opt/jemalloc && \ ./configure --prefix=/opt/jemalloc && \
make -j$(nproc) > /dev/null && \ make -j$(nproc) > /dev/null && \
make install_bin install_include install_lib && \ make install_bin install_include install_lib
cd .. && rm -rf jemalloc-$JE_VER $JE_VER.tar.gz
# Install Ruby # Install Ruby
ENV RUBY_VER="2.7.2" ENV RUBY_VER="2.6.6"
ENV CPPFLAGS="-I/opt/jemalloc/include" ENV CPPFLAGS="-I/opt/jemalloc/include"
ENV LDFLAGS="-L/opt/jemalloc/lib/" ENV LDFLAGS="-L/opt/jemalloc/lib/"
RUN apt update && \ RUN apt update && \
@@ -57,8 +56,7 @@ RUN apt update && \
--disable-install-doc && \ --disable-install-doc && \
ln -s /opt/jemalloc/lib/* /usr/lib/ && \ ln -s /opt/jemalloc/lib/* /usr/lib/ && \
make -j$(nproc) > /dev/null && \ make -j$(nproc) > /dev/null && \
make install && \ make install
cd .. && rm -rf ruby-$RUBY_VER.tar.gz ruby-$RUBY_VER
ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin" ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin"
@@ -109,14 +107,11 @@ RUN apt -y --no-install-recommends install \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
# Add tini # Add tini
ENV TINI_VERSION="0.19.0" ENV TINI_VERSION="0.18.0"
RUN dpkgArch="$(dpkg --print-architecture)" && \ ENV TINI_SUM="12d20136605531b09a2c2dac02ccee85e1b874eb322ef6baf7561cd93f93c855"
ARCH=$dpkgArch && \ ADD https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini /tini
wget https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-$ARCH \ RUN echo "$TINI_SUM tini" | sha256sum -c -
https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-$ARCH.sha256sum && \ RUN chmod +x /tini
cat tini-$ARCH.sha256sum | sha256sum -c - && \
mv tini-$ARCH /tini && rm tini-$ARCH.sha256sum && \
chmod +x /tini
# Copy over mastodon source, and dependencies from building, and set permissions # Copy over mastodon source, and dependencies from building, and set permissions
COPY --chown=mastodon:mastodon . /opt/mastodon COPY --chown=mastodon:mastodon . /opt/mastodon

62
Gemfile
View File

@@ -5,19 +5,22 @@ ruby '>= 2.5.0', '< 3.0.0'
gem 'pkg-config', '~> 1.4' gem 'pkg-config', '~> 1.4'
gem 'puma', '~> 5.0' gem 'puma', '~> 4.3'
gem 'rails', '~> 5.2.4.4' gem 'rails', '~> 5.2.4.3'
gem 'sprockets', '~> 3.7.2' gem 'sprockets', '~> 3.7.2'
gem 'thor', '~> 1.0' gem 'thor', '~> 0.20'
gem 'rack', '~> 2.2.3' gem 'rack', '~> 2.2.3'
gem 'thwait', '~> 0.1.0'
gem 'e2mmap', '~> 0.1.0'
gem 'hamlit-rails', '~> 0.2' gem 'hamlit-rails', '~> 0.2'
gem 'pg', '~> 1.2' gem 'pg', '~> 1.2'
gem 'makara', '~> 0.4' gem 'makara', '~> 0.4'
gem 'pghero', '~> 2.7' gem 'pghero', '~> 2.5'
gem 'dotenv-rails', '~> 2.7' gem 'dotenv-rails', '~> 2.7'
gem 'aws-sdk-s3', '~> 1.85', require: false gem 'aws-sdk-s3', '~> 1.73', require: false
gem 'fog-core', '<= 2.1.0' gem 'fog-core', '<= 2.1.0'
gem 'fog-openstack', '~> 0.3', require: false gem 'fog-openstack', '~> 0.3', require: false
gem 'paperclip', '~> 6.0' gem 'paperclip', '~> 6.0'
@@ -27,7 +30,7 @@ gem 'blurhash', '~> 0.1'
gem 'active_model_serializers', '~> 0.10' gem 'active_model_serializers', '~> 0.10'
gem 'addressable', '~> 2.7' gem 'addressable', '~> 2.7'
gem 'bootsnap', '~> 1.5', require: false gem 'bootsnap', '~> 1.4', require: false
gem 'browser' gem 'browser'
gem 'charlock_holmes', '~> 0.7.7' gem 'charlock_holmes', '~> 0.7.7'
gem 'iso-639' gem 'iso-639'
@@ -41,10 +44,9 @@ group :pam_authentication, optional: true do
end end
gem 'net-ldap', '~> 0.16' gem 'net-ldap', '~> 0.16'
gem 'omniauth-cas', '~> 2.0' gem 'omniauth-cas', '~> 1.1'
gem 'omniauth-saml', '~> 1.10' gem 'omniauth-saml', '~> 1.10'
gem 'omniauth', '~> 1.9' gem 'omniauth', '~> 1.9'
gem 'omniauth-rails_csrf_protection', '~> 0.1'
gem 'color_diff', '~> 0.1' gem 'color_diff', '~> 0.1'
gem 'discard', '~> 1.2' gem 'discard', '~> 1.2'
@@ -53,11 +55,12 @@ gem 'ed25519', '~> 1.2'
gem 'fast_blank', '~> 1.0' gem 'fast_blank', '~> 1.0'
gem 'fastimage' gem 'fastimage'
gem 'hiredis', '~> 0.6' gem 'hiredis', '~> 0.6'
gem 'redis-namespace', '~> 1.8' gem 'redis-namespace', '~> 1.7'
gem 'health_check', git: 'https://github.com/ianheggie/health_check', ref: '0b799ead604f900ed50685e9b2d469cd2befba5b' gem 'health_check', git: 'https://github.com/ianheggie/health_check', ref: '0b799ead604f900ed50685e9b2d469cd2befba5b'
gem 'htmlentities', '~> 4.3' gem 'htmlentities', '~> 4.3'
gem 'http', '~> 4.4' gem 'http', '~> 4.4'
gem 'http_accept_language', '~> 2.1' gem 'http_accept_language', '~> 2.1'
gem 'http_parser.rb', '~> 0.6', git: 'https://github.com/tmm1/http_parser.rb', ref: '54b17ba8c7d8d20a16dfc65d1775241833219cf2', submodules: true
gem 'httplog', '~> 1.4.3' gem 'httplog', '~> 1.4.3'
gem 'idn-ruby', require: 'idn' gem 'idn-ruby', require: 'idn'
gem 'kaminari', '~> 1.2' gem 'kaminari', '~> 1.2'
@@ -69,8 +72,8 @@ gem 'nsa', '~> 0.2'
gem 'oj', '~> 3.10' gem 'oj', '~> 3.10'
gem 'ox', '~> 2.13' gem 'ox', '~> 2.13'
gem 'parslet' gem 'parslet'
gem 'parallel', '~> 1.20' gem 'parallel', '~> 1.19'
gem 'posix-spawn' gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c'
gem 'pundit', '~> 2.1' gem 'pundit', '~> 2.1'
gem 'premailer-rails' gem 'premailer-rails'
gem 'rack-attack', '~> 6.3' gem 'rack-attack', '~> 6.3'
@@ -82,22 +85,20 @@ gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
gem 'rqrcode', '~> 1.1' gem 'rqrcode', '~> 1.1'
gem 'ruby-progressbar', '~> 1.10' gem 'ruby-progressbar', '~> 1.10'
gem 'sanitize', '~> 5.2' gem 'sanitize', '~> 5.2'
gem 'scenic', '~> 1.5' gem 'sidekiq', '~> 6.0'
gem 'sidekiq', '~> 6.1'
gem 'sidekiq-scheduler', '~> 3.0' gem 'sidekiq-scheduler', '~> 3.0'
gem 'sidekiq-unique-jobs', '~> 6.0' gem 'sidekiq-unique-jobs', '~> 6.0'
gem 'sidekiq-bulk', '~>0.2.0' gem 'sidekiq-bulk', '~>0.2.0'
gem 'simple-navigation', '~> 4.1' gem 'simple-navigation', '~> 4.1'
gem 'simple_form', '~> 5.0' gem 'simple_form', '~> 5.0'
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie' gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
gem 'stoplight', '~> 2.2.1' gem 'stoplight', '~> 2.2.0'
gem 'strong_migrations', '~> 0.7' gem 'strong_migrations', '~> 0.6'
gem 'tty-prompt', '~> 0.22', require: false gem 'tty-prompt', '~> 0.21', require: false
gem 'twitter-text', '~> 1.14' gem 'twitter-text', '~> 1.14'
gem 'tzinfo-data', '~> 1.2020' gem 'tzinfo-data', '~> 1.2020'
gem 'webpacker', '~> 5.2' gem 'webpacker', '~> 5.1'
gem 'webpush' gem 'webpush'
gem 'webauthn', '~> 3.0.0.alpha1'
gem 'json-ld' gem 'json-ld'
gem 'json-ld-preloaded', '~> 3.1' gem 'json-ld-preloaded', '~> 3.1'
@@ -119,33 +120,33 @@ end
group :test do group :test do
gem 'capybara', '~> 3.33' gem 'capybara', '~> 3.33'
gem 'climate_control', '~> 0.2' gem 'climate_control', '~> 0.2'
gem 'faker', '~> 2.14' gem 'faker', '~> 2.13'
gem 'microformats', '~> 4.2' gem 'microformats', '~> 4.2'
gem 'rails-controller-testing', '~> 1.0' gem 'rails-controller-testing', '~> 1.0'
gem 'rspec-sidekiq', '~> 3.1' gem 'rspec-sidekiq', '~> 3.1'
gem 'simplecov', '~> 0.19', require: false gem 'simplecov', '~> 0.18', require: false
gem 'webmock', '~> 3.10' gem 'webmock', '~> 3.8'
gem 'parallel_tests', '~> 3.4' gem 'parallel_tests', '~> 3.0'
gem 'rspec_junit_formatter', '~> 0.4' gem 'rspec_junit_formatter', '~> 0.4'
end end
group :development do group :development do
gem 'active_record_query_trace', '~> 1.8' gem 'active_record_query_trace', '~> 1.7'
gem 'annotate', '~> 3.1' gem 'annotate', '~> 3.1'
gem 'better_errors', '~> 2.9' gem 'better_errors', '~> 2.7'
gem 'binding_of_caller', '~> 0.7' gem 'binding_of_caller', '~> 0.7'
gem 'bullet', '~> 6.1' gem 'bullet', '~> 6.1'
gem 'letter_opener', '~> 1.7' gem 'letter_opener', '~> 1.7'
gem 'letter_opener_web', '~> 1.4' gem 'letter_opener_web', '~> 1.4'
gem 'memory_profiler' gem 'memory_profiler'
gem 'rubocop', '~> 1.3', require: false gem 'rubocop', '~> 0.86', require: false
gem 'rubocop-rails', '~> 2.8', require: false gem 'rubocop-rails', '~> 2.6', require: false
gem 'brakeman', '~> 4.10', require: false gem 'brakeman', '~> 4.8', require: false
gem 'bundler-audit', '~> 0.7', require: false gem 'bundler-audit', '~> 0.7', require: false
gem 'capistrano', '~> 3.14' gem 'capistrano', '~> 3.14'
gem 'capistrano-rails', '~> 1.6' gem 'capistrano-rails', '~> 1.5'
gem 'capistrano-rbenv', '~> 2.2' gem 'capistrano-rbenv', '~> 2.1'
gem 'capistrano-yarn', '~> 2.0' gem 'capistrano-yarn', '~> 2.0'
gem 'stackprof' gem 'stackprof'
@@ -158,6 +159,3 @@ end
gem 'concurrent-ruby', require: false gem 'concurrent-ruby', require: false
gem 'connection_pool', require: false gem 'connection_pool', require: false
gem 'xorcist', '~> 1.1'
gem 'pluck_each', '~> 0.1.3'

View File

@@ -6,6 +6,21 @@ GIT
health_check (4.0.0.pre) health_check (4.0.0.pre)
rails (>= 4.0) rails (>= 4.0)
GIT
remote: https://github.com/rtomayko/posix-spawn
revision: 58465d2e213991f8afb13b984854a49fcdcc980c
ref: 58465d2e213991f8afb13b984854a49fcdcc980c
specs:
posix-spawn (0.3.13)
GIT
remote: https://github.com/tmm1/http_parser.rb
revision: 54b17ba8c7d8d20a16dfc65d1775241833219cf2
ref: 54b17ba8c7d8d20a16dfc65d1775241833219cf2
submodules: true
specs:
http_parser.rb (0.6.1)
GIT GIT
remote: https://github.com/witgo/nilsimsa remote: https://github.com/witgo/nilsimsa
revision: fd184883048b922b176939f851338d0a4971a532 revision: fd184883048b922b176939f851338d0a4971a532
@@ -16,25 +31,25 @@ GIT
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (5.2.4.4) actioncable (5.2.4.3)
actionpack (= 5.2.4.4) actionpack (= 5.2.4.3)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
actionmailer (5.2.4.4) actionmailer (5.2.4.3)
actionpack (= 5.2.4.4) actionpack (= 5.2.4.3)
actionview (= 5.2.4.4) actionview (= 5.2.4.3)
activejob (= 5.2.4.4) activejob (= 5.2.4.3)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (5.2.4.4) actionpack (5.2.4.3)
actionview (= 5.2.4.4) actionview (= 5.2.4.3)
activesupport (= 5.2.4.4) activesupport (= 5.2.4.3)
rack (~> 2.0, >= 2.0.8) rack (~> 2.0, >= 2.0.8)
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.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.2.4.4) actionview (5.2.4.3)
activesupport (= 5.2.4.4) activesupport (= 5.2.4.3)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
@@ -44,21 +59,21 @@ GEM
activemodel (>= 4.1, < 6.1) activemodel (>= 4.1, < 6.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)
active_record_query_trace (1.8) active_record_query_trace (1.7)
activejob (5.2.4.4) activejob (5.2.4.3)
activesupport (= 5.2.4.4) activesupport (= 5.2.4.3)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (5.2.4.4) activemodel (5.2.4.3)
activesupport (= 5.2.4.4) activesupport (= 5.2.4.3)
activerecord (5.2.4.4) activerecord (5.2.4.3)
activemodel (= 5.2.4.4) activemodel (= 5.2.4.3)
activesupport (= 5.2.4.4) activesupport (= 5.2.4.3)
arel (>= 9.0) arel (>= 9.0)
activestorage (5.2.4.4) activestorage (5.2.4.3)
actionpack (= 5.2.4.4) actionpack (= 5.2.4.3)
activerecord (= 5.2.4.4) activerecord (= 5.2.4.3)
marcel (~> 0.3.1) marcel (~> 0.3.1)
activesupport (5.2.4.4) activesupport (5.2.4.3)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
minitest (~> 5.1) minitest (~> 5.1)
@@ -67,7 +82,6 @@ GEM
public_suffix (>= 2.0.2, < 5.0) public_suffix (>= 2.0.2, < 5.0)
airbrussh (1.4.0) airbrussh (1.4.0)
sshkit (>= 1.6.1, != 1.7.0) sshkit (>= 1.6.1, != 1.7.0)
android_key_attestation (0.3.0)
annotate (3.1.1) annotate (3.1.1)
activerecord (>= 3.2, < 7.0) activerecord (>= 3.2, < 7.0)
rake (>= 10.4, < 14.0) rake (>= 10.4, < 14.0)
@@ -77,36 +91,34 @@ GEM
encryptor (~> 3.0.0) encryptor (~> 3.0.0)
av (0.9.0) av (0.9.0)
cocaine (~> 0.5.3) cocaine (~> 0.5.3)
awrence (1.1.1)
aws-eventstream (1.1.0) aws-eventstream (1.1.0)
aws-partitions (1.397.0) aws-partitions (1.338.0)
aws-sdk-core (3.109.3) aws-sdk-core (3.103.0)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0) aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
jmespath (~> 1.0) jmespath (~> 1.0)
aws-sdk-kms (1.39.0) aws-sdk-kms (1.36.0)
aws-sdk-core (~> 3, >= 3.109.0) aws-sdk-core (~> 3, >= 3.99.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.85.0) aws-sdk-s3 (1.73.0)
aws-sdk-core (~> 3, >= 3.109.0) aws-sdk-core (~> 3, >= 3.102.1)
aws-sdk-kms (~> 1) aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sigv4 (1.2.2) aws-sigv4 (1.2.1)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
bcrypt (3.1.16) bcrypt (3.1.13)
better_errors (2.9.1) better_errors (2.7.1)
coderay (>= 1.0.0) coderay (>= 1.0.0)
erubi (>= 1.0.0) erubi (>= 1.0.0)
rack (>= 0.9.0) rack (>= 0.9.0)
bindata (2.4.8)
binding_of_caller (0.8.0) binding_of_caller (0.8.0)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
blurhash (0.1.4) blurhash (0.1.4)
ffi (~> 1.10.0) ffi (~> 1.10.0)
bootsnap (1.5.1) bootsnap (1.4.6)
msgpack (~> 1.0) msgpack (~> 1.0)
brakeman (4.10.0) brakeman (4.8.2)
browser (4.2.0) browser (4.2.0)
builder (3.2.4) builder (3.2.4)
bullet (6.1.0) bullet (6.1.0)
@@ -121,12 +133,12 @@ GEM
i18n i18n
rake (>= 10.0.0) rake (>= 10.0.0)
sshkit (>= 1.9.0) sshkit (>= 1.9.0)
capistrano-bundler (2.0.1) capistrano-bundler (1.6.0)
capistrano (~> 3.1) capistrano (~> 3.1)
capistrano-rails (1.6.1) capistrano-rails (1.5.0)
capistrano (~> 3.1) capistrano (~> 3.1)
capistrano-bundler (>= 1.1, < 3) capistrano-bundler (~> 1.1)
capistrano-rbenv (2.2.0) capistrano-rbenv (2.1.6)
capistrano (~> 3.1) capistrano (~> 3.1)
sshkit (~> 1.3) sshkit (~> 1.3)
capistrano-yarn (2.0.2) capistrano-yarn (2.0.2)
@@ -141,13 +153,12 @@ GEM
xpath (~> 3.2) xpath (~> 3.2)
case_transform (0.2) case_transform (0.2)
activesupport activesupport
cbor (0.5.9.6)
charlock_holmes (0.7.7) charlock_holmes (0.7.7)
chewy (5.1.0) chewy (5.1.0)
activesupport (>= 4.0) activesupport (>= 4.0)
elasticsearch (>= 2.0.0) elasticsearch (>= 2.0.0)
elasticsearch-dsl elasticsearch-dsl
chunky_png (1.3.12) chunky_png (1.3.11)
cld3 (3.3.0) cld3 (3.3.0)
ffi (>= 1.1.0, < 1.12.0) ffi (>= 1.1.0, < 1.12.0)
climate_control (0.2.0) climate_control (0.2.0)
@@ -155,17 +166,15 @@ GEM
climate_control (>= 0.0.3, < 1.0) climate_control (>= 0.0.3, < 1.0)
coderay (1.1.3) coderay (1.1.3)
color_diff (0.1) color_diff (0.1)
concurrent-ruby (1.1.7) concurrent-ruby (1.1.6)
connection_pool (2.2.3) connection_pool (2.2.3)
cose (1.0.0) crack (0.4.3)
cbor (~> 0.5.9) safe_yaml (~> 1.0.0)
openssl-signature_algorithm (~> 0.4.0)
crack (0.4.4)
crass (1.0.6) crass (1.0.6)
css_parser (1.7.1) css_parser (1.7.1)
addressable addressable
debug_inspector (0.0.3) debug_inspector (0.0.3)
devise (4.7.3) devise (4.7.2)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (>= 4.1.0) railties (>= 4.1.0)
@@ -188,33 +197,34 @@ GEM
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.4.0) doorkeeper (5.4.0)
railties (>= 5) railties (>= 5)
dotenv (2.7.6) dotenv (2.7.5)
dotenv-rails (2.7.6) dotenv-rails (2.7.5)
dotenv (= 2.7.6) dotenv (= 2.7.5)
railties (>= 3.2) railties (>= 3.2, < 6.1)
e2mmap (0.1.0) e2mmap (0.1.0)
ed25519 (1.2.4) ed25519 (1.2.4)
elasticsearch (7.9.0) elasticsearch (7.8.0)
elasticsearch-api (= 7.9.0) elasticsearch-api (= 7.8.0)
elasticsearch-transport (= 7.9.0) elasticsearch-transport (= 7.8.0)
elasticsearch-api (7.9.0) elasticsearch-api (7.8.0)
multi_json multi_json
elasticsearch-dsl (0.1.9) elasticsearch-dsl (0.1.9)
elasticsearch-transport (7.9.0) elasticsearch-transport (7.8.0)
faraday (~> 1) faraday (~> 1)
multi_json multi_json
encryptor (3.0.0) encryptor (3.0.0)
equatable (0.6.1)
erubi (1.9.0) erubi (1.9.0)
et-orbi (1.2.4) et-orbi (1.2.4)
tzinfo tzinfo
excon (0.76.0) excon (0.75.0)
fabrication (2.21.1) fabrication (2.21.1)
faker (2.14.0) faker (2.13.0)
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
faraday (1.0.1) faraday (1.0.1)
multipart-post (>= 1.2, < 3) multipart-post (>= 1.2, < 3)
fast_blank (1.0.0) fast_blank (1.0.0)
fastimage (2.2.0) fastimage (2.1.7)
ffi (1.10.0) ffi (1.10.0)
ffi-compiler (1.0.1) ffi-compiler (1.0.1)
ffi (>= 1.0.0) ffi (>= 1.0.0)
@@ -232,7 +242,7 @@ GEM
fog-json (>= 1.0) fog-json (>= 1.0)
ipaddress (>= 0.8) ipaddress (>= 0.8)
formatador (0.2.5) formatador (0.2.5)
fugit (1.3.9) fugit (1.3.6)
et-orbi (~> 1.1, >= 1.1.8) et-orbi (~> 1.1, >= 1.1.8)
raabro (~> 1.3) raabro (~> 1.3)
fuubar (2.5.0) fuubar (2.5.0)
@@ -240,7 +250,7 @@ GEM
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.4)
globalid (0.4.2) globalid (0.4.2)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
hamlit (2.13.0) hamlit (2.11.0)
temple (>= 0.8.2) temple (>= 0.8.2)
thor thor
tilt tilt
@@ -271,7 +281,7 @@ GEM
httplog (1.4.3) httplog (1.4.3)
rack (>= 1.0) rack (>= 1.0)
rainbow (>= 2.0.0) rainbow (>= 2.0.0)
i18n (1.8.5) i18n (1.8.3)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
i18n-tasks (0.9.31) i18n-tasks (0.9.31)
activesupport (>= 4.0.2) activesupport (>= 4.0.2)
@@ -289,7 +299,7 @@ GEM
jmespath (1.4.0) jmespath (1.4.0)
json (2.3.1) json (2.3.1)
json-canonicalization (0.2.0) json-canonicalization (0.2.0)
json-ld (3.1.5) json-ld (3.1.4)
htmlentities (~> 4.3) htmlentities (~> 4.3)
json-canonicalization (~> 0.2) json-canonicalization (~> 0.2)
link_header (~> 0.0, >= 0.0.8) link_header (~> 0.0, >= 0.0.8)
@@ -300,7 +310,7 @@ GEM
json-ld (~> 3.1) json-ld (~> 3.1)
rdf (~> 3.1) rdf (~> 3.1)
jsonapi-renderer (0.2.2) jsonapi-renderer (0.2.2)
jwt (2.2.2) jwt (2.2.1)
kaminari (1.2.1) kaminari (1.2.1)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.1) kaminari-actionview (= 1.2.1)
@@ -327,7 +337,7 @@ GEM
activesupport (>= 4) activesupport (>= 4)
railties (>= 4) railties (>= 4)
request_store (~> 1.0) request_store (~> 1.0)
loofah (2.7.0) loofah (2.6.0)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.7.1) mail (2.7.1)
@@ -340,7 +350,7 @@ GEM
redis (>= 3.0.5) redis (>= 3.0.5)
memory_profiler (0.9.14) memory_profiler (0.9.14)
method_source (1.0.0) method_source (1.0.0)
microformats (4.2.1) microformats (4.2.0)
json (~> 2.2) json (~> 2.2)
nokogiri (~> 1.10) nokogiri (~> 1.10)
mime-types (3.3.1) mime-types (3.3.1)
@@ -349,16 +359,17 @@ GEM
mimemagic (0.3.5) mimemagic (0.3.5)
mini_mime (1.0.2) mini_mime (1.0.2)
mini_portile2 (2.4.0) mini_portile2 (2.4.0)
minitest (5.14.2) minitest (5.14.1)
msgpack (1.3.3) msgpack (1.3.3)
multi_json (1.15.0) multi_json (1.14.1)
multipart-post (2.1.1) multipart-post (2.1.1)
net-ldap (0.16.3) necromancer (0.5.1)
net-ldap (0.16.2)
net-scp (3.0.0) net-scp (3.0.0)
net-ssh (>= 2.6.5, < 7.0.0) net-ssh (>= 2.6.5, < 7.0.0)
net-ssh (6.1.0) net-ssh (6.1.0)
nio4r (2.5.4) nio4r (2.5.2)
nokogiri (1.10.10) nokogiri (1.10.9)
mini_portile2 (~> 2.4.0) mini_portile2 (~> 2.4.0)
nokogumbo (2.0.2) nokogumbo (2.0.2)
nokogiri (~> 1.8, >= 1.8.4) nokogiri (~> 1.8, >= 1.8.4)
@@ -367,24 +378,19 @@ GEM
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
sidekiq (>= 3.5) sidekiq (>= 3.5)
statsd-ruby (~> 1.4, >= 1.4.0) statsd-ruby (~> 1.4, >= 1.4.0)
oj (3.10.16) oj (3.10.6)
omniauth (1.9.1) omniauth (1.9.1)
hashie (>= 3.4.6) hashie (>= 3.4.6)
rack (>= 1.6.2, < 3) rack (>= 1.6.2, < 3)
omniauth-cas (2.0.0) omniauth-cas (1.1.1)
addressable (~> 2.3) addressable (~> 2.3)
nokogiri (~> 1.5) nokogiri (~> 1.5)
omniauth (~> 1.2) omniauth (~> 1.2)
omniauth-rails_csrf_protection (0.1.2) omniauth-saml (1.10.2)
actionpack (>= 4.2)
omniauth (>= 1.3.1)
omniauth-saml (1.10.3)
omniauth (~> 1.3, >= 1.3.2) omniauth (~> 1.3, >= 1.3.2)
ruby-saml (~> 1.9) ruby-saml (~> 1.9)
openssl (2.2.0)
openssl-signature_algorithm (0.4.0)
orm_adapter (0.5.0) orm_adapter (0.5.0)
ox (2.13.4) ox (2.13.2)
paperclip (6.0.0) paperclip (6.0.0)
activemodel (>= 4.2.0) activemodel (>= 4.2.0)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
@@ -394,23 +400,20 @@ GEM
paperclip-av-transcoder (0.6.4) paperclip-av-transcoder (0.6.4)
av (~> 0.9.0) av (~> 0.9.0)
paperclip (>= 2.5.2) paperclip (>= 2.5.2)
parallel (1.20.1) parallel (1.19.2)
parallel_tests (3.4.0) parallel_tests (3.0.0)
parallel parallel
parser (2.7.2.0) parser (2.7.1.4)
ast (~> 2.4.1) ast (~> 2.4.1)
parslet (2.0.0) parslet (2.0.0)
pastel (0.8.0) pastel (0.7.4)
equatable (~> 0.6)
tty-color (~> 0.5) tty-color (~> 0.5)
pg (1.2.3) pg (1.2.3)
pghero (2.7.2) pghero (2.5.1)
activerecord (>= 5) activerecord (>= 5)
pkg-config (1.4.4) pkg-config (1.4.1)
pluck_each (0.1.3) premailer (1.11.1)
activerecord (> 3.2.0)
activesupport (> 3.0.0)
posix-spawn (0.3.15)
premailer (1.14.2)
addressable addressable
css_parser (>= 1.6.0) css_parser (>= 1.6.0)
htmlentities (>= 4.0.0) htmlentities (>= 4.0.0)
@@ -426,12 +429,12 @@ GEM
pry (~> 0.13.0) pry (~> 0.13.0)
pry-rails (0.3.9) pry-rails (0.3.9)
pry (>= 0.10.4) pry (>= 0.10.4)
public_suffix (4.0.6) public_suffix (4.0.5)
puma (5.0.4) puma (4.3.5)
nio4r (~> 2.0) nio4r (~> 2.0)
pundit (2.1.0) pundit (2.1.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
raabro (1.3.3) raabro (1.3.1)
rack (2.2.3) rack (2.2.3)
rack-attack (6.3.1) rack-attack (6.3.1)
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
@@ -441,18 +444,18 @@ GEM
rack rack
rack-test (1.1.0) rack-test (1.1.0)
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
rails (5.2.4.4) rails (5.2.4.3)
actioncable (= 5.2.4.4) actioncable (= 5.2.4.3)
actionmailer (= 5.2.4.4) actionmailer (= 5.2.4.3)
actionpack (= 5.2.4.4) actionpack (= 5.2.4.3)
actionview (= 5.2.4.4) actionview (= 5.2.4.3)
activejob (= 5.2.4.4) activejob (= 5.2.4.3)
activemodel (= 5.2.4.4) activemodel (= 5.2.4.3)
activerecord (= 5.2.4.4) activerecord (= 5.2.4.3)
activestorage (= 5.2.4.4) activestorage (= 5.2.4.3)
activesupport (= 5.2.4.4) activesupport (= 5.2.4.3)
bundler (>= 1.3.0) bundler (>= 1.3.0)
railties (= 5.2.4.4) railties (= 5.2.4.3)
sprockets-rails (>= 2.0.0) sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.5) rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1) actionpack (>= 5.0.1.rc1)
@@ -468,20 +471,20 @@ GEM
railties (>= 5.0, < 6) railties (>= 5.0, < 6)
rails-settings-cached (0.6.6) rails-settings-cached (0.6.6)
rails (>= 4.2.0) rails (>= 4.2.0)
railties (5.2.4.4) railties (5.2.4.3)
actionpack (= 5.2.4.4) actionpack (= 5.2.4.3)
activesupport (= 5.2.4.4) activesupport (= 5.2.4.3)
method_source method_source
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.19.0, < 2.0) thor (>= 0.19.0, < 2.0)
rainbow (3.0.0) rainbow (3.0.0)
rake (13.0.1) rake (13.0.1)
rdf (3.1.7) rdf (3.1.4)
hamster (~> 3.0) hamster (~> 3.0)
link_header (~> 0.0, >= 0.0.8) link_header (~> 0.0, >= 0.0.8)
rdf-normalize (0.4.0) rdf-normalize (0.4.0)
rdf (~> 3.1) rdf (~> 3.1)
redis (4.2.5) redis (4.2.1)
redis-actionpack (5.2.0) redis-actionpack (5.2.0)
actionpack (>= 5, < 7) actionpack (>= 5, < 7)
redis-rack (>= 2.1.0, < 3) redis-rack (>= 2.1.0, < 3)
@@ -489,9 +492,9 @@ GEM
redis-activesupport (5.2.0) redis-activesupport (5.2.0)
activesupport (>= 3, < 7) activesupport (>= 3, < 7)
redis-store (>= 1.3, < 2) redis-store (>= 1.3, < 2)
redis-namespace (1.8.0) redis-namespace (1.7.0)
redis (>= 3.0.4) redis (>= 3.0.4)
redis-rack (2.1.3) redis-rack (2.1.2)
rack (>= 2.0.8, < 3) rack (>= 2.0.8, < 3)
redis-store (>= 1.2, < 2) redis-store (>= 1.2, < 2)
redis-rails (5.0.2) redis-rails (5.0.2)
@@ -500,7 +503,7 @@ GEM
redis-store (>= 1.2, < 2) redis-store (>= 1.2, < 2)
redis-store (1.9.0) redis-store (1.9.0)
redis (>= 4, < 5) redis (>= 4, < 5)
regexp_parser (1.8.2) regexp_parser (1.7.1)
request_store (1.5.0) request_store (1.5.0)
rack (>= 1.4) rack (>= 1.4)
responders (3.0.1) responders (3.0.1)
@@ -513,7 +516,7 @@ GEM
chunky_png (~> 1.0) chunky_png (~> 1.0)
rqrcode_core (~> 0.1) rqrcode_core (~> 0.1)
rqrcode_core (0.1.2) rqrcode_core (0.1.2)
rspec-core (3.9.3) rspec-core (3.9.2)
rspec-support (~> 3.9.3) rspec-support (~> 3.9.3)
rspec-expectations (3.9.2) rspec-expectations (3.9.2)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
@@ -535,38 +538,33 @@ GEM
rspec-support (3.9.3) rspec-support (3.9.3)
rspec_junit_formatter (0.4.1) rspec_junit_formatter (0.4.1)
rspec-core (>= 2, < 4, != 2.12.0) rspec-core (>= 2, < 4, != 2.12.0)
rubocop (1.3.1) rubocop (0.86.0)
parallel (~> 1.10) parallel (~> 1.10)
parser (>= 2.7.1.5) parser (>= 2.7.0.1)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8) regexp_parser (>= 1.7)
rexml rexml
rubocop-ast (>= 1.1.1) rubocop-ast (>= 0.0.3, < 1.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0) unicode-display_width (>= 1.4.0, < 2.0)
rubocop-ast (1.1.1) rubocop-ast (0.1.0)
parser (>= 2.7.1.5) parser (>= 2.7.0.1)
rubocop-rails (2.8.1) rubocop-rails (2.6.0)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 0.87.0) rubocop (>= 0.82.0)
ruby-progressbar (1.10.1) ruby-progressbar (1.10.1)
ruby-saml (1.11.0) ruby-saml (1.11.0)
nokogiri (>= 1.5.10) nokogiri (>= 1.5.10)
rufus-scheduler (3.6.0) rufus-scheduler (3.6.0)
fugit (~> 1.1, >= 1.1.6) fugit (~> 1.1, >= 1.1.6)
safety_net_attestation (0.4.0) safe_yaml (1.0.5)
jwt (~> 2.0)
sanitize (5.2.1) sanitize (5.2.1)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.8.0) nokogiri (>= 1.8.0)
nokogumbo (~> 2.0) nokogumbo (~> 2.0)
scenic (1.5.4)
activerecord (>= 4.0.0)
railties (>= 4.0.0)
securecompare (1.0.0)
semantic_range (2.3.0) semantic_range (2.3.0)
sidekiq (6.1.2) sidekiq (6.1.0)
connection_pool (>= 2.2.2) connection_pool (>= 2.2.2)
rack (~> 2.0) rack (~> 2.0)
redis (>= 4.2.0) redis (>= 4.2.0)
@@ -579,87 +577,74 @@ GEM
sidekiq (>= 3) sidekiq (>= 3)
thwait thwait
tilt (>= 1.4.0) tilt (>= 1.4.0)
sidekiq-unique-jobs (6.0.25) sidekiq-unique-jobs (6.0.22)
concurrent-ruby (~> 1.0, >= 1.0.5) concurrent-ruby (~> 1.0, >= 1.0.5)
sidekiq (>= 4.0, < 7.0) sidekiq (>= 4.0, < 7.0)
thor (>= 0.20, < 2.0) thor (~> 0)
simple-navigation (4.1.0) simple-navigation (4.1.0)
activesupport (>= 2.3.2) activesupport (>= 2.3.2)
simple_form (5.0.3) simple_form (5.0.2)
actionpack (>= 5.0) actionpack (>= 5.0)
activemodel (>= 5.0) activemodel (>= 5.0)
simplecov (0.19.1) simplecov (0.18.5)
docile (~> 1.1) docile (~> 1.1)
simplecov-html (~> 0.11) simplecov-html (~> 0.11)
simplecov-html (0.12.3) simplecov-html (0.12.2)
sprockets (3.7.2) sprockets (3.7.2)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
rack (> 1, < 3) rack (> 1, < 3)
sprockets-rails (3.2.2) sprockets-rails (3.2.1)
actionpack (>= 4.0) actionpack (>= 4.0)
activesupport (>= 4.0) activesupport (>= 4.0)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
sshkit (1.21.0) sshkit (1.21.0)
net-scp (>= 1.1.2) net-scp (>= 1.1.2)
net-ssh (>= 2.8.0) net-ssh (>= 2.8.0)
stackprof (0.2.16) stackprof (0.2.15)
statsd-ruby (1.4.0) statsd-ruby (1.4.0)
stoplight (2.2.1) stoplight (2.2.0)
streamio-ffmpeg (3.0.2) streamio-ffmpeg (3.0.2)
multi_json (~> 1.8) multi_json (~> 1.8)
strong_migrations (0.7.2) strong_migrations (0.6.8)
activerecord (>= 5) activerecord (>= 5)
temple (0.8.2) temple (0.8.2)
terminal-table (1.8.0) terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1) unicode-display_width (~> 1.1, >= 1.1.1)
terrapin (0.6.0) terrapin (0.6.0)
climate_control (>= 0.0.3, < 1.0) climate_control (>= 0.0.3, < 1.0)
thor (1.0.1) thor (0.20.3)
thread_safe (0.3.6) thread_safe (0.3.6)
thwait (0.2.0) thwait (0.1.0)
e2mmap
tilt (2.0.10) tilt (2.0.10)
tpm-key_attestation (0.9.0) tty-color (0.5.1)
bindata (~> 2.4)
openssl-signature_algorithm (~> 0.4.0)
tty-color (0.5.2)
tty-cursor (0.7.1) tty-cursor (0.7.1)
tty-prompt (0.22.0) tty-prompt (0.21.0)
pastel (~> 0.8) necromancer (~> 0.5.0)
tty-reader (~> 0.8) pastel (~> 0.7.0)
tty-reader (0.8.0) tty-reader (~> 0.7.0)
tty-reader (0.7.0)
tty-cursor (~> 0.7) tty-cursor (~> 0.7)
tty-screen (~> 0.8) tty-screen (~> 0.7)
wisper (~> 2.0) wisper (~> 2.0.0)
tty-screen (0.8.1) tty-screen (0.8.0)
twitter-text (1.14.7) twitter-text (1.14.7)
unf (~> 0.1.0) unf (~> 0.1.0)
tzinfo (1.2.7) tzinfo (1.2.7)
thread_safe (~> 0.1) thread_safe (~> 0.1)
tzinfo-data (1.2020.4) tzinfo-data (1.2020.1)
tzinfo (>= 1.0.0) tzinfo (>= 1.0.0)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.7) unf_ext (0.0.7.7)
unicode-display_width (1.7.0) unicode-display_width (1.7.0)
uniform_notifier (1.13.0) uniform_notifier (1.13.0)
warden (1.2.9) warden (1.2.8)
rack (>= 2.0.9) rack (>= 2.0.6)
webauthn (3.0.0.alpha1) webmock (3.8.3)
android_key_attestation (~> 0.3.0)
awrence (~> 1.1)
bindata (~> 2.4)
cbor (~> 0.5.9)
cose (~> 1.0)
openssl (~> 2.0)
safety_net_attestation (~> 0.4.0)
securecompare (~> 1.0)
tpm-key_attestation (~> 0.9.0)
webmock (3.10.0)
addressable (>= 2.3.6) addressable (>= 2.3.6)
crack (>= 0.3.2) crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0) hashdiff (>= 0.4.0, < 2.0.0)
webpacker (5.2.1) webpacker (5.1.1)
activesupport (>= 5.2) activesupport (>= 5.2)
rack-proxy (>= 0.6.1) rack-proxy (>= 0.6.1)
railties (>= 5.2) railties (>= 5.2)
@@ -667,11 +652,10 @@ GEM
webpush (0.3.8) webpush (0.3.8)
hkdf (~> 0.2) hkdf (~> 0.2)
jwt (~> 2.0) jwt (~> 2.0)
websocket-driver (0.7.3) websocket-driver (0.7.2)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5) websocket-extensions (0.1.5)
wisper (2.0.1) wisper (2.0.1)
xorcist (1.1.2)
xpath (3.2.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
@@ -680,21 +664,21 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
active_model_serializers (~> 0.10) active_model_serializers (~> 0.10)
active_record_query_trace (~> 1.8) active_record_query_trace (~> 1.7)
addressable (~> 2.7) addressable (~> 2.7)
annotate (~> 3.1) annotate (~> 3.1)
aws-sdk-s3 (~> 1.85) aws-sdk-s3 (~> 1.73)
better_errors (~> 2.9) better_errors (~> 2.7)
binding_of_caller (~> 0.7) binding_of_caller (~> 0.7)
blurhash (~> 0.1) blurhash (~> 0.1)
bootsnap (~> 1.5) bootsnap (~> 1.4)
brakeman (~> 4.10) brakeman (~> 4.8)
browser browser
bullet (~> 6.1) bullet (~> 6.1)
bundler-audit (~> 0.7) bundler-audit (~> 0.7)
capistrano (~> 3.14) capistrano (~> 3.14)
capistrano-rails (~> 1.6) capistrano-rails (~> 1.5)
capistrano-rbenv (~> 2.2) capistrano-rbenv (~> 2.1)
capistrano-yarn (~> 2.0) capistrano-yarn (~> 2.0)
capybara (~> 3.33) capybara (~> 3.33)
charlock_holmes (~> 0.7.7) charlock_holmes (~> 0.7.7)
@@ -710,9 +694,10 @@ DEPENDENCIES
discard (~> 1.2) discard (~> 1.2)
doorkeeper (~> 5.4) doorkeeper (~> 5.4)
dotenv-rails (~> 2.7) dotenv-rails (~> 2.7)
e2mmap (~> 0.1.0)
ed25519 (~> 1.2) ed25519 (~> 1.2)
fabrication (~> 2.21) fabrication (~> 2.21)
faker (~> 2.14) faker (~> 2.13)
fast_blank (~> 1.0) fast_blank (~> 1.0)
fastimage fastimage
fog-core (<= 2.1.0) fog-core (<= 2.1.0)
@@ -724,6 +709,7 @@ DEPENDENCIES
htmlentities (~> 4.3) htmlentities (~> 4.3)
http (~> 4.4) http (~> 4.4)
http_accept_language (~> 2.1) http_accept_language (~> 2.1)
http_parser.rb (~> 0.6)!
httplog (~> 1.4.3) httplog (~> 1.4.3)
i18n-tasks (~> 0.9) i18n-tasks (~> 0.9)
idn-ruby idn-ruby
@@ -746,65 +732,61 @@ DEPENDENCIES
nsa (~> 0.2) nsa (~> 0.2)
oj (~> 3.10) oj (~> 3.10)
omniauth (~> 1.9) omniauth (~> 1.9)
omniauth-cas (~> 2.0) omniauth-cas (~> 1.1)
omniauth-rails_csrf_protection (~> 0.1)
omniauth-saml (~> 1.10) omniauth-saml (~> 1.10)
ox (~> 2.13) ox (~> 2.13)
paperclip (~> 6.0) paperclip (~> 6.0)
paperclip-av-transcoder (~> 0.6) paperclip-av-transcoder (~> 0.6)
parallel (~> 1.20) parallel (~> 1.19)
parallel_tests (~> 3.4) parallel_tests (~> 3.0)
parslet parslet
pg (~> 1.2) pg (~> 1.2)
pghero (~> 2.7) pghero (~> 2.5)
pkg-config (~> 1.4) pkg-config (~> 1.4)
pluck_each (~> 0.1.3) posix-spawn!
posix-spawn
premailer-rails premailer-rails
private_address_check (~> 0.5) private_address_check (~> 0.5)
pry-byebug (~> 3.9) pry-byebug (~> 3.9)
pry-rails (~> 0.3) pry-rails (~> 0.3)
puma (~> 5.0) puma (~> 4.3)
pundit (~> 2.1) pundit (~> 2.1)
rack (~> 2.2.3) rack (~> 2.2.3)
rack-attack (~> 6.3) rack-attack (~> 6.3)
rack-cors (~> 1.1) rack-cors (~> 1.1)
rails (~> 5.2.4.4) rails (~> 5.2.4.3)
rails-controller-testing (~> 1.0) rails-controller-testing (~> 1.0)
rails-i18n (~> 5.1) rails-i18n (~> 5.1)
rails-settings-cached (~> 0.6) rails-settings-cached (~> 0.6)
rdf-normalize (~> 0.4) rdf-normalize (~> 0.4)
redis (~> 4.2) redis (~> 4.2)
redis-namespace (~> 1.8) redis-namespace (~> 1.7)
redis-rails (~> 5.0) redis-rails (~> 5.0)
rqrcode (~> 1.1) rqrcode (~> 1.1)
rspec-rails (~> 4.0) rspec-rails (~> 4.0)
rspec-sidekiq (~> 3.1) rspec-sidekiq (~> 3.1)
rspec_junit_formatter (~> 0.4) rspec_junit_formatter (~> 0.4)
rubocop (~> 1.3) rubocop (~> 0.86)
rubocop-rails (~> 2.8) rubocop-rails (~> 2.6)
ruby-progressbar (~> 1.10) ruby-progressbar (~> 1.10)
sanitize (~> 5.2) sanitize (~> 5.2)
scenic (~> 1.5) sidekiq (~> 6.0)
sidekiq (~> 6.1)
sidekiq-bulk (~> 0.2.0) sidekiq-bulk (~> 0.2.0)
sidekiq-scheduler (~> 3.0) sidekiq-scheduler (~> 3.0)
sidekiq-unique-jobs (~> 6.0) sidekiq-unique-jobs (~> 6.0)
simple-navigation (~> 4.1) simple-navigation (~> 4.1)
simple_form (~> 5.0) simple_form (~> 5.0)
simplecov (~> 0.19) simplecov (~> 0.18)
sprockets (~> 3.7.2) sprockets (~> 3.7.2)
sprockets-rails (~> 3.2) sprockets-rails (~> 3.2)
stackprof stackprof
stoplight (~> 2.2.1) stoplight (~> 2.2.0)
streamio-ffmpeg (~> 3.0) streamio-ffmpeg (~> 3.0)
strong_migrations (~> 0.7) strong_migrations (~> 0.6)
thor (~> 1.0) thor (~> 0.20)
tty-prompt (~> 0.22) thwait (~> 0.1.0)
tty-prompt (~> 0.21)
twitter-text (~> 1.14) twitter-text (~> 1.14)
tzinfo-data (~> 1.2020) tzinfo-data (~> 1.2020)
webauthn (~> 3.0.0.alpha1) webmock (~> 3.8)
webmock (~> 3.10) webpacker (~> 5.1)
webpacker (~> 5.2)
webpush webpush
xorcist (~> 1.1)

View File

@@ -1,4 +1,4 @@
web: bin/heroku-web web: if [ "$RUN_STREAMING" != "true" ]; then BIND=0.0.0.0 bundle exec puma -C config/puma.rb; else BIND=0.0.0.0 node ./streaming; fi
worker: bundle exec sidekiq worker: bundle exec sidekiq
# For the streaming API, you need a separate app that shares Postgres and Redis: # For the streaming API, you need a separate app that shares Postgres and Redis:

View File

@@ -1,15 +1,12 @@
# frozen_string_literal: true # frozen_string_literal: true
class AboutController < ApplicationController class AboutController < ApplicationController
include RegistrationSpamConcern
layout 'public' layout 'public'
before_action :require_open_federation!, only: [:show, :more] before_action :require_open_federation!, only: [:show, :more]
before_action :set_body_classes, only: :show before_action :set_body_classes, only: :show
before_action :set_instance_presenter before_action :set_instance_presenter
before_action :set_expires_in, only: [:more, :terms] before_action :set_expires_in, only: [:show, :more, :terms]
before_action :set_registration_form_time, only: :show
skip_before_action :require_functional!, only: [:more, :terms] skip_before_action :require_functional!, only: [:more, :terms]

View File

@@ -29,7 +29,8 @@ class AccountsController < ApplicationController
end end
@pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses? @pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses?
@statuses = cached_filtered_status_page @statuses = filtered_status_page
@statuses = cache_collection(@statuses, Status)
@rss_url = rss_url @rss_url = rss_url
unless @statuses.empty? unless @statuses.empty?
@@ -81,7 +82,7 @@ class AccountsController < ApplicationController
end end
def account_media_status_ids def account_media_status_ids
@account.media_attachments.attached.reorder(nil).select(:status_id).group(:status_id) @account.media_attachments.attached.reorder(nil).select(:status_id).distinct
end end
def no_replies_scope def no_replies_scope
@@ -102,10 +103,6 @@ class AccountsController < ApplicationController
params[:username] params[:username]
end end
def skip_temporary_suspension_response?
request.format == :json
end
def rss_url def rss_url
if tag_requested? if tag_requested?
short_account_tag_url(@account, params[:tag], format: 'rss') short_account_tag_url(@account, params[:tag], format: 'rss')
@@ -146,13 +143,8 @@ class AccountsController < ApplicationController
request.path.split('.').first.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize) request.path.split('.').first.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
end end
def cached_filtered_status_page def filtered_status_page
cache_collection_paginated_by_id( filtered_statuses.paginate_by_id(PAGE_SIZE, params_slice(:max_id, :min_id, :since_id))
filtered_statuses,
Status,
PAGE_SIZE,
params_slice(:max_id, :min_id, :since_id)
)
end end
def params_slice(*keys) def params_slice(*keys)

View File

@@ -8,8 +8,4 @@ class ActivityPub::BaseController < Api::BaseController
def set_cache_headers def set_cache_headers
response.headers['Vary'] = 'Signature' if authorized_fetch_mode? response.headers['Vary'] = 'Signature' if authorized_fetch_mode?
end end
def skip_temporary_suspension_response?
false
end
end end

View File

@@ -12,7 +12,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
def show def show
expires_in 3.minutes, public: public_fetch_mode? expires_in 3.minutes, public: public_fetch_mode?
render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, skip_activities: true
end end
private private
@@ -20,9 +20,17 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
def set_items def set_items
case params[:id] case params[:id]
when 'featured' when 'featured'
@items = for_signed_account { cache_collection(@account.pinned_statuses, Status) } @items = begin
when 'tags' # Because in public fetch mode we cache the response, there would be no
@items = for_signed_account { @account.featured_tags } # benefit from performing the check below, since a blocked account or domain
# would likely be served the cache from the reverse proxy anyway
if authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain)))
[]
else
cache_collection(@account.pinned_statuses, Status)
end
end
when 'devices' when 'devices'
@items = @account.devices @items = @account.devices
else else
@@ -32,7 +40,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
def set_size def set_size
case params[:id] case params[:id]
when 'featured', 'devices', 'tags' when 'featured', 'devices'
@size = @items.size @size = @items.size
else else
not_found not_found
@@ -43,7 +51,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
case params[:id] case params[:id]
when 'featured' when 'featured'
@type = :ordered @type = :ordered
when 'devices', 'tags' when 'devices'
@type = :unordered @type = :unordered
else else
not_found not_found
@@ -58,16 +66,4 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
items: @items items: @items
) )
end end
def for_signed_account
# Because in public fetch mode we cache the response, there would be no
# benefit from performing the check below, since a blocked account or domain
# would likely be served the cache from the reverse proxy anyway
if authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain)))
[]
else
yield
end
end
end end

View File

@@ -1,36 +0,0 @@
# frozen_string_literal: true
class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseController
include SignatureVerification
include AccountOwnedConcern
before_action :require_signature!
before_action :set_items
before_action :set_cache_headers
def show
expires_in 0, public: false
render json: collection_presenter,
serializer: ActivityPub::CollectionSerializer,
adapter: ActivityPub::Adapter,
content_type: 'application/activity+json'
end
private
def uri_prefix
signed_request_account.uri[/http(s?):\/\/[^\/]+\//]
end
def set_items
@items = @account.followers.where(Account.arel_table[:uri].matches(uri_prefix + '%', false, true)).pluck(:uri)
end
def collection_presenter
ActivityPub::CollectionPresenter.new(
id: account_followers_synchronization_url(@account),
type: :ordered,
items: @items
)
end
end

View File

@@ -11,7 +11,6 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
def create def create
upgrade_account upgrade_account
process_collection_synchronization
process_payload process_payload
head 202 head 202
end end
@@ -33,10 +32,6 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
params[:account_username].present? params[:account_username].present?
end end
def skip_temporary_suspension_response?
true
end
def body def body
return @body if defined?(@body) return @body if defined?(@body)
@@ -57,19 +52,6 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
DeliveryFailureTracker.reset!(signed_request_account.inbox_url) DeliveryFailureTracker.reset!(signed_request_account.inbox_url)
end end
def process_collection_synchronization
raw_params = request.headers['Collection-Synchronization']
return if raw_params.blank? || ENV['DISABLE_FOLLOWERS_SYNCHRONIZATION'] == 'true'
# Re-using the syntax for signature parameters
tree = SignatureParamsParser.new.parse(raw_params)
params = SignatureParamsTransformer.new.apply(tree)
ActivityPub::PrepareFollowersSynchronizationService.new.call(signed_request_account, params)
rescue Parslet::ParseFailed
Rails.logger.warn 'Error parsing Collection-Synchronization header'
end
def process_payload def process_payload
ActivityPub::ProcessingWorker.perform_async(signed_request_account.id, body, @account&.id) ActivityPub::ProcessingWorker.perform_async(signed_request_account.id, body, @account&.id)
end end

View File

@@ -20,9 +20,9 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
def outbox_presenter def outbox_presenter
if page_requested? if page_requested?
ActivityPub::CollectionPresenter.new( ActivityPub::CollectionPresenter.new(
id: outbox_url(page_params), id: account_outbox_url(@account, page_params),
type: :ordered, type: :ordered,
part_of: outbox_url, part_of: account_outbox_url(@account),
prev: prev_page, prev: prev_page,
next: next_page, next: next_page,
items: @statuses items: @statuses
@@ -32,20 +32,12 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
id: account_outbox_url(@account), id: account_outbox_url(@account),
type: :ordered, type: :ordered,
size: @account.statuses_count, size: @account.statuses_count,
first: outbox_url(page: true), first: account_outbox_url(@account, page: true),
last: outbox_url(page: true, min_id: 0) last: account_outbox_url(@account, page: true, min_id: 0)
) )
end end
end end
def outbox_url(**kwargs)
if params[:account_username].present?
account_outbox_url(@account, **kwargs)
else
instance_actor_outbox_url(**kwargs)
end
end
def next_page def next_page
account_outbox_url(@account, page: true, max_id: @statuses.last.id) if @statuses.size == LIMIT account_outbox_url(@account, page: true, max_id: @statuses.last.id) if @statuses.size == LIMIT
end end
@@ -57,12 +49,9 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
def set_statuses def set_statuses
return unless page_requested? return unless page_requested?
@statuses = cache_collection_paginated_by_id( @statuses = @account.statuses.permitted_for(@account, signed_request_account)
@account.statuses.permitted_for(@account, signed_request_account), @statuses = @statuses.paginate_by_id(LIMIT, params_slice(:max_id, :min_id, :since_id))
Status, @statuses = cache_collection(@statuses, Status)
LIMIT,
params_slice(:max_id, :min_id, :since_id)
)
end end
def page_requested? def page_requested?
@@ -72,8 +61,4 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
def page_params def page_params
{ page: true, max_id: params[:max_id], min_id: params[:min_id] }.compact { page: true, max_id: params[:max_id], min_id: params[:min_id] }.compact
end end
def set_account
@account = params[:account_username].present? ? Account.find_local!(username_param) : Account.representative
end
end end

View File

@@ -31,7 +31,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
end end
def set_replies def set_replies
@replies = only_other_accounts? ? Status.where.not(account_id: @account.id).joins(:account).merge(Account.without_suspended) : @account.statuses @replies = only_other_accounts? ? Status.where.not(account_id: @account.id) : @account.statuses
@replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted]) @replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted])
@replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id]) @replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id])
end end

View File

@@ -2,7 +2,7 @@
module Admin module Admin
class AccountsController < BaseController class AccountsController < BaseController
before_action :set_account, except: [:index] before_action :set_account, only: [:show, :redownload, :remove_avatar, :remove_header, :enable, :unsilence, :unsuspend, :memorialize, :approve, :reject]
before_action :require_remote_account!, only: [:redownload] before_action :require_remote_account!, only: [:redownload]
before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject] before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject]
@@ -14,65 +14,49 @@ module Admin
def show def show
authorize @account, :show? authorize @account, :show?
@deletion_request = @account.deletion_request
@account_moderation_note = current_account.account_moderation_notes.new(target_account: @account) @account_moderation_note = current_account.account_moderation_notes.new(target_account: @account)
@moderation_notes = @account.targeted_moderation_notes.latest @moderation_notes = @account.targeted_moderation_notes.latest
@warnings = @account.targeted_account_warnings.latest.custom @warnings = @account.targeted_account_warnings.latest.custom
@domain_block = DomainBlock.rule_for(@account.domain)
end end
def memorialize def memorialize
authorize @account, :memorialize? authorize @account, :memorialize?
@account.memorialize! @account.memorialize!
log_action :memorialize, @account log_action :memorialize, @account
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.memorialized_msg', username: @account.acct) redirect_to admin_account_path(@account.id)
end end
def enable def enable
authorize @account.user, :enable? authorize @account.user, :enable?
@account.user.enable! @account.user.enable!
log_action :enable, @account.user log_action :enable, @account.user
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.enabled_msg', username: @account.acct) redirect_to admin_account_path(@account.id)
end end
def approve def approve
authorize @account.user, :approve? authorize @account.user, :approve?
@account.user.approve! @account.user.approve!
redirect_to admin_pending_accounts_path, notice: I18n.t('admin.accounts.approved_msg', username: @account.acct) redirect_to admin_pending_accounts_path
end end
def reject def reject
authorize @account.user, :reject? authorize @account.user, :reject?
DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false) SuspendAccountService.new.call(@account, reserve_email: false, reserve_username: false)
redirect_to admin_pending_accounts_path, notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct) redirect_to admin_pending_accounts_path
end
def destroy
authorize @account, :destroy?
Admin::AccountDeletionWorker.perform_async(@account.id)
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.destroyed_msg', username: @account.acct)
end
def unsensitive
authorize @account, :unsensitive?
@account.unsensitize!
log_action :unsensitive, @account
redirect_to admin_account_path(@account.id)
end end
def unsilence def unsilence
authorize @account, :unsilence? authorize @account, :unsilence?
@account.unsilence! @account.unsilence!
log_action :unsilence, @account log_action :unsilence, @account
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unsilenced_msg', username: @account.acct) redirect_to admin_account_path(@account.id)
end end
def unsuspend def unsuspend
authorize @account, :unsuspend? authorize @account, :unsuspend?
@account.unsuspend! @account.unsuspend!
Admin::UnsuspensionWorker.perform_async(@account.id)
log_action :unsuspend, @account log_action :unsuspend, @account
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unsuspended_msg', username: @account.acct) redirect_to admin_account_path(@account.id)
end end
def redownload def redownload
@@ -81,7 +65,7 @@ module Admin
@account.update!(last_webfingered_at: nil) @account.update!(last_webfingered_at: nil)
ResolveAccountService.new.call(@account) ResolveAccountService.new.call(@account)
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.redownloaded_msg', username: @account.acct) redirect_to admin_account_path(@account.id)
end end
def remove_avatar def remove_avatar
@@ -92,7 +76,7 @@ module Admin
log_action :remove_avatar, @account.user log_action :remove_avatar, @account.user
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_avatar_msg', username: @account.acct) redirect_to admin_account_path(@account.id)
end end
def remove_header def remove_header
@@ -103,7 +87,7 @@ module Admin
log_action :remove_header, @account.user log_action :remove_header, @account.user
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_header_msg', username: @account.acct) redirect_to admin_account_path(@account.id)
end end
private private

View File

@@ -71,7 +71,7 @@ class Admin::AnnouncementsController < Admin::BaseController
private private
def set_announcements def set_announcements
@announcements = AnnouncementFilter.new(filter_params).results.reverse_chronological.page(params[:page]) @announcements = AnnouncementFilter.new(filter_params).results.page(params[:page])
end end
def set_announcement def set_announcement

View File

@@ -29,7 +29,6 @@ module Admin
@domain_block = existing_domain_block @domain_block = existing_domain_block
@domain_block.update(resource_params) @domain_block.update(resource_params)
end end
if @domain_block.save if @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
@@ -41,7 +40,7 @@ module Admin
end end
def update def update
authorize :domain_block, :update? authorize :domain_block, :create?
@domain_block.update(update_params) @domain_block.update(update_params)
@@ -49,7 +48,7 @@ module Admin
if @domain_block.save if @domain_block.save
DomainBlockWorker.perform_async(@domain_block.id, severity_changed) DomainBlockWorker.perform_async(@domain_block.id, severity_changed)
log_action :update, @domain_block log_action :create, @domain_block
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg') redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
else else
render :edit render :edit
@@ -74,11 +73,11 @@ module Admin
end end
def update_params def update_params
params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate) params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment)
end end
def resource_params def resource_params
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate) params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment)
end end
end end
end end

View File

@@ -27,7 +27,7 @@ module Admin
ips = [] ips = []
Resolv::DNS.open do |dns| Resolv::DNS.open do |dns|
dns.timeouts = 5 dns.timeouts = 1
hostnames = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s } hostnames = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s }

View File

@@ -2,31 +2,65 @@
module Admin module Admin
class InstancesController < BaseController class InstancesController < BaseController
before_action :set_instances, only: :index before_action :set_domain_block, only: :show
before_action :set_domain_allow, only: :show
before_action :set_instance, only: :show before_action :set_instance, only: :show
def index def index
authorize :instance, :index? authorize :instance, :index?
@instances = ordered_instances
end end
def show def show
authorize :instance, :show? authorize :instance, :show?
@following_count = Follow.where(account: Account.where(domain: params[:id])).count
@followers_count = Follow.where(target_account: Account.where(domain: params[:id])).count
@reports_count = Report.where(target_account: Account.where(domain: params[:id])).count
@blocks_count = Block.where(target_account: Account.where(domain: params[:id])).count
@available = DeliveryFailureTracker.available?(params[:id])
@media_storage = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size)
@private_comment = @domain_block&.private_comment
@public_comment = @domain_block&.public_comment
end end
private private
def set_instance def set_domain_block
@instance = Instance.find(params[:id]) @domain_block = DomainBlock.rule_for(params[:id])
end end
def set_instances def set_domain_allow
@instances = filtered_instances.page(params[:page]) @domain_allow = DomainAllow.rule_for(params[:id])
end
def set_instance
resource = Account.by_domain_accounts.find_by(domain: params[:id])
resource ||= @domain_block
resource ||= @domain_allow
if resource
@instance = Instance.new(resource)
else
not_found
end
end end
def filtered_instances def filtered_instances
InstanceFilter.new(whitelist_mode? ? { allowed: true } : filter_params).results InstanceFilter.new(whitelist_mode? ? { allowed: true } : filter_params).results
end end
def paginated_instances
filtered_instances.page(params[:page])
end
helper_method :paginated_instances
def ordered_instances
paginated_instances.map { |resource| Instance.new(resource) }
end
def filter_params def filter_params
params.slice(*InstanceFilter::KEYS).permit(*InstanceFilter::KEYS) params.slice(*InstanceFilter::KEYS).permit(*InstanceFilter::KEYS)
end end

View File

@@ -1,56 +0,0 @@
# frozen_string_literal: true
module Admin
class IpBlocksController < BaseController
def index
authorize :ip_block, :index?
@ip_blocks = IpBlock.page(params[:page])
@form = Form::IpBlockBatch.new
end
def new
authorize :ip_block, :create?
@ip_block = IpBlock.new(ip: '', severity: :no_access, expires_in: 1.year)
end
def create
authorize :ip_block, :create?
@ip_block = IpBlock.new(resource_params)
if @ip_block.save
log_action :create, @ip_block
redirect_to admin_ip_blocks_path, notice: I18n.t('admin.ip_blocks.created_msg')
else
render :new
end
end
def batch
@form = Form::IpBlockBatch.new(form_ip_block_batch_params.merge(current_account: current_account, action: action_from_button))
@form.save
rescue ActionController::ParameterMissing
flash[:alert] = I18n.t('admin.ip_blocks.no_ip_block_selected')
rescue Mastodon::NotPermittedError
flash[:alert] = I18n.t('admin.custom_emojis.not_permitted')
ensure
redirect_to admin_ip_blocks_path
end
private
def resource_params
params.require(:ip_block).permit(:ip, :severity, :comment, :expires_in)
end
def action_from_button
'delete' if params[:delete]
end
def form_ip_block_batch_params
params.require(:form_ip_block_batch).permit(ip_block_ids: [])
end
end
end

View File

@@ -14,7 +14,7 @@ module Admin
@statuses = @account.statuses.where(visibility: [:public, :unlisted]) @statuses = @account.statuses.where(visibility: [:public, :unlisted])
if params[:media] if params[:media]
account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).group(:status_id) account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).distinct
@statuses.merge!(Status.where(id: account_media_status_ids)) @statuses.merge!(Status.where(id: account_media_status_ids))
end end

View File

@@ -40,7 +40,7 @@ class Api::BaseController < ApplicationController
render json: { error: 'This action is not allowed' }, status: 403 render json: { error: 'This action is not allowed' }, status: 403
end end
rescue_from Mastodon::RaceConditionError, Seahorse::Client::NetworkingError, Stoplight::Error::RedLight do rescue_from Mastodon::RaceConditionError do
render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503 render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503
end end
@@ -71,7 +71,6 @@ class Api::BaseController < ApplicationController
def limit_param(default_limit) def limit_param(default_limit)
return default_limit unless params[:limit] return default_limit unless params[:limit]
[params[:limit].to_i.abs, default_limit * 2].min [params[:limit].to_i.abs, default_limit * 2].min
end end
@@ -96,14 +95,14 @@ class Api::BaseController < ApplicationController
def require_user! def require_user!
if !current_user if !current_user
render json: { error: 'This method requires an authenticated user' }, status: 422 render json: { error: 'This method requires an authenticated user' }, status: 422
elsif current_user.disabled?
render json: { error: 'Your login is currently disabled' }, status: 403
elsif !current_user.confirmed? elsif !current_user.confirmed?
render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403 render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403
elsif !current_user.approved? elsif !current_user.approved?
render json: { error: 'Your login is currently pending approval' }, status: 403 render json: { error: 'Your login is currently pending approval' }, status: 403
elsif !current_user.functional?
render json: { error: 'Your login is currently disabled' }, status: 403
else else
update_user_sign_in set_user_activity
end end
end end

View File

@@ -1,22 +0,0 @@
# frozen_string_literal: true
class Api::V1::Accounts::FeaturedTagsController < Api::BaseController
before_action :set_account
before_action :set_featured_tags
respond_to :json
def index
render json: @featured_tags, each_serializer: REST::FeaturedTagSerializer
end
private
def set_account
@account = Account.find(params[:account_id])
end
def set_featured_tags
@featured_tags = @account.suspended? ? [] : @account.featured_tags
end
end

View File

@@ -25,7 +25,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
end end
def hide_results? def hide_results?
@account.suspended? || (@account.hides_followers? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account)) (@account.hides_followers? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
end end
def default_accounts def default_accounts

View File

@@ -25,7 +25,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
end end
def hide_results? def hide_results?
@account.suspended? || (@account.hides_following? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account)) (@account.hides_following? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
end end
def default_accounts def default_accounts

View File

@@ -5,7 +5,7 @@ class Api::V1::Accounts::IdentityProofsController < Api::BaseController
before_action :set_account before_action :set_account
def index def index
@proofs = @account.suspended? ? [] : @account.identity_proofs.active @proofs = @account.identity_proofs.active
render json: @proofs, each_serializer: REST::IdentityProofSerializer render json: @proofs, each_serializer: REST::IdentityProofSerializer
end end

View File

@@ -6,7 +6,7 @@ class Api::V1::Accounts::ListsController < Api::BaseController
before_action :set_account before_action :set_account
def index def index
@lists = @account.suspended? ? [] : @account.lists.where(account: current_account) @lists = @account.lists.where(account: current_account)
render json: @lists, each_serializer: REST::ListSerializer render json: @lists, each_serializer: REST::ListSerializer
end end

View File

@@ -5,7 +5,7 @@ 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.where(id: account_ids).select('id')
# .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 @accounts = accounts.index_by(&:id).values_at(*account_ids).compact

View File

@@ -18,10 +18,14 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
end end
def load_statuses def load_statuses
@account.suspended? ? [] : cached_account_statuses cached_account_statuses
end end
def cached_account_statuses def cached_account_statuses
cache_collection account_statuses, Status
end
def account_statuses
statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses
statuses.merge!(only_media_scope) if truthy_param?(:only_media) statuses.merge!(only_media_scope) if truthy_param?(:only_media)
@@ -29,12 +33,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs) statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs)
statuses.merge!(hashtag_scope) if params[:tagged].present? statuses.merge!(hashtag_scope) if params[:tagged].present?
cache_collection_paginated_by_id( statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
statuses,
Status,
limit_param(DEFAULT_STATUSES_LIMIT),
params_slice(:max_id, :since_id, :min_id)
)
end end
def permitted_account_statuses def permitted_account_statuses
@@ -42,7 +41,17 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
end end
def only_media_scope def only_media_scope
Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id) Status.where(id: account_media_status_ids)
end
def account_media_status_ids
# `SELECT DISTINCT id, updated_at` is too slow, so pluck ids at first, and then select id, updated_at with ids.
# Also, Avoid getting slow by not narrowing down by `statuses.account_id`.
# When narrowing down by `statuses.account_id`, `index_statuses_20180106` will be used
# and the table will be joined by `Merge Semi Join`, so the query will be slow.
@account.statuses.joins(:media_attachments).merge(@account.media_attachments).permitted_for(@account, current_account)
.paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id])
.reorder(id: :desc).distinct(:id).pluck(:id)
end end
def pinned_scope def pinned_scope

View File

@@ -9,6 +9,7 @@ class Api::V1::AccountsController < Api::BaseController
before_action :require_user!, except: [:show, :create] before_action :require_user!, except: [:show, :create]
before_action :set_account, except: [:create] before_action :set_account, except: [:create]
before_action :check_account_suspension, only: [:show]
before_action :check_enabled_registrations, only: [:create] before_action :check_enabled_registrations, only: [:create]
skip_before_action :require_authenticated_user!, only: :create skip_before_action :require_authenticated_user!, only: :create
@@ -20,7 +21,7 @@ class Api::V1::AccountsController < Api::BaseController
end end
def create def create
token = AppSignUpService.new.call(doorkeeper_token.application, request.remote_ip, account_params) token = AppSignUpService.new.call(doorkeeper_token.application, account_params)
response = Doorkeeper::OAuth::TokenResponse.new(token) response = Doorkeeper::OAuth::TokenResponse.new(token)
headers.merge!(response.headers) headers.merge!(response.headers)
@@ -30,8 +31,9 @@ class Api::V1::AccountsController < Api::BaseController
end end
def follow def follow
follow = FollowService.new.call(current_user.account, @account, reblogs: params.key?(:reblogs) ? truthy_param?(:reblogs) : nil, notify: params.key?(:notify) ? truthy_param?(:notify) : nil, with_rate_limit: true) FollowService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs), with_rate_limit: true)
options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: follow.show_reblogs?, notify: follow.notify? } }, requested_map: { @account.id => false } }
options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, requested_map: { @account.id => false } }
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(options) render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(options)
end end
@@ -42,7 +44,7 @@ class Api::V1::AccountsController < Api::BaseController
end end
def mute def mute
MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), duration: (params[:duration] || 0)) MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications))
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
end end
@@ -71,6 +73,10 @@ class Api::V1::AccountsController < Api::BaseController
AccountRelationshipsPresenter.new([@account.id], current_user.account_id, options) AccountRelationshipsPresenter.new([@account.id], current_user.account_id, options)
end end
def check_account_suspension
gone if @account.suspended?
end
def account_params def account_params
params.permit(:username, :email, :password, :agreement, :locale, :reason) params.permit(:username, :email, :password, :agreement, :locale, :reason)
end end

View File

@@ -22,7 +22,6 @@ class Api::V1::Admin::AccountsController < Api::BaseController
active active
pending pending
disabled disabled
sensitized
silenced silenced
suspended suspended
username username
@@ -59,20 +58,7 @@ class Api::V1::Admin::AccountsController < Api::BaseController
def reject def reject
authorize @account.user, :reject? authorize @account.user, :reject?
DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false) SuspendAccountService.new.call(@account, reserve_email: false, reserve_username: false)
render json: @account, serializer: REST::Admin::AccountSerializer
end
def destroy
authorize @account, :destroy?
Admin::AccountDeletionWorker.perform_async(@account.id)
render json: @account, serializer: REST::Admin::AccountSerializer
end
def unsensitive
authorize @account, :unsensitive?
@account.unsensitize!
log_action :unsensitive, @account
render json: @account, serializer: REST::Admin::AccountSerializer render json: @account, serializer: REST::Admin::AccountSerializer
end end
@@ -86,7 +72,6 @@ class Api::V1::Admin::AccountsController < Api::BaseController
def unsuspend def unsuspend
authorize @account, :unsuspend? authorize @account, :unsuspend?
@account.unsuspend! @account.unsuspend!
Admin::UnsuspensionWorker.perform_async(@account.id)
log_action :unsuspend, @account log_action :unsuspend, @account
render json: @account, serializer: REST::Admin::AccountSerializer render json: @account, serializer: REST::Admin::AccountSerializer
end end
@@ -94,7 +79,7 @@ class Api::V1::Admin::AccountsController < Api::BaseController
private private
def set_accounts def set_accounts
@accounts = filtered_accounts.order(id: :desc).includes(user: [:invite_request, :invite]).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) @accounts = filtered_accounts.order(id: :desc).includes(user: [:invite_request, :invite]).paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end end
def set_account def set_account

View File

@@ -63,7 +63,7 @@ class Api::V1::Admin::ReportsController < Api::BaseController
private private
def set_reports def set_reports
@reports = filtered_reports.order(id: :desc).with_accounts.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) @reports = filtered_reports.order(id: :desc).with_accounts.paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end end
def set_report def set_report

View File

@@ -18,8 +18,6 @@ class Api::V1::BlocksController < Api::BaseController
def paginated_blocks def paginated_blocks
@paginated_blocks ||= Block.eager_load(target_account: :account_stat) @paginated_blocks ||= Block.eager_load(target_account: :account_stat)
.joins(:target_account)
.merge(Account.without_suspended)
.where(account: current_account) .where(account: current_account)
.paginate_by_max_id( .paginate_by_max_id(
limit_param(DEFAULT_ACCOUNTS_LIMIT), limit_param(DEFAULT_ACCOUNTS_LIMIT),

View File

@@ -17,11 +17,14 @@ class Api::V1::BookmarksController < Api::BaseController
end end
def cached_bookmarks def cached_bookmarks
cache_collection(results.map(&:status), Status) cache_collection(
Status.reorder(nil).joins(:bookmarks).merge(results),
Status
)
end end
def results def results
@_results ||= account_bookmarks.eager_load(:status).to_a_paginated_by_id( @_results ||= account_bookmarks.paginate_by_id(
limit_param(DEFAULT_STATUSES_LIMIT), limit_param(DEFAULT_STATUSES_LIMIT),
params_slice(:max_id, :since_id, :min_id) params_slice(:max_id, :since_id, :min_id)
) )

View File

@@ -32,7 +32,7 @@ class Api::V1::ConversationsController < Api::BaseController
def paginated_conversations def paginated_conversations
AccountConversation.where(account: current_account) AccountConversation.where(account: current_account)
.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) .paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end end
def insert_pagination_headers def insert_pagination_headers

View File

@@ -26,7 +26,7 @@ class Api::V1::Crypto::EncryptedMessagesController < Api::BaseController
end end
def set_encrypted_messages def set_encrypted_messages
@encrypted_messages = @current_device.encrypted_messages.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) @encrypted_messages = @current_device.encrypted_messages.paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end end
def insert_pagination_headers def insert_pagination_headers

View File

@@ -25,7 +25,7 @@ class Api::V1::EndorsementsController < Api::BaseController
end end
def endorsed_accounts def endorsed_accounts
current_account.endorsed_accounts.includes(:account_stat).without_suspended current_account.endorsed_accounts.includes(:account_stat)
end end
def insert_pagination_headers def insert_pagination_headers

View File

@@ -17,11 +17,14 @@ class Api::V1::FavouritesController < Api::BaseController
end end
def cached_favourites def cached_favourites
cache_collection(results.map(&:status), Status) cache_collection(
Status.reorder(nil).joins(:favourites).merge(results),
Status
)
end end
def results def results
@_results ||= account_favourites.eager_load(:status).to_a_paginated_by_id( @_results ||= account_favourites.paginate_by_id(
limit_param(DEFAULT_STATUSES_LIMIT), limit_param(DEFAULT_STATUSES_LIMIT),
params_slice(:max_id, :since_id, :min_id) params_slice(:max_id, :since_id, :min_id)
) )

View File

@@ -3,15 +3,15 @@
class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index
before_action :require_user! before_action :require_user!
before_action :set_recently_used_tags, only: :index before_action :set_most_used_tags, only: :index
def index def index
render json: @recently_used_tags, each_serializer: REST::TagSerializer render json: @most_used_tags, each_serializer: REST::TagSerializer
end end
private private
def set_recently_used_tags def set_most_used_tags
@recently_used_tags = Tag.recently_used(current_account).where.not(id: current_account.featured_tags).limit(10) @most_used_tags = Tag.most_used(current_account).where.not(id: current_account.featured_tags).limit(10)
end end
end end

View File

@@ -13,7 +13,7 @@ class Api::V1::FollowRequestsController < Api::BaseController
def authorize def authorize
AuthorizeFollowService.new.call(account, current_account) AuthorizeFollowService.new.call(account, current_account)
NotifyService.new.call(current_account, :follow, Follow.find_by(account: account, target_account: current_account)) NotifyService.new.call(current_account, Follow.find_by(account: account, target_account: current_account))
render json: account, serializer: REST::RelationshipSerializer, relationships: relationships render json: account, serializer: REST::RelationshipSerializer, relationships: relationships
end end
@@ -37,7 +37,7 @@ class Api::V1::FollowRequestsController < Api::BaseController
end end
def default_accounts def default_accounts
Account.without_suspended.includes(:follow_requests, :account_stat).references(:follow_requests) Account.includes(:follow_requests, :account_stat).references(:follow_requests)
end end
def paginated_follow_requests def paginated_follow_requests

View File

@@ -8,7 +8,7 @@ class Api::V1::Instances::PeersController < Api::BaseController
def index def index
expires_in 1.day, public: true expires_in 1.day, public: true
render_with_cache(expires_in: 1.day) { Instance.where.not(domain: DomainBlock.select(:domain)).pluck(:domain) } render_with_cache(expires_in: 1.day) { Account.remote.domains }
end end
private private

View File

@@ -37,9 +37,9 @@ class Api::V1::Lists::AccountsController < Api::BaseController
def load_accounts def load_accounts
if unlimited? if unlimited?
@list.accounts.without_suspended.includes(:account_stat).all @list.accounts.includes(:account_stat).all
else else
@list.accounts.without_suspended.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]) @list.accounts.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
end end
end end

View File

@@ -38,6 +38,6 @@ class Api::V1::ListsController < Api::BaseController
end end
def list_params def list_params
params.permit(:title, :replies_policy) params.permit(:title)
end end
end end

View File

@@ -7,7 +7,7 @@ class Api::V1::MutesController < Api::BaseController
def index def index
@accounts = load_accounts @accounts = load_accounts
render json: @accounts, each_serializer: REST::MutedAccountSerializer render json: @accounts, each_serializer: REST::AccountSerializer
end end
private private
@@ -18,8 +18,6 @@ class Api::V1::MutesController < Api::BaseController
def paginated_mutes def paginated_mutes
@paginated_mutes ||= Mute.eager_load(:target_account) @paginated_mutes ||= Mute.eager_load(:target_account)
.joins(:target_account)
.merge(Account.without_suspended)
.where(account: current_account) .where(account: current_account)
.paginate_by_max_id( .paginate_by_max_id(
limit_param(DEFAULT_ACCOUNTS_LIMIT), limit_param(DEFAULT_ACCOUNTS_LIMIT),

View File

@@ -14,7 +14,7 @@ class Api::V1::NotificationsController < Api::BaseController
end end
def show def show
@notification = current_account.notifications.without_suspended.find(params[:id]) @notification = current_account.notifications.find(params[:id])
render json: @notification, serializer: REST::NotificationSerializer render json: @notification, serializer: REST::NotificationSerializer
end end
@@ -31,16 +31,18 @@ class Api::V1::NotificationsController < Api::BaseController
private private
def load_notifications def load_notifications
cache_collection_paginated_by_id( cache_collection paginated_notifications, Notification
browserable_account_notifications, end
Notification,
def paginated_notifications
browserable_account_notifications.paginate_by_id(
limit_param(DEFAULT_NOTIFICATIONS_LIMIT), limit_param(DEFAULT_NOTIFICATIONS_LIMIT),
params_slice(:max_id, :since_id, :min_id) params_slice(:max_id, :since_id, :min_id)
) )
end end
def browserable_account_notifications def browserable_account_notifications
current_account.notifications.without_suspended.browserable(exclude_types, from_account) current_account.notifications.browserable(exclude_types, from_account)
end end
def target_statuses_from_notifications def target_statuses_from_notifications

View File

@@ -52,6 +52,6 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
def data_params def data_params
return {} if params[:data].blank? return {} if params[:data].blank?
params.require(:data).permit(alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll, :status]) params.require(:data).permit(alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll])
end end
end end

View File

@@ -32,7 +32,7 @@ class Api::V1::ScheduledStatusesController < Api::BaseController
private private
def set_statuses def set_statuses
@statuses = current_account.scheduled_statuses.to_a_paginated_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id)) @statuses = current_account.scheduled_statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
end end
def set_status def set_status

View File

@@ -5,7 +5,7 @@ class Api::V1::Statuses::BookmarksController < Api::BaseController
before_action -> { doorkeeper_authorize! :write, :'write:bookmarks' } before_action -> { doorkeeper_authorize! :write, :'write:bookmarks' }
before_action :require_user! before_action :require_user!
before_action :set_status, only: [:create] before_action :set_status
def create def create
current_account.bookmarks.find_or_create_by!(account: current_account, status: @status) current_account.bookmarks.find_or_create_by!(account: current_account, status: @status)
@@ -13,20 +13,10 @@ class Api::V1::Statuses::BookmarksController < Api::BaseController
end end
def destroy def destroy
bookmark = current_account.bookmarks.find_by(status_id: params[:status_id]) bookmark = current_account.bookmarks.find_by(status: @status)
if bookmark
@status = bookmark.status
else
@status = Status.find(params[:status_id])
authorize @status, :show?
end
bookmark&.destroy! bookmark&.destroy!
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, bookmarks_map: { @status.id => false }) render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, bookmarks_map: { @status.id => false })
rescue Mastodon::NotPermittedError
not_found
end end
private private

View File

@@ -22,7 +22,6 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
def default_accounts def default_accounts
Account Account
.without_suspended
.includes(:favourites, :account_stat) .includes(:favourites, :account_stat)
.references(:favourites) .references(:favourites)
.where(favourites: { status_id: @status.id }) .where(favourites: { status_id: @status.id })

View File

@@ -5,7 +5,7 @@ class Api::V1::Statuses::FavouritesController < Api::BaseController
before_action -> { doorkeeper_authorize! :write, :'write:favourites' } before_action -> { doorkeeper_authorize! :write, :'write:favourites' }
before_action :require_user! before_action :require_user!
before_action :set_status, only: [:create] before_action :set_status
def create def create
FavouriteService.new.call(current_account, @status) FavouriteService.new.call(current_account, @status)
@@ -13,19 +13,8 @@ class Api::V1::Statuses::FavouritesController < Api::BaseController
end end
def destroy def destroy
fav = current_account.favourites.find_by(status_id: params[:status_id]) UnfavouriteWorker.perform_async(current_account.id, @status.id)
if fav
@status = fav.status
UnfavouriteWorker.perform_async(current_account.id, @status.id)
else
@status = Status.find(params[:status_id])
authorize @status, :show?
end
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => false }) render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => false })
rescue Mastodon::NotPermittedError
not_found
end end
private private

View File

@@ -21,7 +21,7 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
end end
def default_accounts def default_accounts
Account.without_suspended.includes(:statuses, :account_stat).references(:statuses) Account.includes(:statuses, :account_stat).references(:statuses)
end end
def paginated_statuses def paginated_statuses

View File

@@ -16,29 +16,30 @@ class Api::V1::Timelines::PublicController < Api::BaseController
end end
def load_statuses def load_statuses
cached_public_statuses_page cached_public_statuses
end end
def cached_public_statuses_page def cached_public_statuses
cache_collection(public_statuses, Status) cache_collection public_statuses, Status
end end
def public_statuses def public_statuses
public_feed.get( statuses = public_timeline_statuses.paginate_by_id(
limit_param(DEFAULT_STATUSES_LIMIT), limit_param(DEFAULT_STATUSES_LIMIT),
params[:max_id], params_slice(:max_id, :since_id, :min_id)
params[:since_id],
params[:min_id]
) )
if truthy_param?(:only_media)
# `SELECT DISTINCT id, updated_at` is too slow, so pluck ids at first, and then select id, updated_at with ids.
status_ids = statuses.joins(:media_attachments).distinct(:id).pluck(:id)
statuses.where(id: status_ids)
else
statuses
end
end end
def public_feed def public_timeline_statuses
PublicFeed.new( Status.as_public_timeline(current_account, truthy_param?(:remote) ? :remote : truthy_param?(:local))
current_account,
local: truthy_param?(:local),
remote: truthy_param?(:remote),
only_media: truthy_param?(:only_media)
)
end end
def insert_pagination_headers def insert_pagination_headers

View File

@@ -20,29 +20,30 @@ class Api::V1::Timelines::TagController < Api::BaseController
end end
def cached_tagged_statuses def cached_tagged_statuses
@tag.nil? ? [] : cache_collection(tag_timeline_statuses, Status) cache_collection tagged_statuses, Status
end
def tagged_statuses
if @tag.nil?
[]
else
statuses = tag_timeline_statuses.paginate_by_id(
limit_param(DEFAULT_STATUSES_LIMIT),
params_slice(:max_id, :since_id, :min_id)
)
if truthy_param?(:only_media)
# `SELECT DISTINCT id, updated_at` is too slow, so pluck ids at first, and then select id, updated_at with ids.
status_ids = statuses.joins(:media_attachments).distinct(:id).pluck(:id)
statuses.where(id: status_ids)
else
statuses
end
end
end end
def tag_timeline_statuses def tag_timeline_statuses
tag_feed.get( HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none), current_account, truthy_param?(:local))
limit_param(DEFAULT_STATUSES_LIMIT),
params[:max_id],
params[:since_id],
params[:min_id]
)
end
def tag_feed
TagFeed.new(
@tag,
current_account,
any: params[:any],
all: params[:all],
none: params[:none],
local: truthy_param?(:local),
remote: truthy_param?(:remote),
only_media: truthy_param?(:only_media)
)
end end
def insert_pagination_headers def insert_pagination_headers

View File

@@ -22,7 +22,6 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
reblog: alerts_enabled, reblog: alerts_enabled,
mention: alerts_enabled, mention: alerts_enabled,
poll: alerts_enabled, poll: alerts_enabled,
status: alerts_enabled,
}, },
} }
@@ -58,6 +57,6 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
end end
def data_params def data_params
@data_params ||= params.require(:data).permit(alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll, :status]) @data_params ||= params.require(:data).permit(alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll])
end end
end end

View File

@@ -28,7 +28,7 @@ class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :not_found rescue_from ActiveRecord::RecordNotFound, with: :not_found
rescue_from Mastodon::NotPermittedError, with: :forbidden rescue_from Mastodon::NotPermittedError, with: :forbidden
rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error
rescue_from Mastodon::RaceConditionError, Seahorse::Client::NetworkingError, Stoplight::Error::RedLight, with: :service_unavailable rescue_from Mastodon::RaceConditionError, with: :service_unavailable
rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests
before_action :store_current_location, except: :raise_not_found, unless: :devise_controller? before_action :store_current_location, except: :raise_not_found, unless: :devise_controller?

View File

@@ -2,7 +2,6 @@
class Auth::RegistrationsController < Devise::RegistrationsController class Auth::RegistrationsController < Devise::RegistrationsController
include Devise::Controllers::Rememberable include Devise::Controllers::Rememberable
include RegistrationSpamConcern
layout :determine_layout layout :determine_layout
@@ -14,7 +13,6 @@ class Auth::RegistrationsController < Devise::RegistrationsController
before_action :set_body_classes, only: [:new, :create, :edit, :update] before_action :set_body_classes, only: [:new, :create, :edit, :update]
before_action :require_not_suspended!, only: [:update] before_action :require_not_suspended!, only: [:update]
before_action :set_cache_headers, only: [:edit, :update] before_action :set_cache_headers, only: [:edit, :update]
before_action :set_registration_form_time, only: :new
skip_before_action :require_functional!, only: [:edit, :update] skip_before_action :require_functional!, only: [:edit, :update]
@@ -47,17 +45,16 @@ class Auth::RegistrationsController < Devise::RegistrationsController
def build_resource(hash = nil) def build_resource(hash = nil)
super(hash) super(hash)
resource.locale = I18n.locale resource.locale = I18n.locale
resource.invite_code = params[:invite_code] if resource.invite_code.blank? resource.invite_code = params[:invite_code] if resource.invite_code.blank?
resource.registration_form_time = session[:registration_form_time] resource.current_sign_in_ip = request.remote_ip
resource.sign_up_ip = request.remote_ip
resource.build_account if resource.account.nil? resource.build_account if resource.account.nil?
end end
def configure_sign_up_params def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up) do |u| devise_parameter_sanitizer.permit(:sign_up) do |u|
u.permit({ account_attributes: [:username], invite_request_attributes: [:text] }, :email, :password, :password_confirmation, :invite_code, :agreement, :website, :confirm_password) u.permit({ account_attributes: [:username], invite_request_attributes: [:text] }, :email, :password, :password_confirmation, :invite_code, :agreement)
end end
end end

View File

@@ -7,7 +7,6 @@ class Auth::SessionsController < Devise::SessionsController
skip_before_action :require_no_authentication, only: [:create] skip_before_action :require_no_authentication, only: [:create]
skip_before_action :require_functional! skip_before_action :require_functional!
skip_before_action :update_user_sign_in
include TwoFactorAuthenticationConcern include TwoFactorAuthenticationConcern
include SignInTokenAuthenticationConcern include SignInTokenAuthenticationConcern
@@ -25,7 +24,6 @@ class Auth::SessionsController < Devise::SessionsController
def create def create
super do |resource| super do |resource|
resource.update_sign_in!(request, new_sign_in: true)
remember_me(resource) remember_me(resource)
flash.delete(:notice) flash.delete(:notice)
end end
@@ -39,27 +37,11 @@ class Auth::SessionsController < Devise::SessionsController
store_location_for(:user, tmp_stored_location) if continue_after? store_location_for(:user, tmp_stored_location) if continue_after?
end end
def webauthn_options
user = find_user
if user.webauthn_enabled?
options_for_get = WebAuthn::Credential.options_for_get(
allow: user.webauthn_credentials.pluck(:external_id)
)
session[:webauthn_challenge] = options_for_get.challenge
render json: options_for_get, status: :ok
else
render json: { error: t('webauthn_credentials.not_enabled') }, status: :unauthorized
end
end
protected protected
def find_user def find_user
if session[:attempt_user_id] if session[:attempt_user_id]
User.find_by(id: session[:attempt_user_id]) User.find(session[:attempt_user_id])
else else
user = User.authenticate_with_ldap(user_params) if Devise.ldap_authentication user = User.authenticate_with_ldap(user_params) if Devise.ldap_authentication
user ||= User.authenticate_with_pam(user_params) if Devise.pam_authentication user ||= User.authenticate_with_pam(user_params) if Devise.pam_authentication
@@ -69,7 +51,7 @@ class Auth::SessionsController < Devise::SessionsController
end end
def user_params def user_params
params.require(:user).permit(:email, :password, :otp_attempt, :sign_in_token_attempt, credential: {}) params.require(:user).permit(:email, :password, :otp_attempt, :sign_in_token_attempt)
end end
def after_sign_in_path_for(resource) def after_sign_in_path_for(resource)
@@ -92,7 +74,6 @@ class Auth::SessionsController < Devise::SessionsController
def require_no_authentication def require_no_authentication
super super
# Delete flash message that isn't entirely useful and may be confusing in # Delete flash message that isn't entirely useful and may be confusing in
# most cases because /web doesn't display/clear flash messages. # most cases because /web doesn't display/clear flash messages.
flash.delete(:alert) if flash[:alert] == I18n.t('devise.failure.already_authenticated') flash.delete(:alert) if flash[:alert] == I18n.t('devise.failure.already_authenticated')
@@ -110,30 +91,13 @@ class Auth::SessionsController < Devise::SessionsController
def home_paths(resource) def home_paths(resource)
paths = [about_path] paths = [about_path]
if single_user_mode? && resource.is_a?(User) if single_user_mode? && resource.is_a?(User)
paths << short_account_path(username: resource.account) paths << short_account_path(username: resource.account)
end end
paths paths
end end
def continue_after? def continue_after?
truthy_param?(:continue) truthy_param?(:continue)
end end
def restart_session
clear_attempt_from_session
redirect_to new_user_session_path, alert: I18n.t('devise.failure.timeout')
end
def set_attempt_session(user)
session[:attempt_user_id] = user.id
session[:attempt_user_updated_at] = user.updated_at.to_s
end
def clear_attempt_from_session
session.delete(:attempt_user_id)
session.delete(:attempt_user_updated_at)
end
end end

View File

@@ -29,24 +29,6 @@ module AccountOwnedConcern
end end
def check_account_suspension def check_account_suspension
if @account.suspended_permanently? expires_in(3.minutes, public: true) && gone if @account.suspended?
permanent_suspension_response
elsif @account.suspended? && !skip_temporary_suspension_response?
temporary_suspension_response
end
end
def skip_temporary_suspension_response?
false
end
def permanent_suspension_response
expires_in(3.minutes, public: true)
gone
end
def temporary_suspension_response
expires_in(3.minutes, public: true)
forbidden
end end
end end

View File

@@ -47,8 +47,4 @@ module CacheConcern
raw.map { |item| cached_keys_with_value[item.id] || uncached[item.id] }.compact raw.map { |item| cached_keys_with_value[item.id] || uncached[item.id] }.compact
end end
def cache_collection_paginated_by_id(raw, klass, limit, options)
cache_collection raw.cache_ids.to_a_paginated_by_id(limit, options), klass
end
end end

View File

@@ -32,6 +32,7 @@ module ChallengableConcern
if params.key?(:form_challenge) if params.key?(:form_challenge)
if challenge_passed? if challenge_passed?
session[:challenge_passed_at] = Time.now.utc session[:challenge_passed_at] = Time.now.utc
return
else else
flash.now[:alert] = I18n.t('challenge.invalid_password') flash.now[:alert] = I18n.t('challenge.invalid_password')
render_challenge render_challenge

View File

@@ -5,6 +5,7 @@ module ExportControllerConcern
included do included do
before_action :authenticate_user! before_action :authenticate_user!
before_action :require_not_suspended!
before_action :load_export before_action :load_export
skip_before_action :require_functional! skip_before_action :require_functional!
@@ -29,4 +30,8 @@ module ExportControllerConcern
def export_filename def export_filename
"#{controller_name}.csv" "#{controller_name}.csv"
end end
def require_not_suspended!
forbidden if current_account.suspended?
end
end end

View File

@@ -1,9 +0,0 @@
# frozen_string_literal: true
module RegistrationSpamConcern
extend ActiveSupport::Concern
def set_registration_form_time
session[:registration_form_time] = Time.now.utc
end
end

View File

@@ -18,9 +18,7 @@ module SignInTokenAuthenticationConcern
def authenticate_with_sign_in_token def authenticate_with_sign_in_token
user = self.resource = find_user user = self.resource = find_user
if user.present? && session[:attempt_user_id].present? && session[:attempt_user_updated_at] != user.updated_at.to_s if user_params[:sign_in_token_attempt].present? && session[:attempt_user_id]
restart_session
elsif user_params.key?(:sign_in_token_attempt) && session[:attempt_user_id]
authenticate_with_sign_in_token_attempt(user) authenticate_with_sign_in_token_attempt(user)
elsif user.present? && user.external_or_valid_password?(user_params[:password]) elsif user.present? && user.external_or_valid_password?(user_params[:password])
prompt_for_sign_in_token(user) prompt_for_sign_in_token(user)
@@ -29,7 +27,7 @@ module SignInTokenAuthenticationConcern
def authenticate_with_sign_in_token_attempt(user) def authenticate_with_sign_in_token_attempt(user)
if valid_sign_in_token_attempt?(user) if valid_sign_in_token_attempt?(user)
clear_attempt_from_session session.delete(:attempt_user_id)
remember_me(user) remember_me(user)
sign_in(user) sign_in(user)
else else
@@ -44,10 +42,10 @@ module SignInTokenAuthenticationConcern
UserMailer.sign_in_token(user, request.remote_ip, request.user_agent, Time.now.utc.to_s).deliver_later! UserMailer.sign_in_token(user, request.remote_ip, request.user_agent, Time.now.utc.to_s).deliver_later!
end end
set_attempt_session(user) set_locale do
session[:attempt_user_id] = user.id
@body_classes = 'lighter' @body_classes = 'lighter'
render :sign_in_token
set_locale { render :sign_in_token } end
end end
end end

View File

@@ -76,7 +76,6 @@ module SignatureVerification
raise SignatureVerificationError, 'Signed request date outside acceptable time window' unless matches_time_window? raise SignatureVerificationError, 'Signed request date outside acceptable time window' unless matches_time_window?
verify_signature_strength! verify_signature_strength!
verify_body_digest!
account = account_from_key_id(signature_params['keyId']) account = account_from_key_id(signature_params['keyId'])
@@ -127,21 +126,12 @@ module SignatureVerification
def verify_signature_strength! def verify_signature_strength!
raise SignatureVerificationError, 'Mastodon requires the Date header or (created) pseudo-header to be signed' unless signed_headers.include?('date') || signed_headers.include?('(created)') raise SignatureVerificationError, 'Mastodon requires the Date header or (created) pseudo-header to be signed' unless signed_headers.include?('date') || signed_headers.include?('(created)')
raise SignatureVerificationError, 'Mastodon requires the Digest header or (request-target) pseudo-header to be signed' unless signed_headers.include?(Request::REQUEST_TARGET) || signed_headers.include?('digest') raise SignatureVerificationError, 'Mastodon requires the Digest header or (request-target) pseudo-header to be signed' unless signed_headers.include?(Request::REQUEST_TARGET) || signed_headers.include?('digest')
raise SignatureVerificationError, 'Mastodon requires the Host header to be signed when doing a GET request' if request.get? && !signed_headers.include?('host') raise SignatureVerificationError, 'Mastodon requires the Host header to be signed' unless signed_headers.include?('host')
raise SignatureVerificationError, 'Mastodon requires the Digest header to be signed when doing a POST request' if request.post? && !signed_headers.include?('digest') raise SignatureVerificationError, 'Mastodon requires the Digest header to be signed when doing a POST request' if request.post? && !signed_headers.include?('digest')
end end
def verify_body_digest!
return unless signed_headers.include?('digest')
digests = request.headers['Digest'].split(',').map { |digest| digest.split('=', 2) }.map { |key, value| [key.downcase, value] }
sha256 = digests.assoc('sha-256')
raise SignatureVerificationError, "Mastodon only supports SHA-256 in Digest header. Offered algorithms: #{digests.map(&:first).join(', ')}" if sha256.nil?
raise SignatureVerificationError, "Invalid Digest value. Computed SHA-256 digest: #{body_digest}; given: #{sha256[1]}" if body_digest != sha256[1]
end
def verify_signature(account, signature, compare_signed_string) def verify_signature(account, signature, compare_signed_string)
if account.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), signature, compare_signed_string) if account.keypair.public_key.verify(OpenSSL::Digest::SHA256.new, signature, compare_signed_string)
@signed_request_account = account @signed_request_account = account
@signed_request_account @signed_request_account
end end
@@ -163,6 +153,8 @@ module SignatureVerification
raise SignatureVerificationError, 'Pseudo-header (expires) used but corresponding argument missing' if signature_params['expires'].blank? raise SignatureVerificationError, 'Pseudo-header (expires) used but corresponding argument missing' if signature_params['expires'].blank?
"(expires): #{signature_params['expires']}" "(expires): #{signature_params['expires']}"
elsif signed_header == 'digest'
"digest: #{body_digest}"
else else
"#{signed_header}: #{request.headers[to_header_name(signed_header)]}" "#{signed_header}: #{request.headers[to_header_name(signed_header)]}"
end end
@@ -195,7 +187,7 @@ module SignatureVerification
end end
def body_digest def body_digest
@body_digest ||= Digest::SHA256.base64digest(request_body) "SHA-256=#{Digest::SHA256.base64digest(request_body)}"
end end
def to_header_name(name) def to_header_name(name)

View File

@@ -8,23 +8,7 @@ module TwoFactorAuthenticationConcern
end end
def two_factor_enabled? def two_factor_enabled?
find_user&.two_factor_enabled? find_user&.otp_required_for_login?
end
def valid_webauthn_credential?(user, webauthn_credential)
user_credential = user.webauthn_credentials.find_by!(external_id: webauthn_credential.id)
begin
webauthn_credential.verify(
session[:webauthn_challenge],
public_key: user_credential.public_key,
sign_count: user_credential.sign_count
)
user_credential.update!(sign_count: webauthn_credential.sign_count)
rescue WebAuthn::Error
false
end
end end
def valid_otp_attempt?(user) def valid_otp_attempt?(user)
@@ -37,33 +21,16 @@ module TwoFactorAuthenticationConcern
def authenticate_with_two_factor def authenticate_with_two_factor
user = self.resource = find_user user = self.resource = find_user
if user.present? && session[:attempt_user_id].present? && session[:attempt_user_updated_at] != user.updated_at.to_s if user_params[:otp_attempt].present? && session[:attempt_user_id]
restart_session authenticate_with_two_factor_attempt(user)
elsif user.webauthn_enabled? && user_params.key?(:credential) && session[:attempt_user_id]
authenticate_with_two_factor_via_webauthn(user)
elsif user_params.key?(:otp_attempt) && session[:attempt_user_id]
authenticate_with_two_factor_via_otp(user)
elsif user.present? && user.external_or_valid_password?(user_params[:password]) elsif user.present? && user.external_or_valid_password?(user_params[:password])
prompt_for_two_factor(user) prompt_for_two_factor(user)
end end
end end
def authenticate_with_two_factor_via_webauthn(user) def authenticate_with_two_factor_attempt(user)
webauthn_credential = WebAuthn::Credential.from_get(user_params[:credential])
if valid_webauthn_credential?(user, webauthn_credential)
clear_attempt_from_session
remember_me(user)
sign_in(user)
render json: { redirect_path: root_path }, status: :ok
else
render json: { error: t('webauthn_credentials.invalid_credential') }, status: :unprocessable_entity
end
end
def authenticate_with_two_factor_via_otp(user)
if valid_otp_attempt?(user) if valid_otp_attempt?(user)
clear_attempt_from_session session.delete(:attempt_user_id)
remember_me(user) remember_me(user)
sign_in(user) sign_in(user)
else else
@@ -73,18 +40,10 @@ module TwoFactorAuthenticationConcern
end end
def prompt_for_two_factor(user) def prompt_for_two_factor(user)
set_attempt_session(user) set_locale do
session[:attempt_user_id] = user.id
@body_classes = 'lighter' @body_classes = 'lighter'
@webauthn_enabled = user.webauthn_enabled? render :two_factor
@scheme_type = begin
if user.webauthn_enabled? && user_params[:otp_attempt].blank?
'webauthn'
else
'totp'
end
end end
set_locale { render :two_factor }
end end
end end

View File

@@ -6,13 +6,14 @@ module UserTrackingConcern
UPDATE_SIGN_IN_HOURS = 24 UPDATE_SIGN_IN_HOURS = 24
included do included do
before_action :update_user_sign_in before_action :set_user_activity
end end
private private
def update_user_sign_in def set_user_activity
current_user.update_sign_in!(request) if user_needs_sign_in_update? return unless user_needs_sign_in_update?
current_user.update_tracked_fields!(request)
end end
def user_needs_sign_in_update? def user_needs_sign_in_update?

View File

@@ -9,7 +9,7 @@ class FiltersController < ApplicationController
before_action :set_body_classes before_action :set_body_classes
def index def index
@filters = current_account.custom_filters.order(:phrase) @filters = current_account.custom_filters
end end
def new def new

View File

@@ -52,14 +52,6 @@ class FollowerAccountsController < ApplicationController
account_followers_url(@account, page: page) unless page.nil? account_followers_url(@account, page: page) unless page.nil?
end end
def next_page_url
page_url(follows.next_page) if follows.respond_to?(:next_page)
end
def prev_page_url
page_url(follows.prev_page) if follows.respond_to?(:prev_page)
end
def collection_presenter def collection_presenter
if page_requested? if page_requested?
ActivityPub::CollectionPresenter.new( ActivityPub::CollectionPresenter.new(
@@ -68,8 +60,8 @@ class FollowerAccountsController < ApplicationController
size: @account.followers_count, size: @account.followers_count,
items: follows.map { |f| ActivityPub::TagManager.instance.uri_for(f.account) }, items: follows.map { |f| ActivityPub::TagManager.instance.uri_for(f.account) },
part_of: account_followers_url(@account), part_of: account_followers_url(@account),
next: next_page_url, next: page_url(follows.next_page),
prev: prev_page_url prev: page_url(follows.prev_page)
) )
else else
ActivityPub::CollectionPresenter.new( ActivityPub::CollectionPresenter.new(

View File

@@ -52,14 +52,6 @@ class FollowingAccountsController < ApplicationController
account_following_index_url(@account, page: page) unless page.nil? account_following_index_url(@account, page: page) unless page.nil?
end end
def next_page_url
page_url(follows.next_page) if follows.respond_to?(:next_page)
end
def prev_page_url
page_url(follows.prev_page) if follows.respond_to?(:prev_page)
end
def collection_presenter def collection_presenter
if page_requested? if page_requested?
ActivityPub::CollectionPresenter.new( ActivityPub::CollectionPresenter.new(
@@ -68,8 +60,8 @@ class FollowingAccountsController < ApplicationController
size: @account.following_count, size: @account.following_count,
items: follows.map { |f| ActivityPub::TagManager.instance.uri_for(f.target_account) }, items: follows.map { |f| ActivityPub::TagManager.instance.uri_for(f.target_account) },
part_of: account_following_index_url(@account), part_of: account_following_index_url(@account),
next: next_page_url, next: page_url(follows.next_page),
prev: prev_page_url prev: page_url(follows.prev_page)
) )
else else
ActivityPub::CollectionPresenter.new( ActivityPub::CollectionPresenter.new(

View File

@@ -17,6 +17,6 @@ class InstanceActorsController < ApplicationController
end end
def restrict_fields_to def restrict_fields_to
%i(id type preferred_username inbox outbox public_key endpoints url manually_approves_followers) %i(id type preferred_username inbox public_key endpoints url manually_approves_followers)
end end
end end

View File

@@ -5,7 +5,6 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
before_action :store_current_location before_action :store_current_location
before_action :authenticate_resource_owner! before_action :authenticate_resource_owner!
before_action :require_not_suspended!, only: :destroy
before_action :set_body_classes before_action :set_body_classes
skip_before_action :require_functional! skip_before_action :require_functional!
@@ -26,8 +25,4 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
def store_current_location def store_current_location
store_location_for(:user, request.url) store_location_for(:user, request.url)
end end
def require_not_suspended!
forbidden if current_account.suspended?
end
end end

View File

@@ -5,7 +5,6 @@ class RelationshipsController < ApplicationController
before_action :authenticate_user! before_action :authenticate_user!
before_action :set_accounts, only: :show before_action :set_accounts, only: :show
before_action :set_relationships, only: :show
before_action :set_body_classes before_action :set_body_classes
helper_method :following_relationship?, :followed_by_relationship?, :mutual_relationship? helper_method :following_relationship?, :followed_by_relationship?, :mutual_relationship?
@@ -29,10 +28,6 @@ class RelationshipsController < ApplicationController
@accounts = RelationshipFilter.new(current_account, filter_params).results.page(params[:page]).per(40) @accounts = RelationshipFilter.new(current_account, filter_params).results.page(params[:page]).per(40)
end end
def set_relationships
@relationships = AccountRelationshipsPresenter.new(@accounts.pluck(:id), current_user.account_id)
end
def form_account_batch_params def form_account_batch_params
params.require(:form_account_batch).permit(:action, account_ids: []) params.require(:form_account_batch).permit(:action, account_ids: [])
end end
@@ -54,9 +49,7 @@ class RelationshipsController < ApplicationController
end end
def action_from_button def action_from_button
if params[:follow] if params[:unfollow]
'follow'
elsif params[:unfollow]
'unfollow' 'unfollow'
elsif params[:remove_from_followers] elsif params[:remove_from_followers]
'remove_from_followers' 'remove_from_followers'

View File

@@ -1,9 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class Settings::AliasesController < Settings::BaseController class Settings::AliasesController < Settings::BaseController
skip_before_action :require_functional! layout 'admin'
before_action :require_not_suspended! before_action :authenticate_user!
before_action :set_aliases, except: :destroy before_action :set_aliases, except: :destroy
before_action :set_alias, only: :destroy before_action :set_alias, only: :destroy

View File

@@ -1,6 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class Settings::ApplicationsController < Settings::BaseController class Settings::ApplicationsController < Settings::BaseController
layout 'admin'
before_action :authenticate_user!
before_action :set_application, only: [:show, :update, :destroy, :regenerate] before_action :set_application, only: [:show, :update, :destroy, :regenerate]
before_action :prepare_scopes, only: [:create, :update] before_action :prepare_scopes, only: [:create, :update]

View File

@@ -1,9 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
class Settings::BaseController < ApplicationController class Settings::BaseController < ApplicationController
layout 'admin'
before_action :authenticate_user!
before_action :set_body_classes before_action :set_body_classes
before_action :set_cache_headers before_action :set_cache_headers
@@ -16,8 +13,4 @@ class Settings::BaseController < ApplicationController
def set_cache_headers def set_cache_headers
response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate' response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
end end
def require_not_suspended!
forbidden if current_account.suspended?
end
end end

View File

@@ -1,10 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
class Settings::DeletesController < Settings::BaseController class Settings::DeletesController < Settings::BaseController
skip_before_action :require_functional! layout 'admin'
before_action :require_not_suspended!
before_action :check_enabled_deletion before_action :check_enabled_deletion
before_action :authenticate_user!
before_action :require_not_suspended!
skip_before_action :require_functional!
def show def show
@confirmation = Form::DeleteConfirmation.new @confirmation = Form::DeleteConfirmation.new
@@ -42,8 +45,8 @@ class Settings::DeletesController < Settings::BaseController
end end
def destroy_account! def destroy_account!
current_account.suspend!(origin: :local) current_account.suspend!
AccountDeletionWorker.perform_async(current_user.account_id) Admin::SuspensionWorker.perform_async(current_user.account_id, true)
sign_out sign_out
end end
end end

View File

@@ -2,7 +2,7 @@
module Settings module Settings
module Exports module Exports
class BlockedAccountsController < BaseController class BlockedAccountsController < ApplicationController
include ExportControllerConcern include ExportControllerConcern
def index def index

View File

@@ -2,7 +2,7 @@
module Settings module Settings
module Exports module Exports
class BlockedDomainsController < BaseController class BlockedDomainsController < ApplicationController
include ExportControllerConcern include ExportControllerConcern
def index def index

View File

@@ -1,19 +0,0 @@
# frozen_string_literal: true
module Settings
module Exports
class BookmarksController < BaseController
include ExportControllerConcern
def index
send_export_file
end
private
def export_data
@export.to_bookmarks_csv
end
end
end
end

View File

@@ -2,7 +2,7 @@
module Settings module Settings
module Exports module Exports
class FollowingAccountsController < BaseController class FollowingAccountsController < ApplicationController
include ExportControllerConcern include ExportControllerConcern
def index def index

View File

@@ -2,7 +2,7 @@
module Settings module Settings
module Exports module Exports
class ListsController < BaseController class ListsController < ApplicationController
include ExportControllerConcern include ExportControllerConcern
def index def index

View File

@@ -2,7 +2,7 @@
module Settings module Settings
module Exports module Exports
class MutedAccountsController < BaseController class MutedAccountsController < ApplicationController
include ExportControllerConcern include ExportControllerConcern
def index def index

View File

@@ -3,6 +3,11 @@
class Settings::ExportsController < Settings::BaseController class Settings::ExportsController < Settings::BaseController
include Authorization include Authorization
layout 'admin'
before_action :authenticate_user!
before_action :require_not_suspended!
skip_before_action :require_functional! skip_before_action :require_functional!
def show def show
@@ -11,6 +16,8 @@ class Settings::ExportsController < Settings::BaseController
end end
def create def create
raise Mastodon::NotPermittedError unless user_signed_in?
backup = nil backup = nil
RedisLock.acquire(lock_options) do |lock| RedisLock.acquire(lock_options) do |lock|
@@ -30,4 +37,8 @@ class Settings::ExportsController < Settings::BaseController
def lock_options def lock_options
{ redis: Redis.current, key: "backup:#{current_user.id}" } { redis: Redis.current, key: "backup:#{current_user.id}" }
end end
def require_not_suspended!
forbidden if current_account.suspended?
end
end end

View File

@@ -1,9 +1,12 @@
# frozen_string_literal: true # frozen_string_literal: true
class Settings::FeaturedTagsController < Settings::BaseController class Settings::FeaturedTagsController < Settings::BaseController
layout 'admin'
before_action :authenticate_user!
before_action :set_featured_tags, only: :index before_action :set_featured_tags, only: :index
before_action :set_featured_tag, except: [:index, :create] before_action :set_featured_tag, except: [:index, :create]
before_action :set_recently_used_tags, only: :index before_action :set_most_used_tags, only: :index
def index def index
@featured_tag = FeaturedTag.new @featured_tag = FeaturedTag.new
@@ -17,7 +20,7 @@ class Settings::FeaturedTagsController < Settings::BaseController
redirect_to settings_featured_tags_path redirect_to settings_featured_tags_path
else else
set_featured_tags set_featured_tags
set_recently_used_tags set_most_used_tags
render :index render :index
end end
@@ -38,8 +41,8 @@ class Settings::FeaturedTagsController < Settings::BaseController
@featured_tags = current_account.featured_tags.order(statuses_count: :desc).reject(&:new_record?) @featured_tags = current_account.featured_tags.order(statuses_count: :desc).reject(&:new_record?)
end end
def set_recently_used_tags def set_most_used_tags
@recently_used_tags = Tag.recently_used(current_account).where.not(id: @featured_tags.map(&:id)).limit(10) @most_used_tags = Tag.most_used(current_account).where.not(id: @featured_tags.map(&:id)).limit(10)
end end
def featured_tag_params def featured_tag_params

View File

@@ -1,6 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class Settings::IdentityProofsController < Settings::BaseController class Settings::IdentityProofsController < Settings::BaseController
layout 'admin'
before_action :authenticate_user!
before_action :check_required_params, only: :new before_action :check_required_params, only: :new
def index def index

View File

@@ -1,6 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class Settings::ImportsController < Settings::BaseController class Settings::ImportsController < Settings::BaseController
layout 'admin'
before_action :authenticate_user!
before_action :set_account before_action :set_account
def show def show

View File

@@ -1,10 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
class Settings::Migration::RedirectsController < Settings::BaseController class Settings::Migration::RedirectsController < Settings::BaseController
skip_before_action :require_functional! layout 'admin'
before_action :authenticate_user!
before_action :require_not_suspended! before_action :require_not_suspended!
skip_before_action :require_functional!
def new def new
@redirect = Form::Redirect.new @redirect = Form::Redirect.new
end end
@@ -35,4 +38,8 @@ class Settings::Migration::RedirectsController < Settings::BaseController
def resource_params def resource_params
params.require(:form_redirect).permit(:acct, :current_password, :current_username) params.require(:form_redirect).permit(:acct, :current_password, :current_username)
end end
def require_not_suspended!
forbidden if current_account.suspended?
end
end end

View File

@@ -1,12 +1,15 @@
# frozen_string_literal: true # frozen_string_literal: true
class Settings::MigrationsController < Settings::BaseController class Settings::MigrationsController < Settings::BaseController
skip_before_action :require_functional! layout 'admin'
before_action :authenticate_user!
before_action :require_not_suspended! before_action :require_not_suspended!
before_action :set_migrations before_action :set_migrations
before_action :set_cooldown before_action :set_cooldown
skip_before_action :require_functional!
def show def show
@migration = current_account.migrations.build @migration = current_account.migrations.build
end end
@@ -41,4 +44,8 @@ class Settings::MigrationsController < Settings::BaseController
def on_cooldown? def on_cooldown?
@cooldown.present? @cooldown.present?
end end
def require_not_suspended!
forbidden if current_account.suspended?
end
end end

View File

@@ -2,6 +2,7 @@
module Settings module Settings
class PicturesController < BaseController class PicturesController < BaseController
before_action :authenticate_user!
before_action :set_account before_action :set_account
before_action :set_picture before_action :set_picture

View File

@@ -1,6 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
class Settings::PreferencesController < Settings::BaseController class Settings::PreferencesController < Settings::BaseController
layout 'admin'
before_action :authenticate_user!
def show; end def show; end
def update def update
@@ -43,7 +47,6 @@ class Settings::PreferencesController < Settings::BaseController
:setting_display_media, :setting_display_media,
:setting_expand_spoilers, :setting_expand_spoilers,
:setting_reduce_motion, :setting_reduce_motion,
:setting_disable_swiping,
:setting_system_font_ui, :setting_system_font_ui,
:setting_noindex, :setting_noindex,
:setting_theme, :setting_theme,

View File

@@ -1,6 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class Settings::ProfilesController < Settings::BaseController class Settings::ProfilesController < Settings::BaseController
layout 'admin'
before_action :authenticate_user!
before_action :set_account before_action :set_account
def show def show

View File

@@ -1,11 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
class Settings::SessionsController < Settings::BaseController class Settings::SessionsController < Settings::BaseController
skip_before_action :require_functional! before_action :authenticate_user!
before_action :require_not_suspended!
before_action :set_session, only: :destroy before_action :set_session, only: :destroy
skip_before_action :require_functional!
def destroy def destroy
@session.destroy! @session.destroy!
flash[:notice] = I18n.t('sessions.revoke_success') flash[:notice] = I18n.t('sessions.revoke_success')

View File

@@ -5,31 +5,31 @@ module Settings
class ConfirmationsController < BaseController class ConfirmationsController < BaseController
include ChallengableConcern include ChallengableConcern
skip_before_action :require_functional! layout 'admin'
before_action :authenticate_user!
before_action :require_challenge! before_action :require_challenge!
before_action :ensure_otp_secret before_action :ensure_otp_secret
skip_before_action :require_functional!
def new def new
prepare_two_factor_form prepare_two_factor_form
end end
def create def create
if current_user.validate_and_consume_otp!(confirmation_params[:otp_attempt], otp_secret: session[:new_otp_secret]) if current_user.validate_and_consume_otp!(confirmation_params[:otp_attempt])
flash.now[:notice] = I18n.t('two_factor_authentication.enabled_success') flash.now[:notice] = I18n.t('two_factor_authentication.enabled_success')
current_user.otp_required_for_login = true current_user.otp_required_for_login = true
current_user.otp_secret = session[:new_otp_secret]
@recovery_codes = current_user.generate_otp_backup_codes! @recovery_codes = current_user.generate_otp_backup_codes!
current_user.save! current_user.save!
UserMailer.two_factor_enabled(current_user).deliver_later! UserMailer.two_factor_enabled(current_user).deliver_later!
session.delete(:new_otp_secret)
render 'settings/two_factor_authentication/recovery_codes/index' render 'settings/two_factor_authentication/recovery_codes/index'
else else
flash.now[:alert] = I18n.t('otp_authentication.wrong_code') flash.now[:alert] = I18n.t('two_factor_authentication.wrong_code')
prepare_two_factor_form prepare_two_factor_form
render :new render :new
end end
@@ -43,15 +43,12 @@ module Settings
def prepare_two_factor_form def prepare_two_factor_form
@confirmation = Form::TwoFactorConfirmation.new @confirmation = Form::TwoFactorConfirmation.new
@new_otp_secret = session[:new_otp_secret] @provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Rails.configuration.x.local_domain)
@provision_url = current_user.otp_provisioning_uri(current_user.email,
otp_secret: @new_otp_secret,
issuer: Rails.configuration.x.local_domain)
@qrcode = RQRCode::QRCode.new(@provision_url) @qrcode = RQRCode::QRCode.new(@provision_url)
end end
def ensure_otp_secret def ensure_otp_secret
redirect_to settings_otp_authentication_path if session[:new_otp_secret].blank? redirect_to settings_two_factor_authentication_path unless current_user.otp_secret
end end
end end
end end

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