Merge commit 'a13756148d353c7479f68e65a210f6d88d26c785' into glitch-soc/merge-upstream

Conflicts:
- `app/views/layouts/embedded.html.haml`:
  Upstream made a change to javascript tags next to lines changed in glitch-soc
  because of the theming system.
  Added the javascript entrypoint upstream added.
- `app/views/layouts/error.html.haml`:
  Upstream made a change to javascript tags next to lines changed in glitch-soc
  because of the theming system.
  Added the javascript entrypoint upstream added.
This commit is contained in:
Claire
2025-05-25 15:11:58 +02:00
108 changed files with 1297 additions and 355 deletions

View File

@@ -36,4 +36,15 @@ RSpec.describe Rule do
.to change { described_class.ordered.pluck(:text) }.from(%w(foo baz bar)).to(%w(foo bar baz))
end
end
describe '#translation_for' do
let!(:rule) { Fabricate(:rule, text: 'This is a rule', hint: 'This is an explanation of the rule') }
let!(:translation) { Fabricate(:rule_translation, rule: rule, text: 'Ceci est une règle', hint: 'Ceci est une explication de la règle', language: 'fr') }
it 'returns the expected translation, including fallbacks' do
expect(rule.translation_for(:en)).to have_attributes(text: rule.text, hint: rule.hint)
expect(rule.translation_for(:fr)).to have_attributes(text: translation.text, hint: translation.hint)
expect(rule.translation_for(:'fr-CA')).to have_attributes(text: translation.text, hint: translation.hint)
end
end
end

View File

@@ -67,31 +67,59 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService do
type: 'Collection',
id: actor.featured_collection_url,
items: items,
}.with_indifferent_access
end
shared_examples 'sets pinned posts' do
before do
stub_request(:get, 'https://example.com/account/pinned/known').to_return(status: 200, body: Oj.dump(status_json_pinned_known), headers: { 'Content-Type': 'application/activity+json' })
stub_request(:get, 'https://example.com/account/pinned/unknown-inlined').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_inlined), headers: { 'Content-Type': 'application/activity+json' })
stub_request(:get, 'https://example.com/account/pinned/unknown-unreachable').to_return(status: 404)
stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_reachable), headers: { 'Content-Type': 'application/activity+json' })
stub_request(:get, 'https://example.com/account/collections/featured').to_return(status: 200, body: Oj.dump(featured_with_null), headers: { 'Content-Type': 'application/activity+json' })
subject.call(actor, note: true, hashtag: false)
end
it 'sets expected posts as pinned posts' do
expect(actor.pinned_statuses.pluck(:uri)).to contain_exactly(
'https://example.com/account/pinned/known',
'https://example.com/account/pinned/unknown-inlined',
'https://example.com/account/pinned/unknown-reachable'
)
expect(actor.pinned_statuses).to_not include(known_status)
end
}.deep_stringify_keys
end
describe '#call' do
subject { described_class.new.call(actor, note: true, hashtag: false) }
shared_examples 'sets pinned posts' do
before do
stub_request(:get, 'https://example.com/account/pinned/known').to_return(status: 200, body: Oj.dump(status_json_pinned_known), headers: { 'Content-Type': 'application/activity+json' })
stub_request(:get, 'https://example.com/account/pinned/unknown-inlined').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_inlined), headers: { 'Content-Type': 'application/activity+json' })
stub_request(:get, 'https://example.com/account/pinned/unknown-unreachable').to_return(status: 404)
stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_reachable), headers: { 'Content-Type': 'application/activity+json' })
stub_request(:get, 'https://example.com/account/collections/featured').to_return(status: 200, body: Oj.dump(featured_with_null), headers: { 'Content-Type': 'application/activity+json' })
subject
end
it 'sets expected posts as pinned posts' do
expect(actor.pinned_statuses.pluck(:uri)).to contain_exactly(
'https://example.com/account/pinned/known',
'https://example.com/account/pinned/unknown-inlined',
'https://example.com/account/pinned/unknown-reachable'
)
expect(actor.pinned_statuses).to_not include(known_status)
end
end
context 'when passing the collection via an argument' do
subject { described_class.new.call(actor, note: true, hashtag: false, collection: collection_or_uri) }
context 'when the collection is an URL' do
let(:collection_or_uri) { actor.featured_collection_url }
before do
stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
end
it_behaves_like 'sets pinned posts'
end
context 'when the collection is inlined' do
let(:collection_or_uri) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
type: 'Collection',
items: items,
}.deep_stringify_keys
end
it_behaves_like 'sets pinned posts'
end
end
context 'when the endpoint is a Collection' do
before do
stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
@@ -121,7 +149,7 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService do
before do
stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_reachable), headers: { 'Content-Type': 'application/activity+json' })
subject.call(actor, note: true, hashtag: false)
subject
end
it 'sets expected posts as pinned posts' do
@@ -157,7 +185,7 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService do
before do
stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_reachable), headers: { 'Content-Type': 'application/activity+json' })
subject.call(actor, note: true, hashtag: false)
subject
end
it 'sets expected posts as pinned posts' do

