maxminder 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/max_mind.rb +36 -0
- data/lib/max_mind/enquiry.rb +120 -0
- data/lib/max_mind/lookup.rb +78 -0
- metadata +64 -0
data/lib/max_mind.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/https'
|
3
|
+
require 'logger'
|
4
|
+
require 'timeout'
|
5
|
+
|
6
|
+
require 'max_mind/enquiry'
|
7
|
+
require 'max_mind/lookup'
|
8
|
+
|
9
|
+
module MaxMind
|
10
|
+
|
11
|
+
## Exception class for generic errors.
|
12
|
+
class Error < StandardError; end
|
13
|
+
|
14
|
+
## Exception class for when connection to the max mind service fails.
|
15
|
+
class ConnectionError < Error; end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
## Licence key for accessing the maxmind service. A trial key
|
19
|
+
## can be requested from http://www.maxmind.com/app/ccv2r_signup
|
20
|
+
attr_accessor :licence_key
|
21
|
+
|
22
|
+
## The server (or servers) to accept your request
|
23
|
+
attr_accessor :endpoints
|
24
|
+
|
25
|
+
## Return a default array of end points if none are specified.
|
26
|
+
def endpoints
|
27
|
+
@endpoints || ["https://minfraud1.maxmind.com/app/ccv2r", "https://minfraud3.maxmind.com/app/ccv2r"]
|
28
|
+
end
|
29
|
+
|
30
|
+
## Return a logger object for this MaxMind interaction
|
31
|
+
def logger
|
32
|
+
@logger ||= Logger.new(STDOUT)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module MaxMind
|
2
|
+
class Enquiry
|
3
|
+
|
4
|
+
attr_accessor :attributes
|
5
|
+
|
6
|
+
def initialize(attributes = {})
|
7
|
+
self.attributes = attributes if attributes.is_a?(Hash)
|
8
|
+
end
|
9
|
+
|
10
|
+
## The attributes here will be sent to max mind
|
11
|
+
def sendable_attributes
|
12
|
+
hash = Hash.new
|
13
|
+
for field, data in attributes
|
14
|
+
hash[mapping[field]] = data
|
15
|
+
end
|
16
|
+
hash['license_key'] = MaxMind.licence_key
|
17
|
+
hash
|
18
|
+
end
|
19
|
+
|
20
|
+
## Run the check and return a hash of responses from the server.
|
21
|
+
def lookup
|
22
|
+
MaxMind.logger.debug "Beginning lookups"
|
23
|
+
MaxMind.logger.debug "('#{sendable_attributes.inspect}')"
|
24
|
+
for endpoint in MaxMind.endpoints
|
25
|
+
MaxMind.logger.debug "Attempting lookup on #{endpoint}"
|
26
|
+
uri = URI.parse(endpoint)
|
27
|
+
req = Net::HTTP::Post.new(uri.path)
|
28
|
+
req.set_form_data(sendable_attributes)
|
29
|
+
res = Net::HTTP.new(uri.host, uri.port)
|
30
|
+
if uri.scheme == 'https'
|
31
|
+
res.use_ssl = true
|
32
|
+
res.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
33
|
+
end
|
34
|
+
|
35
|
+
begin
|
36
|
+
case res = res.request(req)
|
37
|
+
when Net::HTTPSuccess
|
38
|
+
return Lookup.new(res.body)
|
39
|
+
else
|
40
|
+
MaxMind.logger.debug "Error on #{endpoint} (#{res.class.to_s})"
|
41
|
+
next
|
42
|
+
end
|
43
|
+
rescue Timeout::Error
|
44
|
+
MaxMind.logger.debug "Timed out connecting to #{endpoint}"
|
45
|
+
next
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
## Set or get an attribute from the attributes hash or raise
|
53
|
+
## no method error if it doesn't exist in our mapping.
|
54
|
+
def method_missing(name, value = nil)
|
55
|
+
attribute_name = name.to_s.gsub(/\=\z/, '').to_sym
|
56
|
+
return super unless mapping.keys.include?(attribute_name)
|
57
|
+
value ? (attributes[attribute_name] = value) : attributes[attribute_name]
|
58
|
+
end
|
59
|
+
|
60
|
+
## Various methods to make setting fields in the attributes hash
|
61
|
+
## a little easier.
|
62
|
+
attr_reader :email, :username, :password
|
63
|
+
|
64
|
+
def email=(value)
|
65
|
+
@email = value
|
66
|
+
self.attributes[:email_md5] = Digest::MD5.hexdigest(value)
|
67
|
+
self.attributes[:email_domain] = value.split('@', 2).last
|
68
|
+
value
|
69
|
+
end
|
70
|
+
|
71
|
+
def username=(value)
|
72
|
+
@username = value
|
73
|
+
self.attributes[:username_md5] = Digest::MD5.hexdigest(value)
|
74
|
+
value
|
75
|
+
end
|
76
|
+
|
77
|
+
def password=(value)
|
78
|
+
@password = value
|
79
|
+
self.attributes[:password_md5] = Digest::MD5.hexdigest(value)
|
80
|
+
value
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
## This method returns a mapping of method names to the appropriate attribute
|
86
|
+
## expected by the MaxMind API.
|
87
|
+
def mapping
|
88
|
+
{
|
89
|
+
:ip_address => 'i',
|
90
|
+
:forwarded_ip => 'forwardedIP',
|
91
|
+
|
92
|
+
:city => 'city',
|
93
|
+
:region => 'region',
|
94
|
+
:postal => 'postal',
|
95
|
+
:country => 'country',
|
96
|
+
|
97
|
+
:shipping_address => 'shipAddr',
|
98
|
+
:shipping_city => 'shipCity',
|
99
|
+
:shipping_region => 'shipRegion',
|
100
|
+
:shipping_postal => 'shipPostal',
|
101
|
+
:shipping_country => 'shipCountry',
|
102
|
+
|
103
|
+
:email_domain => 'domain',
|
104
|
+
:email_md5 => 'emailMD5',
|
105
|
+
:username_md5 => 'usernameMD5',
|
106
|
+
:password_md5 => 'passwordMD5',
|
107
|
+
|
108
|
+
:bin => 'bin',
|
109
|
+
:bin_name => 'binName',
|
110
|
+
:bin_phone => 'binPhone',
|
111
|
+
|
112
|
+
:transaction_id => 'txnID',
|
113
|
+
:session_id => 'sessionID',
|
114
|
+
:user_agent => 'user_agent',
|
115
|
+
:accept_language => 'accept_language'
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module MaxMind
|
2
|
+
class Lookup
|
3
|
+
|
4
|
+
def initialize(raw)
|
5
|
+
@attributes = Hash.new
|
6
|
+
for key, value in raw.split(';').map{|r| r.split('=', 2)}
|
7
|
+
field_name, type = mapping[key]
|
8
|
+
@attributes[field_name] = parse_value(value, type)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
## Return the appropriate attribute from the attributes hash.
|
13
|
+
def method_missing(name)
|
14
|
+
super unless mapping.values.map(&:first).include?(name.to_sym)
|
15
|
+
@attributes[name.to_sym]
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
## This is the mapping of response attributes to their local variable name
|
21
|
+
## and type of data which is expected.
|
22
|
+
def mapping
|
23
|
+
{
|
24
|
+
'countryMatch' => [:country_match, :boolean],
|
25
|
+
'countryCode' => [:country_code, :string],
|
26
|
+
'highRiskCountry' => [:high_risk_country?, :boolean],
|
27
|
+
'distance' => [:distance, :integer],
|
28
|
+
'ip_region' => [:ip_region, :string],
|
29
|
+
'ip_city' => [:ip_city, :string],
|
30
|
+
'ip_latitude' => [:ip_latitude, :decimal],
|
31
|
+
'ip_longitude' => [:ip_longitude, :decimal],
|
32
|
+
'ip_isp' => [:ip_isp, :string],
|
33
|
+
'ip_org' => [:ip_organisation, :string],
|
34
|
+
'anonymousProxy' => [:anonymous_proxy?, :boolean],
|
35
|
+
'proxyScore' => [:proxy_score, :float],
|
36
|
+
'isTransProxy' => [:transparent_proxy?, :boolean],
|
37
|
+
'freeMail' => [:free_email?, :boolean],
|
38
|
+
'carderEmail' => [:high_risk_email?, :boolean],
|
39
|
+
'highRiskUsername' => [:high_risk_username?, :boolean],
|
40
|
+
'highRiskPassword' => [:high_risk_password?, :boolean],
|
41
|
+
'binMatch' => [:bin_match?, :boolean],
|
42
|
+
'binCountry' => [:bin_country, :string],
|
43
|
+
'binNameMatch' => [:bin_name_match?, :boolean],
|
44
|
+
'binName' => [:bin_name, :string],
|
45
|
+
'binPhoneMatch' => [:bin_phone_match?, :boolean],
|
46
|
+
'binPhone' => [:bin_phone, :string],
|
47
|
+
'custPhoneInBillingLoc' => [:phone_in_billing_location?, :boolean],
|
48
|
+
'shipForward' => [:mail_drop_address?, :boolean],
|
49
|
+
'cityPostalMatch' => [:city_matches_postal?, :boolean],
|
50
|
+
'shipCityPostalMatch' => [:ship_city_matches_postal?, :boolean],
|
51
|
+
'score' => [:score, :decimal],
|
52
|
+
'explanation' => [:explanation, :string],
|
53
|
+
'riskScore' => [:risk_score, :decimal],
|
54
|
+
'queriesRemaining' => [:queries_remaining, :decimal],
|
55
|
+
'maxmindID' => [:id, :string],
|
56
|
+
'err' => [:error, :string]
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
## Return a value as an instance of an appropriate ruby object.
|
61
|
+
def parse_value(value, type = :string)
|
62
|
+
case type
|
63
|
+
when :integer then value.to_i
|
64
|
+
when :decimal then value.to_f
|
65
|
+
when :boolean
|
66
|
+
case value
|
67
|
+
when 'Yes' then true
|
68
|
+
when 'No' then false
|
69
|
+
else
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
else
|
73
|
+
value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: maxminder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Adam Cooke
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-09-05 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description:
|
22
|
+
email: adam@atechmedia.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- lib/max_mind/enquiry.rb
|
31
|
+
- lib/max_mind/lookup.rb
|
32
|
+
- lib/max_mind.rb
|
33
|
+
has_rdoc: false
|
34
|
+
homepage: http://github.com/adamcooke/maxminder
|
35
|
+
licenses: []
|
36
|
+
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options: []
|
39
|
+
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
version: "0"
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
requirements: []
|
57
|
+
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 1.3.6
|
60
|
+
signing_key:
|
61
|
+
specification_version: 3
|
62
|
+
summary: Simple (non-bloated) Ruby library for MaxMind
|
63
|
+
test_files: []
|
64
|
+
|