mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
Add screening by IP address. When deleting a user as a spammer, block all signups from the same IP address.
This commit is contained in:
@@ -226,6 +226,18 @@ describe UserDestroyer do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'ip address screening' do
|
||||
it "doesn't create screened_ip_address records by default" do
|
||||
ScreenedIpAddress.expects(:watch).never
|
||||
UserDestroyer.new(@admin).destroy(@user)
|
||||
end
|
||||
|
||||
it "creates new screened_ip_address records when block_ip is true" do
|
||||
ScreenedIpAddress.expects(:watch).with(@user.ip_address).returns(stub_everything)
|
||||
UserDestroyer.new(@admin).destroy(@user, {block_ip: true})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe AllowedIpAddressValidator do
|
||||
|
||||
let(:record) { Fabricate.build(:user, ip_address: '99.232.23.123') }
|
||||
let(:validator) { described_class.new({attributes: :ip_address}) }
|
||||
subject(:validate) { validator.validate_each(record, :ip_address, record.ip_address) }
|
||||
|
||||
context "ip address should be blocked" do
|
||||
it 'should add an error' do
|
||||
ScreenedIpAddress.stubs(:should_block?).returns(true)
|
||||
validate
|
||||
record.errors[:ip_address].should be_present
|
||||
end
|
||||
end
|
||||
|
||||
context "ip address should not be blocked" do
|
||||
it "shouldn't add an error" do
|
||||
ScreenedIpAddress.stubs(:should_block?).returns(false)
|
||||
validate
|
||||
record.errors[:ip_address].should_not be_present
|
||||
end
|
||||
end
|
||||
|
||||
context 'ip_address is nil' do
|
||||
it "shouldn't add an error" do
|
||||
ScreenedIpAddress.expects(:should_block?).never
|
||||
record.ip_address = nil
|
||||
validate
|
||||
record.errors[:ip_address].should_not be_present
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Admin::ScreenedIpAddressesController do
|
||||
it "is a subclass of AdminController" do
|
||||
(Admin::ScreenedIpAddressesController < Admin::AdminController).should be_true
|
||||
end
|
||||
|
||||
let!(:user) { log_in(:admin) }
|
||||
|
||||
context '.index' do
|
||||
before do
|
||||
xhr :get, :index
|
||||
end
|
||||
|
||||
subject { response }
|
||||
it { should be_success }
|
||||
|
||||
it 'returns JSON' do
|
||||
::JSON.parse(subject.body).should be_a(Array)
|
||||
end
|
||||
end
|
||||
end
|
||||
3
spec/fabricators/screened_ip_address_fabricator.rb
Normal file
3
spec/fabricators/screened_ip_address_fabricator.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
Fabricator(:screened_ip_address) do
|
||||
ip_address { sequence(:ip_address) { |n| "123.#{(n*3)%255}.#{(n*2)%255}.#{n%255}" } }
|
||||
end
|
||||
126
spec/models/screened_ip_address_spec.rb
Normal file
126
spec/models/screened_ip_address_spec.rb
Normal file
@@ -0,0 +1,126 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe ScreenedIpAddress do
|
||||
let(:ip_address) { '99.232.23.124' }
|
||||
let(:valid_params) { {ip_address: ip_address} }
|
||||
|
||||
describe 'new record' do
|
||||
it 'sets a default action_type' do
|
||||
described_class.create(valid_params).action_type.should == described_class.actions[:block]
|
||||
end
|
||||
end
|
||||
|
||||
describe '#watch' do
|
||||
context 'ip_address is not being watched' do
|
||||
it 'should create a new record' do
|
||||
record = described_class.watch(ip_address)
|
||||
record.should_not be_new_record
|
||||
record.action_type.should == described_class.actions[:block]
|
||||
end
|
||||
|
||||
it 'lets action_type be overridden' do
|
||||
record = described_class.watch(ip_address, action_type: described_class.actions[:do_nothing])
|
||||
record.should_not be_new_record
|
||||
record.action_type.should == described_class.actions[:do_nothing]
|
||||
end
|
||||
|
||||
it "a record with subnet mask exists, but doesn't match" do
|
||||
existing = Fabricate(:screened_ip_address, ip_address: '99.232.23.124/24')
|
||||
expect { described_class.watch('99.232.55.124') }.to change { described_class.count }
|
||||
end
|
||||
|
||||
it "a record with exact matching exists, but doesn't match" do
|
||||
existing = Fabricate(:screened_ip_address, ip_address: '99.232.23.124')
|
||||
expect { described_class.watch('99.232.23.123') }.to change { described_class.count }
|
||||
end
|
||||
end
|
||||
|
||||
context 'ip_address is already being watched' do
|
||||
shared_examples 'exact match of ip address' do
|
||||
it 'should not create a new record' do
|
||||
expect { described_class.watch(ip_address_arg) }.to_not change { described_class.count }
|
||||
end
|
||||
|
||||
it 'returns the existing record' do
|
||||
described_class.watch(ip_address_arg).should == existing
|
||||
end
|
||||
end
|
||||
|
||||
context 'using exact match' do
|
||||
let!(:existing) { Fabricate(:screened_ip_address) }
|
||||
let(:ip_address_arg) { existing.ip_address }
|
||||
include_examples 'exact match of ip address'
|
||||
end
|
||||
|
||||
context 'using subnet mask 255.255.255.0' do
|
||||
let!(:existing) { Fabricate(:screened_ip_address, ip_address: '99.232.23.124/24') }
|
||||
|
||||
context 'at exact address' do
|
||||
let(:ip_address_arg) { '99.232.23.124' }
|
||||
include_examples 'exact match of ip address'
|
||||
end
|
||||
|
||||
context 'at address in same subnet' do
|
||||
let(:ip_address_arg) { '99.232.23.135' }
|
||||
include_examples 'exact match of ip address'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't block 10.0.0.0/8" do
|
||||
described_class.watch('10.0.0.0').action_type.should == described_class.actions[:do_nothing]
|
||||
described_class.watch('10.0.0.1').action_type.should == described_class.actions[:do_nothing]
|
||||
described_class.watch('10.10.125.111').action_type.should == described_class.actions[:do_nothing]
|
||||
end
|
||||
|
||||
it "doesn't block 192.168.0.0/16" do
|
||||
described_class.watch('192.168.0.5').action_type.should == described_class.actions[:do_nothing]
|
||||
described_class.watch('192.168.10.1').action_type.should == described_class.actions[:do_nothing]
|
||||
end
|
||||
|
||||
it "doesn't block 127.0.0.0/8" do
|
||||
described_class.watch('127.0.0.1').action_type.should == described_class.actions[:do_nothing]
|
||||
end
|
||||
|
||||
it "doesn't block fc00::/7 addresses (IPv6)" do
|
||||
described_class.watch('fd12:db8::ff00:42:8329').action_type.should == described_class.actions[:do_nothing]
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
describe '#should_block?' do
|
||||
it 'returns false when record does not exist' do
|
||||
described_class.should_block?(ip_address).should eq(false)
|
||||
end
|
||||
|
||||
it 'returns false when no record matches' do
|
||||
Fabricate(:screened_ip_address, ip_address: '111.234.23.11', action_type: described_class.actions[:block])
|
||||
described_class.should_block?('222.12.12.12').should eq(false)
|
||||
end
|
||||
|
||||
context 'IPv4' do
|
||||
it 'returns false when when record matches and action is :do_nothing' do
|
||||
Fabricate(:screened_ip_address, ip_address: '111.234.23.11', action_type: described_class.actions[:do_nothing])
|
||||
described_class.should_block?('111.234.23.11').should eq(false)
|
||||
end
|
||||
|
||||
it 'returns true when when record matches and action is :block' do
|
||||
Fabricate(:screened_ip_address, ip_address: '111.234.23.11', action_type: described_class.actions[:block])
|
||||
described_class.should_block?('111.234.23.11').should eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'IPv6' do
|
||||
it 'returns false when when record matches and action is :do_nothing' do
|
||||
Fabricate(:screened_ip_address, ip_address: '2001:db8::ff00:42:8329', action_type: described_class.actions[:do_nothing])
|
||||
described_class.should_block?('2001:db8::ff00:42:8329').should eq(false)
|
||||
end
|
||||
|
||||
it 'returns true when when record matches and action is :block' do
|
||||
Fabricate(:screened_ip_address, ip_address: '2001:db8::ff00:42:8329', action_type: described_class.actions[:block])
|
||||
described_class.should_block?('2001:db8::ff00:42:8329').should eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -226,7 +226,6 @@ describe User do
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
context 'after_save' do
|
||||
before do
|
||||
subject.save
|
||||
@@ -238,6 +237,21 @@ describe User do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'ip address validation' do
|
||||
it 'validates ip_address for new users' do
|
||||
u = Fabricate.build(:user)
|
||||
AllowedIpAddressValidator.any_instance.expects(:validate_each).with(u, :ip_address, u.ip_address)
|
||||
u.valid?
|
||||
end
|
||||
|
||||
it 'does not validate ip_address when updating an existing user' do
|
||||
u = Fabricate(:user)
|
||||
u.ip_address = '87.123.23.11'
|
||||
AllowedIpAddressValidator.any_instance.expects(:validate_each).never
|
||||
u.valid?
|
||||
end
|
||||
end
|
||||
|
||||
describe "trust levels" do
|
||||
|
||||
# NOTE be sure to use build to avoid db calls
|
||||
|
||||
Reference in New Issue
Block a user