Merge pull request #3420 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 1e87bd178d96ab7d760f019fcf651ecc1585e701
This commit is contained in:
commit
c857c91508
20
CHANGELOG.md
20
CHANGELOG.md
@ -2,6 +2,26 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [4.5.7] - 2026-02-24
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Reject unconfirmed FASPs (#37926 by @oneiros, [GHSA-qgmm-vr4c-ggjg](https://github.com/mastodon/mastodon/security/advisories/GHSA-qgmm-vr4c-ggjg))
|
||||||
|
- Re-use custom socket class for FASP requests (#37925 by @oneiros, [GHSA-46w6-g98f-wxqm](https://github.com/mastodon/mastodon/security/advisories/GHSA-46w6-g98f-wxqm))
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add `--suspended-only` option to `tootctl emoji purge` (#37828 and #37861 by @ClearlyClaire and @mjankowski)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix emoji data not being properly cached (#37858 by @ChaosExAnima)
|
||||||
|
- Fix delete & redraft of pending posts (#37839 by @ClearlyClaire)
|
||||||
|
- Fix processing separate key documents without the ActivityStreams context (#37826 by @ClearlyClaire)
|
||||||
|
- Fix custom emojis not being purged on domain suspension (#37808 by @ClearlyClaire)
|
||||||
|
- Fix users without special permissions being able to stream disabled timelines (#37791 by @ClearlyClaire)
|
||||||
|
- Fix processing of object updates with duplicate hashtags (#37756 by @ClearlyClaire)
|
||||||
|
|
||||||
## [4.5.6] - 2026-02-03
|
## [4.5.6] - 2026-02-03
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|||||||
@ -1,11 +1,41 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::ProfilesController < Api::BaseController
|
class Api::V1::ProfilesController < Api::BaseController
|
||||||
before_action -> { doorkeeper_authorize! :profile, :read, :'read:accounts' }
|
before_action -> { doorkeeper_authorize! :profile, :read, :'read:accounts' }, except: [:update]
|
||||||
|
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:update]
|
||||||
before_action :require_user!
|
before_action :require_user!
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@account = current_account
|
@account = current_account
|
||||||
render json: @account, serializer: REST::ProfileSerializer
|
render json: @account, serializer: REST::ProfileSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@account = current_account
|
||||||
|
UpdateAccountService.new.call(@account, account_params, raise_error: true)
|
||||||
|
ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id)
|
||||||
|
|
||||||
|
render json: @account, serializer: REST::ProfileSerializer
|
||||||
|
rescue ActiveRecord::RecordInvalid => e
|
||||||
|
render json: ValidationErrorFormatter.new(e).as_json, status: 422
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_params
|
||||||
|
params.permit(
|
||||||
|
:display_name,
|
||||||
|
:note,
|
||||||
|
:avatar,
|
||||||
|
:header,
|
||||||
|
:locked,
|
||||||
|
:bot,
|
||||||
|
:discoverable,
|
||||||
|
:hide_collections,
|
||||||
|
:indexable,
|
||||||
|
:show_media,
|
||||||
|
:show_media_replies,
|
||||||
|
:show_featured,
|
||||||
|
attribution_domains: [],
|
||||||
|
fields_attributes: [:name, :value]
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -323,16 +323,16 @@ class Account < ApplicationRecord
|
|||||||
old_fields = self[:fields] || []
|
old_fields = self[:fields] || []
|
||||||
old_fields = [] if old_fields.is_a?(Hash)
|
old_fields = [] if old_fields.is_a?(Hash)
|
||||||
|
|
||||||
if attributes.is_a?(Hash)
|
attributes = attributes.values if attributes.is_a?(Hash)
|
||||||
attributes.each_value do |attr|
|
|
||||||
next if attr[:name].blank? && attr[:value].blank?
|
|
||||||
|
|
||||||
previous = old_fields.find { |item| item['value'] == attr[:value] }
|
attributes.each do |attr|
|
||||||
|
next if attr[:name].blank? && attr[:value].blank?
|
||||||
|
|
||||||
attr[:verified_at] = previous['verified_at'] if previous && previous['verified_at'].present?
|
previous = old_fields.find { |item| item['value'] == attr[:value] }
|
||||||
|
|
||||||
fields << attr
|
attr[:verified_at] = previous['verified_at'] if previous && previous['verified_at'].present?
|
||||||
end
|
|
||||||
|
fields << attr
|
||||||
end
|
end
|
||||||
|
|
||||||
self[:fields] = fields
|
self[:fields] = fields
|
||||||
|
|||||||
@ -113,7 +113,7 @@ namespace :api, format: false do
|
|||||||
resources :endorsements, only: [:index]
|
resources :endorsements, only: [:index]
|
||||||
resources :markers, only: [:index, :create]
|
resources :markers, only: [:index, :create]
|
||||||
|
|
||||||
resource :profile, only: [:show] do
|
resource :profile, only: [:show, :update] do
|
||||||
scope module: :profile do
|
scope module: :profile do
|
||||||
resource :avatar, only: :destroy
|
resource :avatar, only: :destroy
|
||||||
resource :header, only: :destroy
|
resource :header, only: :destroy
|
||||||
|
|||||||
@ -59,7 +59,7 @@ services:
|
|||||||
web:
|
web:
|
||||||
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
|
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
|
||||||
# build: .
|
# build: .
|
||||||
image: ghcr.io/glitch-soc/mastodon:v4.5.6
|
image: ghcr.io/glitch-soc/mastodon:v4.5.7
|
||||||
restart: always
|
restart: always
|
||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
command: bundle exec puma -C config/puma.rb
|
command: bundle exec puma -C config/puma.rb
|
||||||
@ -83,7 +83,7 @@ services:
|
|||||||
# build:
|
# build:
|
||||||
# dockerfile: ./streaming/Dockerfile
|
# dockerfile: ./streaming/Dockerfile
|
||||||
# context: .
|
# context: .
|
||||||
image: ghcr.io/glitch-soc/mastodon-streaming:v4.5.6
|
image: ghcr.io/glitch-soc/mastodon-streaming:v4.5.7
|
||||||
restart: always
|
restart: always
|
||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
command: node ./streaming/index.js
|
command: node ./streaming/index.js
|
||||||
@ -102,7 +102,7 @@ services:
|
|||||||
sidekiq:
|
sidekiq:
|
||||||
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
|
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
|
||||||
# build: .
|
# build: .
|
||||||
image: ghcr.io/glitch-soc/mastodon:v4.5.6
|
image: ghcr.io/glitch-soc/mastodon:v4.5.7
|
||||||
restart: always
|
restart: always
|
||||||
env_file: .env.production
|
env_file: .env.production
|
||||||
command: bundle exec sidekiq
|
command: bundle exec sidekiq
|
||||||
|
|||||||
@ -17,7 +17,7 @@ module Mastodon
|
|||||||
end
|
end
|
||||||
|
|
||||||
def default_prerelease
|
def default_prerelease
|
||||||
'alpha.4'
|
'alpha.5'
|
||||||
end
|
end
|
||||||
|
|
||||||
def prerelease
|
def prerelease
|
||||||
|
|||||||
@ -53,6 +53,80 @@ RSpec.describe 'Profile API' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'PATCH /api/v1/profile' do
|
||||||
|
subject do
|
||||||
|
patch '/api/v1/profile', headers: headers, params: params
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:params) do
|
||||||
|
{
|
||||||
|
avatar: fixture_file_upload('avatar.gif', 'image/gif'),
|
||||||
|
discoverable: true,
|
||||||
|
display_name: "Alice Isn't Dead",
|
||||||
|
header: fixture_file_upload('attachment.jpg', 'image/jpeg'),
|
||||||
|
indexable: true,
|
||||||
|
locked: false,
|
||||||
|
note: 'Hello!',
|
||||||
|
attribution_domains: ['example.com'],
|
||||||
|
fields_attributes: [
|
||||||
|
{ name: 'pronouns', value: 'she/her' },
|
||||||
|
{ name: 'foo', value: 'bar' },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'forbidden for wrong scope', 'read read:accounts'
|
||||||
|
|
||||||
|
describe 'with invalid data' do
|
||||||
|
let(:params) { { note: 'a' * 2 * Account::NOTE_LENGTH_LIMIT } }
|
||||||
|
|
||||||
|
it 'returns http unprocessable entity' do
|
||||||
|
subject
|
||||||
|
expect(response).to have_http_status(422)
|
||||||
|
expect(response.content_type)
|
||||||
|
.to start_with('application/json')
|
||||||
|
expect(response.parsed_body)
|
||||||
|
.to include(
|
||||||
|
error: /Validation failed/,
|
||||||
|
details: include(note: contain_exactly(include(error: 'ERR_TOO_LONG', description: /character limit/)))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns http success with updated JSON attributes' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response)
|
||||||
|
.to have_http_status(200)
|
||||||
|
expect(response.content_type)
|
||||||
|
.to start_with('application/json')
|
||||||
|
expect(response.parsed_body)
|
||||||
|
.to include({
|
||||||
|
locked: false,
|
||||||
|
})
|
||||||
|
expect(user.account.reload)
|
||||||
|
.to have_attributes(
|
||||||
|
display_name: eq("Alice Isn't Dead"),
|
||||||
|
note: 'Hello!',
|
||||||
|
avatar: exist,
|
||||||
|
header: exist,
|
||||||
|
attribution_domains: ['example.com'],
|
||||||
|
fields: contain_exactly(
|
||||||
|
have_attributes(
|
||||||
|
name: 'pronouns',
|
||||||
|
value: 'she/her'
|
||||||
|
),
|
||||||
|
have_attributes(
|
||||||
|
name: 'foo',
|
||||||
|
value: 'bar'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
expect(ActivityPub::UpdateDistributionWorker)
|
||||||
|
.to have_enqueued_sidekiq_job(user.account_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'DELETE /api/v1/profile/avatar' do
|
describe 'DELETE /api/v1/profile/avatar' do
|
||||||
context 'with wrong scope' do
|
context 'with wrong scope' do
|
||||||
before do
|
before do
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user