ip_filter 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|