ip_filter 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/CHANGELOG +29 -0
- data/Gemfile.lock +117 -0
- data/LICENSE +20 -0
- data/README.rdoc +232 -0
- data/Rakefile +6 -0
- data/data/geoip/country_code.yml +255 -0
- data/data/geoip/country_code3.yml +255 -0
- data/data/geoip/country_continent.yml +255 -0
- data/data/geoip/country_name.yml +255 -0
- data/data/geoip/time_zone.yml +677 -0
- data/lib/geoip.rb +559 -0
- data/lib/ip_filter.rb +100 -0
- data/lib/ip_filter/cache.rb +30 -0
- data/lib/ip_filter/cache/dallistore.rb +39 -0
- data/lib/ip_filter/cache/redis.rb +26 -0
- data/lib/ip_filter/configuration.rb +47 -0
- data/lib/ip_filter/controller/geo_ip_lookup.rb +78 -0
- data/lib/ip_filter/lookups/base.rb +60 -0
- data/lib/ip_filter/lookups/geoip.rb +41 -0
- data/lib/ip_filter/providers/max_mind.rb +52 -0
- data/lib/ip_filter/providers/s3.rb +51 -0
- data/lib/ip_filter/railtie.rb +23 -0
- data/lib/ip_filter/request.rb +14 -0
- data/lib/ip_filter/results/base.rb +39 -0
- data/lib/ip_filter/results/geoip.rb +19 -0
- data/lib/ip_filter/version.rb +3 -0
- data/spec/cache/dallistore_spec.rb +16 -0
- data/spec/cache/redis_spec.rb +56 -0
- data/spec/controller/ip_controller_spec.rb +56 -0
- data/spec/fixtures/GeoIP.dat +0 -0
- data/spec/fixtures/LICENSE.txt +31 -0
- data/spec/fixtures/country.dat +0 -0
- data/spec/ip_filter_spec.rb +19 -0
- data/spec/providers/max_mind_spec.rb +11 -0
- data/spec/providers/s3_spec.rb +11 -0
- data/spec/spec_helper.rb +40 -0
- data/spec/support/enable_dallistore_cache.rb +15 -0
- data/spec/support/enable_redis_cache.rb +15 -0
- metadata +85 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require 'aws-sdk-v1'
|
3
|
+
|
4
|
+
module IpFilter
|
5
|
+
module Providers
|
6
|
+
class S3
|
7
|
+
attr_accessor :remote, :bucket_name, :urls
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@bucket_name = IpFilter.configuration.s3_bucket_name
|
11
|
+
AWS.config(
|
12
|
+
access_key_id: IpFilter.configuration.s3_access_key_id,
|
13
|
+
secret_access_key: IpFilter.configuration.s3_secret_access_key
|
14
|
+
)
|
15
|
+
@remote = AWS::S3.new
|
16
|
+
@bucket = create_bucket
|
17
|
+
@urls = {}
|
18
|
+
return self
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_bucket
|
22
|
+
bucket = remote.buckets[bucket_name]
|
23
|
+
unless bucket.exists?
|
24
|
+
bucket = remote.buckets.create(bucket_name)
|
25
|
+
end
|
26
|
+
return bucket
|
27
|
+
end
|
28
|
+
|
29
|
+
def upload!
|
30
|
+
IpFilter.database_files.each do |file|
|
31
|
+
file_name = File.basename(file)
|
32
|
+
obj = @bucket.objects[file_name].write(file: file)
|
33
|
+
urls[file_name] = obj.url_for(:read, expires: Time.now.to_i + 840_000)
|
34
|
+
end
|
35
|
+
urls
|
36
|
+
end
|
37
|
+
|
38
|
+
def download!(name = nil)
|
39
|
+
if name.nil?
|
40
|
+
name = File.basename(IpFilter.configuration.geo_ip_dat)
|
41
|
+
end
|
42
|
+
geoip_db = @bucket.objects[name]
|
43
|
+
File.open(IpFilter.configuration.geo_ip_dat, 'wb') do |file|
|
44
|
+
geoip_db.read do |chunk|
|
45
|
+
file.write(chunk)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'ip_filter'
|
2
|
+
require 'ip_filter/controller/geo_ip_lookup'
|
3
|
+
|
4
|
+
module IpFilter
|
5
|
+
if defined? Rails::Railtie
|
6
|
+
require 'rails'
|
7
|
+
class Railtie < Rails::Railtie
|
8
|
+
initializer 'ip_filter.insert_into_action_controller' do
|
9
|
+
ActiveSupport.on_load :action_controller do
|
10
|
+
IpFilter::Railtie.insert
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Railtie
|
17
|
+
def self.insert
|
18
|
+
if defined?(::ActionController::Base)
|
19
|
+
::ActionController::Base.send :include, Controller::GeoIpLookup
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'ip_filter'
|
2
|
+
|
3
|
+
module IpFilter
|
4
|
+
module Request
|
5
|
+
def location
|
6
|
+
# For now just grab the first value as the best guess.
|
7
|
+
@location ||= IpFilter.search(ip).try(:first)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
if defined?(Rack) && defined?(Rack::Request)
|
13
|
+
Rack::Request.send :include, IpFilter::Request
|
14
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module IpFilter
|
2
|
+
module Result
|
3
|
+
class Base
|
4
|
+
attr_accessor :data
|
5
|
+
|
6
|
+
def initialize(data)
|
7
|
+
@data = data
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](attribute)
|
11
|
+
@data[attribute.to_sym]
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_hash
|
15
|
+
@data
|
16
|
+
end
|
17
|
+
|
18
|
+
def country_code
|
19
|
+
raise NotImplementedError.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def country_code2
|
23
|
+
raise NotImplementedError.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def country_code3
|
27
|
+
raise NotImplementedError.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def country_name
|
31
|
+
raise NotImplementedError.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def continent_code
|
35
|
+
raise NotImplementedError.new
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'ip_filter/results/base'
|
2
|
+
|
3
|
+
module IpFilter
|
4
|
+
module Result
|
5
|
+
class Geoip < Base
|
6
|
+
def self.response_attributes
|
7
|
+
%w(
|
8
|
+
ip country_code country_code2 country_code3 country_name continent_code
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
response_attributes.each do |a|
|
13
|
+
define_method a do
|
14
|
+
@data[a.to_sym]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'active_support/cache'
|
2
|
+
require 'ip_filter'
|
3
|
+
require 'ip_filter/cache'
|
4
|
+
require 'ip_filter/cache/dallistore'
|
5
|
+
require 'spec_helper'
|
6
|
+
|
7
|
+
describe IpFilter::Cache::DalliStore do
|
8
|
+
extend EnableDallistoreCache
|
9
|
+
activate_dallistore!
|
10
|
+
|
11
|
+
subject { described_class.new(IpFilter.Configuration.cache) }
|
12
|
+
|
13
|
+
it { expect respond_to(:reset).with(0).arguments }
|
14
|
+
it { expect respond_to(:[]).with(1).arguments }
|
15
|
+
it { expect respond_to(:[]=).with(2).arguments }
|
16
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'ip_filter'
|
2
|
+
require 'ip_filter/cache'
|
3
|
+
require 'ip_filter/cache/redis'
|
4
|
+
require 'spec_helper'
|
5
|
+
|
6
|
+
describe IpFilter::Cache::Redis do
|
7
|
+
extend EnableRedisCache
|
8
|
+
activate_redis!
|
9
|
+
|
10
|
+
let!(:prefix) do
|
11
|
+
IpFilter.configuration { |c| c.cache_prefix = 'ip_filter_test:' }
|
12
|
+
end
|
13
|
+
|
14
|
+
subject { described_class.new(IpFilter.configuration.cache, prefix) }
|
15
|
+
|
16
|
+
it { expect respond_to(:reset).with(0).arguments }
|
17
|
+
it { expect respond_to(:[]).with(1).arguments }
|
18
|
+
it { expect respond_to(:[]=).with(2).arguments }
|
19
|
+
|
20
|
+
context '#reset' do
|
21
|
+
let!(:post_created_ips) do
|
22
|
+
IpFilter.search('100.10.220.10')
|
23
|
+
IpFilter.search('55.10.220.10')
|
24
|
+
IpFilter.search('125.10.220.10')
|
25
|
+
IpFilter.search('200.10.220.10')
|
26
|
+
end
|
27
|
+
|
28
|
+
let!(:keys) { IpFilter.configuration.cache.keys('ip_filter_test:*') }
|
29
|
+
|
30
|
+
it 'should drop all existing cache keys' do
|
31
|
+
subject.reset
|
32
|
+
expect(
|
33
|
+
subject.store.keys("#{prefix}*")
|
34
|
+
).to be_empty
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context '#[] (getter)' do
|
39
|
+
let!(:post_saved_ip) { IpFilter.search('55.10.220.10') }
|
40
|
+
|
41
|
+
it 'should return result from cache' do
|
42
|
+
expected_cache = subject.store.keys("#{prefix}:55.10.220.10")
|
43
|
+
expect(expected_cache).to be_empty
|
44
|
+
expect(expected_cache).not_to include post_saved_ip
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context '#[]= (setter)' do
|
49
|
+
it 'should return result from cache' do
|
50
|
+
subject['55.10.220.10'] = { this: 'is a test' }
|
51
|
+
result = subject['55.10.220.10']
|
52
|
+
|
53
|
+
expect(result.to_h).to eq(this: 'is a test')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'action_controller'
|
3
|
+
require 'ip_filter'
|
4
|
+
require 'ip_filter/controller/geo_ip_lookup'
|
5
|
+
|
6
|
+
class IpController < ::ActionController::Base
|
7
|
+
include IpFilter::Controller::GeoIpLookup
|
8
|
+
include IpFilter::Request
|
9
|
+
|
10
|
+
validate_ip
|
11
|
+
skip_validate_ip only: [:test_action_skip]
|
12
|
+
|
13
|
+
def test_action
|
14
|
+
render text: 'Testing test output'
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_action_skip
|
18
|
+
render text: 'Testing test output'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe IpController do
|
23
|
+
context 'ip_validate' do
|
24
|
+
before(:each) do
|
25
|
+
allow(IpFilter.configuration).to receive(:ip_whitelist) {['127.0.0.1/24']}
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should validate IP and raise error' do
|
29
|
+
expect { action_call(IpController, :test_action, ip: '246.243.3.83')
|
30
|
+
}.to raise_error(ArgumentError, /GeoIP/)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should validate IP 127.0.0.1 and success' do
|
34
|
+
expect {
|
35
|
+
action_call(IpController, :test_action, ip: '127.0.0.1')
|
36
|
+
}.to_not raise_error
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should validate IP 127.0.0.254 and success' do
|
40
|
+
expect {
|
41
|
+
action_call(IpController, :test_action, ip: '127.0.0.254')
|
42
|
+
}.to_not raise_error
|
43
|
+
end
|
44
|
+
end
|
45
|
+
context 'skip_ip_validate' do
|
46
|
+
before(:each) do
|
47
|
+
IpFilter.configuration.ip_whitelist = proc { ['127.0.0.1/24'] }
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should validate IP and not raise error' do
|
51
|
+
expect {
|
52
|
+
action_call(IpController, :test_action_skip, ip: '146.243.3.83')
|
53
|
+
}.to_not raise_error
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
Binary file
|
@@ -0,0 +1,31 @@
|
|
1
|
+
OPEN DATA LICENSE (GeoLite Country and GeoLite City databases)
|
2
|
+
|
3
|
+
Copyright (c) 2008 MaxMind, Inc. All Rights Reserved.
|
4
|
+
|
5
|
+
All advertising materials and documentation mentioning features or use of
|
6
|
+
this database must display the following acknowledgment:
|
7
|
+
"This product includes GeoLite data created by MaxMind, available from
|
8
|
+
http://maxmind.com/"
|
9
|
+
|
10
|
+
Redistribution and use with or without modification, are permitted provided
|
11
|
+
that the following conditions are met:
|
12
|
+
1. Redistributions must retain the above copyright notice, this list of
|
13
|
+
conditions and the following disclaimer in the documentation and/or other
|
14
|
+
materials provided with the distribution.
|
15
|
+
2. All advertising materials and documentation mentioning features or use of
|
16
|
+
this database must display the following acknowledgement:
|
17
|
+
"This product includes GeoLite data created by MaxMind, available from
|
18
|
+
http://maxmind.com/"
|
19
|
+
3. "MaxMind" may not be used to endorse or promote products derived from this
|
20
|
+
database without specific prior written permission.
|
21
|
+
|
22
|
+
THIS DATABASE IS PROVIDED BY MAXMIND, INC ``AS IS'' AND ANY
|
23
|
+
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
24
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
25
|
+
DISCLAIMED. IN NO EVENT SHALL MAXMIND BE LIABLE FOR ANY
|
26
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
27
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
28
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
29
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
30
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
31
|
+
DATABASE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
Binary file
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe IpFilter do
|
4
|
+
context 'Methods' do
|
5
|
+
context '#ip_address?' do
|
6
|
+
it 'should success on IP range ' do
|
7
|
+
expect(
|
8
|
+
described_class.send(:ip_address?, '1.2.3.4/1')
|
9
|
+
).to be_truthy
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should success on IP ' do
|
13
|
+
expect(
|
14
|
+
described_class.send(:ip_address?, '1.2.3.4')
|
15
|
+
).to be_truthy
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'ip_filter'
|
2
|
+
require 'ip_filter/providers/max_mind'
|
3
|
+
|
4
|
+
describe IpFilter::Providers::MaxMind do
|
5
|
+
subject { IpFilter::Providers::MaxMind.new }
|
6
|
+
|
7
|
+
it { expect respond_to(:update!).with(0).argument }
|
8
|
+
it { expect respond_to(:config).with(0).argument }
|
9
|
+
it { expect respond_to(:folder).with(0).argument }
|
10
|
+
it { expect respond_to(:refresh_file_list).with(0).argument }
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'ip_filter'
|
2
|
+
require 'ip_filter/providers/s3'
|
3
|
+
|
4
|
+
describe IpFilter::Providers::S3 do
|
5
|
+
it { expect respond_to(:new).with(0).argument }
|
6
|
+
it { expect respond_to(:upload!).with(0).argument }
|
7
|
+
it { expect respond_to(:create_bucket).with(0).argument }
|
8
|
+
it { expect respond_to(:download!).with(0).argument }
|
9
|
+
it { expect respond_to(:download!).with(1).argument }
|
10
|
+
it { expect respond_to(:refresh_file_list).with(0).argument }
|
11
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
ENV['RAILS_ENV'] ||= 'test'
|
2
|
+
|
3
|
+
# require File.expand_path('../../rails_app/config/environment', __FILE__)
|
4
|
+
require 'rails/all'
|
5
|
+
require 'rspec'
|
6
|
+
require 'rspec/rails'
|
7
|
+
require 'ip_filter'
|
8
|
+
|
9
|
+
# Load support helpers
|
10
|
+
require 'support/enable_dallistore_cache'
|
11
|
+
require 'support/enable_redis_cache'
|
12
|
+
|
13
|
+
# Configure RSpec
|
14
|
+
RSpec.configure do |config|
|
15
|
+
# config.include Rack::Test::Methods
|
16
|
+
end
|
17
|
+
|
18
|
+
# Configure IP filter gem
|
19
|
+
IpFilter.configure do |config|
|
20
|
+
config.data_folder = File.expand_path('../fixtures', __FILE__)
|
21
|
+
config.geo_ip_dat = File.expand_path('../fixtures/GeoIP.dat', __FILE__)
|
22
|
+
config.ip_code_type = 'country_code2'
|
23
|
+
config.ip_codes = ['country_code2']
|
24
|
+
config.ip_whitelist = ['127.0.0.1/24']
|
25
|
+
config.cache = nil
|
26
|
+
config.allow_loopback = false
|
27
|
+
config.ip_exception = Proc.new {
|
28
|
+
raise ArgumentError, ('GeoIP: IP is not in whitelist')
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
# Stub Rack::Request and preload IpFilter::Request
|
33
|
+
# to get IP location as a request's attribute.
|
34
|
+
Rack::Request.send :include, IpFilter::Request
|
35
|
+
|
36
|
+
def action_call(controller, action, opts)
|
37
|
+
env = Rack::MockRequest.env_for('/', 'REMOTE_ADDR' => opts[:ip] || '127.0.0.1')
|
38
|
+
status, headers, body = controller.action(action).call(env)
|
39
|
+
ActionDispatch::TestResponse.new(status, headers, body)
|
40
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'dalli'
|
2
|
+
|
3
|
+
module EnableDallistoreCache
|
4
|
+
def activate_dallistore!
|
5
|
+
let(:cache) { Dalli::Client.new }
|
6
|
+
|
7
|
+
before do
|
8
|
+
allow(IpFilter.configuration).to receive_messages(cache: cache)
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
allow(IpFilter.configuration).to receive_messages(cache: nil)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'fakeredis'
|
2
|
+
|
3
|
+
module EnableRedisCache
|
4
|
+
def activate_redis!
|
5
|
+
let(:cache) { Redis.new }
|
6
|
+
|
7
|
+
before do
|
8
|
+
allow(IpFilter.configuration).to receive(:cache) { cache }
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
allow(IpFilter.configuration).to receive(:cache) { nil }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|