maxminder 1.0.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.
- 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
|
+
|