Remove WebSub subscriptions (#11303)
parent
4bd58b7f2d
commit
bd87e66679
|
@ -30,9 +30,6 @@ module AccountAssociations
|
||||||
has_many :media_attachments, dependent: :destroy
|
has_many :media_attachments, dependent: :destroy
|
||||||
has_many :polls, dependent: :destroy
|
has_many :polls, dependent: :destroy
|
||||||
|
|
||||||
# PuSH subscriptions
|
|
||||||
has_many :subscriptions, dependent: :destroy
|
|
||||||
|
|
||||||
# Report relationships
|
# Report relationships
|
||||||
has_many :reports, dependent: :destroy, inverse_of: :account
|
has_many :reports, dependent: :destroy, inverse_of: :account
|
||||||
has_many :targeted_reports, class_name: 'Report', foreign_key: :target_account_id, dependent: :destroy, inverse_of: :target_account
|
has_many :targeted_reports, class_name: 'Report', foreign_key: :target_account_id, dependent: :destroy, inverse_of: :target_account
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
# == Schema Information
|
|
||||||
#
|
|
||||||
# Table name: subscriptions
|
|
||||||
#
|
|
||||||
# id :bigint(8) not null, primary key
|
|
||||||
# callback_url :string default(""), not null
|
|
||||||
# secret :string
|
|
||||||
# expires_at :datetime
|
|
||||||
# confirmed :boolean default(FALSE), not null
|
|
||||||
# created_at :datetime not null
|
|
||||||
# updated_at :datetime not null
|
|
||||||
# last_successful_delivery_at :datetime
|
|
||||||
# domain :string
|
|
||||||
# account_id :bigint(8) not null
|
|
||||||
#
|
|
||||||
|
|
||||||
class Subscription < ApplicationRecord
|
|
||||||
MIN_EXPIRATION = 1.day.to_i
|
|
||||||
MAX_EXPIRATION = 30.days.to_i
|
|
||||||
|
|
||||||
belongs_to :account
|
|
||||||
|
|
||||||
validates :callback_url, presence: true
|
|
||||||
validates :callback_url, uniqueness: { scope: :account_id }
|
|
||||||
|
|
||||||
scope :confirmed, -> { where(confirmed: true) }
|
|
||||||
scope :future_expiration, -> { where(arel_table[:expires_at].gt(Time.now.utc)) }
|
|
||||||
scope :expired, -> { where(arel_table[:expires_at].lt(Time.now.utc)) }
|
|
||||||
scope :active, -> { confirmed.future_expiration }
|
|
||||||
|
|
||||||
def lease_seconds=(value)
|
|
||||||
self.expires_at = future_expiration(value)
|
|
||||||
end
|
|
||||||
|
|
||||||
def lease_seconds
|
|
||||||
(expires_at - Time.now.utc).to_i
|
|
||||||
end
|
|
||||||
|
|
||||||
def expired?
|
|
||||||
Time.now.utc > expires_at
|
|
||||||
end
|
|
||||||
|
|
||||||
before_validation :set_min_expiration
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def future_expiration(value)
|
|
||||||
Time.now.utc + future_offset(value).seconds
|
|
||||||
end
|
|
||||||
|
|
||||||
def future_offset(seconds)
|
|
||||||
[
|
|
||||||
[MIN_EXPIRATION, seconds.to_i].max,
|
|
||||||
MAX_EXPIRATION,
|
|
||||||
].min
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_min_expiration
|
|
||||||
self.lease_seconds = 0 unless expires_at
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class SubscriptionPolicy < ApplicationPolicy
|
|
||||||
def index?
|
|
||||||
admin?
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -24,7 +24,6 @@ class SuspendAccountService < BaseService
|
||||||
report_notes
|
report_notes
|
||||||
scheduled_statuses
|
scheduled_statuses
|
||||||
status_pins
|
status_pins
|
||||||
subscriptions
|
|
||||||
).freeze
|
).freeze
|
||||||
|
|
||||||
ASSOCIATIONS_ON_DESTROY = %w(
|
ASSOCIATIONS_ON_DESTROY = %w(
|
||||||
|
|
|
@ -5,7 +5,5 @@ class Scheduler::SubscriptionsCleanupScheduler
|
||||||
|
|
||||||
sidekiq_options unique: :until_executed, retry: 0
|
sidekiq_options unique: :until_executed, retry: 0
|
||||||
|
|
||||||
def perform
|
def perform; end
|
||||||
Subscription.expired.in_batches.delete_all
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,9 +21,6 @@
|
||||||
user_cleanup_scheduler:
|
user_cleanup_scheduler:
|
||||||
cron: '<%= Random.rand(0..59) %> <%= Random.rand(4..6) %> * * *'
|
cron: '<%= Random.rand(0..59) %> <%= Random.rand(4..6) %> * * *'
|
||||||
class: Scheduler::UserCleanupScheduler
|
class: Scheduler::UserCleanupScheduler
|
||||||
subscriptions_cleanup_scheduler:
|
|
||||||
cron: '<%= Random.rand(0..59) %> <%= Random.rand(1..3) %> * * 0'
|
|
||||||
class: Scheduler::SubscriptionsCleanupScheduler
|
|
||||||
ip_cleanup_scheduler:
|
ip_cleanup_scheduler:
|
||||||
cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *'
|
cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *'
|
||||||
class: Scheduler::IpCleanupScheduler
|
class: Scheduler::IpCleanupScheduler
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class DropSubscriptions < ActiveRecord::Migration[5.2]
|
||||||
|
def up
|
||||||
|
drop_table :subscriptions
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
raise ActiveRecord::IrreversibleMigration
|
||||||
|
end
|
||||||
|
end
|
14
db/schema.rb
14
db/schema.rb
|
@ -648,19 +648,6 @@ ActiveRecord::Schema.define(version: 2019_07_15_164535) do
|
||||||
t.index ["tag_id", "status_id"], name: "index_statuses_tags_on_tag_id_and_status_id", unique: true
|
t.index ["tag_id", "status_id"], name: "index_statuses_tags_on_tag_id_and_status_id", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "subscriptions", force: :cascade do |t|
|
|
||||||
t.string "callback_url", default: "", null: false
|
|
||||||
t.string "secret"
|
|
||||||
t.datetime "expires_at"
|
|
||||||
t.boolean "confirmed", default: false, null: false
|
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.datetime "updated_at", null: false
|
|
||||||
t.datetime "last_successful_delivery_at"
|
|
||||||
t.string "domain"
|
|
||||||
t.bigint "account_id", null: false
|
|
||||||
t.index ["account_id", "callback_url"], name: "index_subscriptions_on_account_id_and_callback_url", unique: true
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "tags", force: :cascade do |t|
|
create_table "tags", force: :cascade do |t|
|
||||||
t.string "name", default: "", null: false
|
t.string "name", default: "", null: false
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
|
@ -821,7 +808,6 @@ ActiveRecord::Schema.define(version: 2019_07_15_164535) do
|
||||||
add_foreign_key "statuses", "statuses", column: "reblog_of_id", on_delete: :cascade
|
add_foreign_key "statuses", "statuses", column: "reblog_of_id", on_delete: :cascade
|
||||||
add_foreign_key "statuses_tags", "statuses", on_delete: :cascade
|
add_foreign_key "statuses_tags", "statuses", on_delete: :cascade
|
||||||
add_foreign_key "statuses_tags", "tags", name: "fk_3081861e21", on_delete: :cascade
|
add_foreign_key "statuses_tags", "tags", name: "fk_3081861e21", on_delete: :cascade
|
||||||
add_foreign_key "subscriptions", "accounts", name: "fk_9847d1cbb5", on_delete: :cascade
|
|
||||||
add_foreign_key "tombstones", "accounts", on_delete: :cascade
|
add_foreign_key "tombstones", "accounts", on_delete: :cascade
|
||||||
add_foreign_key "user_invite_requests", "users", on_delete: :cascade
|
add_foreign_key "user_invite_requests", "users", on_delete: :cascade
|
||||||
add_foreign_key "users", "accounts", name: "fk_50500f500d", on_delete: :cascade
|
add_foreign_key "users", "accounts", name: "fk_50500f500d", on_delete: :cascade
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
Fabricator(:subscription) do
|
|
||||||
account
|
|
||||||
callback_url "http://example.com/callback"
|
|
||||||
secret "foobar"
|
|
||||||
expires_at "2016-11-28 11:30:07"
|
|
||||||
confirmed false
|
|
||||||
end
|
|
|
@ -1,67 +0,0 @@
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Subscription, type: :model do
|
|
||||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
|
||||||
|
|
||||||
subject { Fabricate(:subscription, account: alice) }
|
|
||||||
|
|
||||||
describe '#expired?' do
|
|
||||||
it 'return true when expires_at is past' do
|
|
||||||
subject.expires_at = 2.days.ago
|
|
||||||
expect(subject.expired?).to be true
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'return false when expires_at is future' do
|
|
||||||
subject.expires_at = 2.days.from_now
|
|
||||||
expect(subject.expired?).to be false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'lease_seconds' do
|
|
||||||
it 'returns the time remaining until expiration' do
|
|
||||||
datetime = 1.day.from_now
|
|
||||||
subscription = Subscription.new(expires_at: datetime)
|
|
||||||
travel_to(datetime - 12.hours) do
|
|
||||||
expect(subscription.lease_seconds).to eq(12.hours)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'lease_seconds=' do
|
|
||||||
it 'sets expires_at to min expiration when small value is provided' do
|
|
||||||
subscription = Subscription.new
|
|
||||||
datetime = 1.day.from_now
|
|
||||||
too_low = Subscription::MIN_EXPIRATION - 1000
|
|
||||||
travel_to(datetime) do
|
|
||||||
subscription.lease_seconds = too_low
|
|
||||||
end
|
|
||||||
|
|
||||||
expected = datetime + Subscription::MIN_EXPIRATION.seconds
|
|
||||||
expect(subscription.expires_at).to be_within(1.0).of(expected)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'sets expires_at to value when valid value is provided' do
|
|
||||||
subscription = Subscription.new
|
|
||||||
datetime = 1.day.from_now
|
|
||||||
valid = Subscription::MIN_EXPIRATION + 1000
|
|
||||||
travel_to(datetime) do
|
|
||||||
subscription.lease_seconds = valid
|
|
||||||
end
|
|
||||||
|
|
||||||
expected = datetime + valid.seconds
|
|
||||||
expect(subscription.expires_at).to be_within(1.0).of(expected)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'sets expires_at to max expiration when large value is provided' do
|
|
||||||
subscription = Subscription.new
|
|
||||||
datetime = 1.day.from_now
|
|
||||||
too_high = Subscription::MAX_EXPIRATION + 1000
|
|
||||||
travel_to(datetime) do
|
|
||||||
subscription.lease_seconds = too_high
|
|
||||||
end
|
|
||||||
|
|
||||||
expected = datetime + Subscription::MAX_EXPIRATION.seconds
|
|
||||||
expect(subscription.expires_at).to be_within(1.0).of(expected)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,24 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'rails_helper'
|
|
||||||
require 'pundit/rspec'
|
|
||||||
|
|
||||||
RSpec.describe SubscriptionPolicy do
|
|
||||||
let(:subject) { described_class }
|
|
||||||
let(:admin) { Fabricate(:user, admin: true).account }
|
|
||||||
let(:john) { Fabricate(:user).account }
|
|
||||||
|
|
||||||
permissions :index? do
|
|
||||||
context 'admin?' do
|
|
||||||
it 'permits' do
|
|
||||||
expect(subject).to permit(admin, Subscription)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context '!admin?' do
|
|
||||||
it 'denies' do
|
|
||||||
expect(subject).to_not permit(john, Subscription)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -14,11 +14,8 @@ RSpec.describe BatchedRemoveStatusService, type: :service do
|
||||||
before do
|
before do
|
||||||
allow(Redis.current).to receive_messages(publish: nil)
|
allow(Redis.current).to receive_messages(publish: nil)
|
||||||
|
|
||||||
stub_request(:post, 'http://example.com/push').to_return(status: 200, body: '', headers: {})
|
|
||||||
stub_request(:post, 'http://example.com/salmon').to_return(status: 200, body: '', headers: {})
|
|
||||||
stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
|
stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
|
||||||
|
|
||||||
Fabricate(:subscription, account: alice, callback_url: 'http://example.com/push', confirmed: true, expires_at: 30.days.from_now)
|
|
||||||
jeff.user.update(current_sign_in_at: Time.zone.now)
|
jeff.user.update(current_sign_in_at: Time.zone.now)
|
||||||
jeff.follow!(alice)
|
jeff.follow!(alice)
|
||||||
hank.follow!(alice)
|
hank.follow!(alice)
|
||||||
|
|
|
@ -10,12 +10,9 @@ RSpec.describe RemoveStatusService, type: :service do
|
||||||
let!(:bill) { Fabricate(:account, username: 'bill', protocol: :activitypub, domain: 'example2.com', inbox_url: 'http://example2.com/inbox') }
|
let!(:bill) { Fabricate(:account, username: 'bill', protocol: :activitypub, domain: 'example2.com', inbox_url: 'http://example2.com/inbox') }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_request(:post, 'http://example.com/push').to_return(status: 200, body: '', headers: {})
|
|
||||||
stub_request(:post, 'http://example.com/salmon').to_return(status: 200, body: '', headers: {})
|
|
||||||
stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
|
stub_request(:post, 'http://example.com/inbox').to_return(status: 200)
|
||||||
stub_request(:post, 'http://example2.com/inbox').to_return(status: 200)
|
stub_request(:post, 'http://example2.com/inbox').to_return(status: 200)
|
||||||
|
|
||||||
Fabricate(:subscription, account: alice, callback_url: 'http://example.com/push', confirmed: true, expires_at: 30.days.from_now)
|
|
||||||
jeff.follow!(alice)
|
jeff.follow!(alice)
|
||||||
hank.follow!(alice)
|
hank.follow!(alice)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ RSpec.describe SuspendAccountService, type: :service do
|
||||||
let!(:favourite) { Fabricate(:favourite, account: account) }
|
let!(:favourite) { Fabricate(:favourite, account: account) }
|
||||||
let!(:active_relationship) { Fabricate(:follow, account: account) }
|
let!(:active_relationship) { Fabricate(:follow, account: account) }
|
||||||
let!(:passive_relationship) { Fabricate(:follow, target_account: account) }
|
let!(:passive_relationship) { Fabricate(:follow, target_account: account) }
|
||||||
let!(:subscription) { Fabricate(:subscription, account: account) }
|
|
||||||
let!(:remote_alice) { Fabricate(:account, inbox_url: 'https://alice.com/inbox', protocol: :activitypub) }
|
let!(:remote_alice) { Fabricate(:account, inbox_url: 'https://alice.com/inbox', protocol: :activitypub) }
|
||||||
let!(:remote_bob) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', protocol: :activitypub) }
|
let!(:remote_bob) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', protocol: :activitypub) }
|
||||||
|
|
||||||
|
@ -31,9 +30,8 @@ RSpec.describe SuspendAccountService, type: :service do
|
||||||
account.favourites,
|
account.favourites,
|
||||||
account.active_relationships,
|
account.active_relationships,
|
||||||
account.passive_relationships,
|
account.passive_relationships,
|
||||||
account.subscriptions
|
|
||||||
].map(&:count)
|
].map(&:count)
|
||||||
}.from([1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0])
|
}.from([1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sends a delete actor activity to all known inboxes' do
|
it 'sends a delete actor activity to all known inboxes' do
|
||||||
|
@ -62,7 +60,6 @@ RSpec.describe SuspendAccountService, type: :service do
|
||||||
let!(:favourite) { Fabricate(:favourite, account: remote_bob) }
|
let!(:favourite) { Fabricate(:favourite, account: remote_bob) }
|
||||||
let!(:active_relationship) { Fabricate(:follow, account: remote_bob, target_account: account) }
|
let!(:active_relationship) { Fabricate(:follow, account: remote_bob, target_account: account) }
|
||||||
let!(:passive_relationship) { Fabricate(:follow, target_account: remote_bob) }
|
let!(:passive_relationship) { Fabricate(:follow, target_account: remote_bob) }
|
||||||
let!(:subscription) { Fabricate(:subscription, account: remote_bob) }
|
|
||||||
|
|
||||||
it 'deletes associated records' do
|
it 'deletes associated records' do
|
||||||
is_expected.to change {
|
is_expected.to change {
|
||||||
|
@ -73,9 +70,8 @@ RSpec.describe SuspendAccountService, type: :service do
|
||||||
remote_bob.favourites,
|
remote_bob.favourites,
|
||||||
remote_bob.active_relationships,
|
remote_bob.active_relationships,
|
||||||
remote_bob.passive_relationships,
|
remote_bob.passive_relationships,
|
||||||
remote_bob.subscriptions
|
|
||||||
].map(&:count)
|
].map(&:count)
|
||||||
}.from([1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0])
|
}.from([1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sends a reject follow to follwer inboxes' do
|
it 'sends a reject follow to follwer inboxes' do
|
||||||
|
|
Reference in New Issue