View File

@@ -83,6 +83,27 @@ RSpec.describe ActivityPub::ProcessAccountService do
end
end
context 'with inlined feature collection' do
let(:payload) do
{
id: 'https://foo.test',
type: 'Actor',
inbox: 'https://foo.test/inbox',
featured: {
type: 'OrderedCollection',
orderedItems: ['https://example.com/statuses/1'],
},
}.deep_stringify_keys
end
it 'queues featured collection synchronization', :aggregate_failures do
account = subject.call('alice', 'example.com', payload)
expect(account.featured_collection_url).to eq ''
expect(ActivityPub::SynchronizeFeaturedCollectionWorker).to have_enqueued_sidekiq_job(account.id, { 'hashtag' => true, 'request_id' => anything, 'collection' => payload['featured'] })
end
end
context 'when account is not suspended' do
subject { described_class.new.call(account.username, account.domain, payload) }

View File

@@ -23,19 +23,27 @@ module ProviderRequestHelper
body = encode_body(body)
headers = {}
headers['content-digest'] = content_digest(body)
request = Linzer.new_request(method, url, {}, headers)
request = "Net::HTTP::#{method.to_s.classify}".constantize.new(URI(url), headers)
key = private_key_for(provider)
signature = sign(request, key, %w(@method @target-uri content-digest))
headers.merge(signature.to_h)
Linzer.sign!(request, key:, components: %w(@method @target-uri content-digest))
signature_headers(request)
end
def response_authentication_headers(provider, status, body)
headers = {}
headers['content-digest'] = content_digest(body)
response = Linzer.new_response(body, status, headers)
response = Net::HTTPResponse::CODE_TO_OBJ[status.to_s].new('1.1', status, Rack::Utils::HTTP_STATUS_CODES[status])
response.body = body
response['content-digest'] = content_digest(body)
key = private_key_for(provider)
signature = sign(response, key, %w(@status content-digest))
headers.merge(signature.to_h)
Linzer.sign!(response, key:, components: %w(@status content-digest))
signature_headers(response)
end
def signature_headers(operation)
{
'content-digest' => operation['content-digest'],
'signature-input' => operation['signature-input'],
'signature' => operation['signature'],
}
end
def private_key_for(provider)
@@ -47,16 +55,7 @@ module ProviderRequestHelper
key
end
{
id: provider.id.to_s,
private_key: @cached_provider_keys[provider].private_to_pem,
}
end
def sign(request_or_response, key, components)
message = Linzer::Message.new(request_or_response)
linzer_key = Linzer.new_ed25519_key(key[:private_key], key[:id])
Linzer.sign(linzer_key, message, components)
Linzer.new_ed25519_key(@cached_provider_keys[provider].private_to_pem, provider.id.to_s)
end
def encode_body(body)