ip_filter 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +1 -0
  4. data/CHANGELOG +29 -0
  5. data/Gemfile.lock +117 -0
  6. data/LICENSE +20 -0
  7. data/README.rdoc +232 -0
  8. data/Rakefile +6 -0
  9. data/data/geoip/country_code.yml +255 -0
  10. data/data/geoip/country_code3.yml +255 -0
  11. data/data/geoip/country_continent.yml +255 -0
  12. data/data/geoip/country_name.yml +255 -0
  13. data/data/geoip/time_zone.yml +677 -0
  14. data/lib/geoip.rb +559 -0
  15. data/lib/ip_filter.rb +100 -0
  16. data/lib/ip_filter/cache.rb +30 -0
  17. data/lib/ip_filter/cache/dallistore.rb +39 -0
  18. data/lib/ip_filter/cache/redis.rb +26 -0
  19. data/lib/ip_filter/configuration.rb +47 -0
  20. data/lib/ip_filter/controller/geo_ip_lookup.rb +78 -0
  21. data/lib/ip_filter/lookups/base.rb +60 -0
  22. data/lib/ip_filter/lookups/geoip.rb +41 -0
  23. data/lib/ip_filter/providers/max_mind.rb +52 -0
  24. data/lib/ip_filter/providers/s3.rb +51 -0
  25. data/lib/ip_filter/railtie.rb +23 -0
  26. data/lib/ip_filter/request.rb +14 -0
  27. data/lib/ip_filter/results/base.rb +39 -0
  28. data/lib/ip_filter/results/geoip.rb +19 -0
  29. data/lib/ip_filter/version.rb +3 -0
  30. data/spec/cache/dallistore_spec.rb +16 -0
  31. data/spec/cache/redis_spec.rb +56 -0
  32. data/spec/controller/ip_controller_spec.rb +56 -0
  33. data/spec/fixtures/GeoIP.dat +0 -0
  34. data/spec/fixtures/LICENSE.txt +31 -0
  35. data/spec/fixtures/country.dat +0 -0
  36. data/spec/ip_filter_spec.rb +19 -0
  37. data/spec/providers/max_mind_spec.rb +11 -0
  38. data/spec/providers/s3_spec.rb +11 -0
  39. data/spec/spec_helper.rb +40 -0
  40. data/spec/support/enable_dallistore_cache.rb +15 -0
  41. data/spec/support/enable_redis_cache.rb +15 -0
  42. 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,3 @@
1
+ module IpFilter
2
+ VERSION = '0.8.0'
3
+ 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
@@ -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