minfraud 1.1.0 → 1.5.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 +4 -4
- data/.github/workflows/rubocop.yml +12 -0
- data/.github/workflows/test.yml +33 -0
- data/.rubocop.yml +108 -0
- data/CHANGELOG.md +57 -0
- data/Gemfile +4 -2
- data/README.dev.md +1 -1
- data/README.md +170 -55
- data/Rakefile +9 -3
- data/bin/console +4 -3
- data/lib/maxmind/geoip2/model/city.rb +3 -3
- data/lib/maxmind/geoip2/model/country.rb +5 -5
- data/lib/maxmind/geoip2/record/traits.rb +13 -4
- data/lib/minfraud.rb +44 -6
- data/lib/minfraud/assessments.rb +113 -53
- data/lib/minfraud/components/account.rb +31 -9
- data/lib/minfraud/components/addressable.rb +73 -26
- data/lib/minfraud/components/base.rb +26 -14
- data/lib/minfraud/components/billing.rb +5 -0
- data/lib/minfraud/components/credit_card.rb +64 -20
- data/lib/minfraud/components/custom_inputs.rb +14 -3
- data/lib/minfraud/components/device.rb +45 -15
- data/lib/minfraud/components/email.rb +120 -9
- data/lib/minfraud/components/event.rb +60 -24
- data/lib/minfraud/components/order.rb +59 -22
- data/lib/minfraud/components/payment.rb +44 -9
- data/lib/minfraud/components/report/transaction.rb +50 -39
- data/lib/minfraud/components/shipping.rb +14 -5
- data/lib/minfraud/components/shopping_cart.rb +19 -12
- data/lib/minfraud/components/shopping_cart_item.rb +42 -13
- data/lib/minfraud/enum.rb +22 -8
- data/lib/minfraud/error_handler.rb +32 -25
- data/lib/minfraud/errors.rb +22 -2
- data/lib/minfraud/http_service.rb +23 -8
- data/lib/minfraud/http_service/request.rb +19 -18
- data/lib/minfraud/http_service/response.rb +19 -14
- data/lib/minfraud/model/address.rb +4 -4
- data/lib/minfraud/model/credit_card.rb +7 -7
- data/lib/minfraud/model/device.rb +2 -2
- data/lib/minfraud/model/email.rb +4 -4
- data/lib/minfraud/model/error.rb +1 -1
- data/lib/minfraud/model/insights.rb +5 -5
- data/lib/minfraud/model/ip_address.rb +20 -1
- data/lib/minfraud/model/ip_risk_reason.rb +48 -0
- data/lib/minfraud/model/issuer.rb +3 -3
- data/lib/minfraud/model/score.rb +6 -6
- data/lib/minfraud/model/shipping_address.rb +1 -1
- data/lib/minfraud/model/subscores.rb +38 -16
- data/lib/minfraud/model/warning.rb +2 -2
- data/lib/minfraud/report.rb +33 -13
- data/lib/minfraud/resolver.rb +25 -17
- data/lib/minfraud/validates.rb +187 -0
- data/lib/minfraud/version.rb +4 -1
- data/minfraud.gemspec +8 -2
- metadata +77 -10
- data/.travis.yml +0 -22
data/Rakefile
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
require 'rubocop/rake_task'
|
3
6
|
|
4
7
|
RSpec::Core::RakeTask.new(:spec)
|
5
8
|
|
6
|
-
|
9
|
+
RuboCop::RakeTask.new
|
10
|
+
|
11
|
+
task default: :spec
|
12
|
+
task default: :rubocop
|
data/bin/console
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'minfraud'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +11,5 @@ require "minfraud"
|
|
10
11
|
# require "pry"
|
11
12
|
# Pry.start
|
12
13
|
|
13
|
-
require
|
14
|
+
require 'irb'
|
14
15
|
IRB.start
|
@@ -68,9 +68,9 @@ module MaxMind
|
|
68
68
|
# @!visibility private
|
69
69
|
def initialize(record, locales)
|
70
70
|
super(record, locales)
|
71
|
-
@city
|
72
|
-
@location
|
73
|
-
@postal
|
71
|
+
@city = MaxMind::GeoIP2::Record::City.new(record['city'], locales)
|
72
|
+
@location = MaxMind::GeoIP2::Record::Location.new(record['location'])
|
73
|
+
@postal = MaxMind::GeoIP2::Record::Postal.new(record['postal'])
|
74
74
|
@subdivisions = create_subdivisions(record['subdivisions'], locales)
|
75
75
|
end
|
76
76
|
|
@@ -69,16 +69,16 @@ module MaxMind
|
|
69
69
|
|
70
70
|
# @!visibility private
|
71
71
|
def initialize(record, locales)
|
72
|
-
@continent
|
72
|
+
@continent = MaxMind::GeoIP2::Record::Continent.new(
|
73
73
|
record['continent'],
|
74
74
|
locales,
|
75
75
|
)
|
76
|
-
@country
|
76
|
+
@country = MaxMind::GeoIP2::Record::Country.new(
|
77
77
|
record['country'],
|
78
78
|
locales,
|
79
79
|
)
|
80
|
-
@maxmind
|
81
|
-
@registered_country
|
80
|
+
@maxmind = MaxMind::GeoIP2::Record::MaxMind.new(record['maxmind'])
|
81
|
+
@registered_country = MaxMind::GeoIP2::Record::Country.new(
|
82
82
|
record['registered_country'],
|
83
83
|
locales,
|
84
84
|
)
|
@@ -86,7 +86,7 @@ module MaxMind
|
|
86
86
|
record['represented_country'],
|
87
87
|
locales,
|
88
88
|
)
|
89
|
-
@traits
|
89
|
+
@traits = MaxMind::GeoIP2::Record::Traits.new(record['traits'])
|
90
90
|
end
|
91
91
|
end
|
92
92
|
end
|
@@ -33,9 +33,9 @@ module MaxMind
|
|
33
33
|
# @!visibility private
|
34
34
|
def initialize(record)
|
35
35
|
super(record)
|
36
|
-
if !record.key?('network') && record.key?('ip_address') &&
|
36
|
+
if record && !record.key?('network') && record.key?('ip_address') &&
|
37
37
|
record.key?('prefix_length')
|
38
|
-
ip
|
38
|
+
ip = IPAddr.new(record['ip_address']).mask(record['prefix_length'])
|
39
39
|
# We could use ip.prefix instead of record['prefix_length'], but that
|
40
40
|
# method only becomes available in Ruby 2.5+.
|
41
41
|
record['network'] = format('%s/%d', ip.to_s, record['prefix_length'])
|
@@ -89,7 +89,7 @@ module MaxMind
|
|
89
89
|
# NAT, this may differ from the IP address locally assigned to it. This
|
90
90
|
# attribute is returned by all end points.
|
91
91
|
#
|
92
|
-
# @return [String]
|
92
|
+
# @return [String, nil]
|
93
93
|
def ip_address
|
94
94
|
get('ip_address')
|
95
95
|
end
|
@@ -139,6 +139,15 @@ module MaxMind
|
|
139
139
|
get('is_public_proxy')
|
140
140
|
end
|
141
141
|
|
142
|
+
# This is true if the IP address is on a suspected anonymizing network
|
143
|
+
# and belongs to a residential ISP. This property is only available
|
144
|
+
# from GeoIP2 Precision Insights.
|
145
|
+
#
|
146
|
+
# @return [Boolean]
|
147
|
+
def residential_proxy?
|
148
|
+
get('is_residential_proxy')
|
149
|
+
end
|
150
|
+
|
142
151
|
# This is true if the IP address is a Tor exit node. This property is only
|
143
152
|
# available from GeoIP2 Precision Insights.
|
144
153
|
#
|
@@ -160,7 +169,7 @@ module MaxMind
|
|
160
169
|
# this is the largest network where all of the fields besides ip_address
|
161
170
|
# have the same value.
|
162
171
|
#
|
163
|
-
# @return [String]
|
172
|
+
# @return [String, nil]
|
164
173
|
def network
|
165
174
|
get('network')
|
166
175
|
end
|
data/lib/minfraud.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'faraday'
|
1
4
|
require 'minfraud'
|
2
5
|
require 'minfraud/enum'
|
6
|
+
require 'minfraud/validates'
|
3
7
|
require 'minfraud/components/base'
|
4
8
|
require 'minfraud/components/account'
|
5
9
|
require 'minfraud/components/addressable'
|
@@ -25,22 +29,56 @@ require 'minfraud/error_handler'
|
|
25
29
|
require 'minfraud/assessments'
|
26
30
|
require 'minfraud/report'
|
27
31
|
|
32
|
+
# This class holds global configuration parameters and provides a namespace
|
33
|
+
# for the gem's classes.
|
28
34
|
module Minfraud
|
29
35
|
class << self
|
30
|
-
#
|
31
|
-
#
|
36
|
+
# The MaxMind account ID that is used for authorization.
|
37
|
+
#
|
38
|
+
# @return [Integer, nil]
|
39
|
+
attr_accessor :account_id
|
40
|
+
|
41
|
+
# Enable client side validation. This is disabled by default.
|
42
|
+
#
|
43
|
+
# @return [Boolean, nil]
|
44
|
+
attr_accessor :enable_validation
|
45
|
+
|
46
|
+
# The host to use when connecting to the web service.
|
47
|
+
#
|
48
|
+
# @return [String, nil]
|
49
|
+
attr_accessor :host
|
50
|
+
|
51
|
+
# The MaxMind account ID that is used for authorization.
|
52
|
+
#
|
53
|
+
# @deprecated Use {::account_id} instead. This will be removed in the next
|
54
|
+
# major version.
|
55
|
+
#
|
56
|
+
# @return [Integer, nil]
|
32
57
|
attr_accessor :user_id
|
33
58
|
|
34
|
-
#
|
35
|
-
#
|
59
|
+
# The MaxMind license key that is used for authorization.
|
60
|
+
#
|
61
|
+
# @return [String, nil]
|
36
62
|
attr_accessor :license_key
|
37
63
|
|
38
|
-
#
|
64
|
+
# @!visibility private
|
65
|
+
attr_reader :connection
|
66
|
+
|
67
|
+
# Yield self to accept configuration settings.
|
68
|
+
#
|
69
|
+
# @yield [self]
|
39
70
|
def configure
|
40
71
|
yield self
|
72
|
+
|
73
|
+
config = Minfraud::HTTPService.configuration
|
74
|
+
@connection = Faraday.new(config[:server], {}, &config[:middleware])
|
41
75
|
end
|
42
76
|
|
43
|
-
#
|
77
|
+
# The current Minfraud configuration.
|
78
|
+
#
|
79
|
+
# @deprecated This will be removed in the next major version.
|
80
|
+
#
|
81
|
+
# @return [Hash]
|
44
82
|
def configuration
|
45
83
|
{
|
46
84
|
user_id: @user_id,
|
data/lib/minfraud/assessments.rb
CHANGED
@@ -1,56 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Minfraud
|
4
|
+
# Assessments is used to perform minFraud Score, Insights, and Factors
|
5
|
+
# requests.
|
6
|
+
#
|
7
|
+
# @see https://dev.maxmind.com/minfraud/
|
2
8
|
class Assessments
|
3
9
|
include ::Minfraud::HTTPService
|
4
10
|
include ::Minfraud::Resolver
|
5
11
|
|
6
|
-
#
|
7
|
-
#
|
12
|
+
# The Account component.
|
13
|
+
#
|
14
|
+
# @return [Minfraud::Components::Account, nil]
|
8
15
|
attr_accessor :account
|
9
16
|
|
10
|
-
#
|
11
|
-
#
|
17
|
+
# The Billing component.
|
18
|
+
#
|
19
|
+
# @return [Minfraud::Components::Billing, nil]
|
12
20
|
attr_accessor :billing
|
13
21
|
|
14
|
-
#
|
15
|
-
#
|
22
|
+
# The CreditCard component.
|
23
|
+
#
|
24
|
+
# @return [Minfraud::Components::CreditCard, nil]
|
16
25
|
attr_accessor :credit_card
|
17
26
|
|
18
|
-
#
|
19
|
-
#
|
27
|
+
# The CustomInputs component.
|
28
|
+
#
|
29
|
+
# @return [Minfraud::Components::CustomInputs, nil]
|
20
30
|
attr_accessor :custom_inputs
|
21
31
|
|
22
|
-
#
|
23
|
-
#
|
32
|
+
# The Device component.
|
33
|
+
#
|
34
|
+
# @return [Minfraud::Components::Device, nil]
|
24
35
|
attr_accessor :device
|
25
36
|
|
26
|
-
#
|
27
|
-
#
|
37
|
+
# The Email component.
|
38
|
+
#
|
39
|
+
# @return [Minfraud::Components::Email, nil]
|
28
40
|
attr_accessor :email
|
29
41
|
|
30
|
-
#
|
31
|
-
#
|
42
|
+
# The Event component.
|
43
|
+
#
|
44
|
+
# @return [Minfraud::Components::Event, nil]
|
32
45
|
attr_accessor :event
|
33
46
|
|
34
|
-
#
|
35
|
-
#
|
47
|
+
# The Order component.
|
48
|
+
#
|
49
|
+
# @return [Minfraud::Components::Order, nil]
|
36
50
|
attr_accessor :order
|
37
51
|
|
38
|
-
#
|
39
|
-
#
|
52
|
+
# The Payment component.
|
53
|
+
#
|
54
|
+
# @return [Minfraud::Components::Payment, nil]
|
40
55
|
attr_accessor :payment
|
41
56
|
|
42
|
-
#
|
43
|
-
#
|
57
|
+
# The Shipping component.
|
58
|
+
#
|
59
|
+
# @return [Minfraud::Components::Shipping, nil]
|
44
60
|
attr_accessor :shipping
|
45
61
|
|
46
|
-
#
|
47
|
-
#
|
62
|
+
# The ShoppingCart component.
|
63
|
+
#
|
64
|
+
# @return [Minfraud::Components::ShoppingCart, nil]
|
48
65
|
attr_accessor :shopping_cart
|
49
66
|
|
50
|
-
# @param
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
67
|
+
# @param params [Hash] Hash of parameters. Each key is a symbol
|
68
|
+
# corresponding to one of the available component attributes. Values may
|
69
|
+
# be component objects or hashes that will be provided to the component
|
70
|
+
# constructors.
|
71
|
+
#
|
72
|
+
# @param resolver [Minfraud::Resolver] Resolver that maps parameters to
|
73
|
+
# components.
|
54
74
|
def initialize(params = {}, resolver = ::Minfraud::Resolver)
|
55
75
|
@locales = params.delete('locales')
|
56
76
|
@locales = ['en'] if @locales.nil?
|
@@ -58,42 +78,82 @@ module Minfraud
|
|
58
78
|
resolver.assign(self, params)
|
59
79
|
end
|
60
80
|
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
)
|
76
|
-
|
77
|
-
::Minfraud::ErrorHandler.examine(response)
|
78
|
-
end
|
81
|
+
# Perform a minFraud Factors request.
|
82
|
+
#
|
83
|
+
# @return [Minfraud::HTTPService::Response]
|
84
|
+
#
|
85
|
+
# @raise [Minfraud::AuthorizationError] If there was an authentication
|
86
|
+
# problem.
|
87
|
+
#
|
88
|
+
# @raise [Minfraud::ClientError] If there was a critical problem with one
|
89
|
+
# of your inputs.
|
90
|
+
#
|
91
|
+
# @raise [Minfraud::ServerError] If the server reported an error of some
|
92
|
+
# kind.
|
93
|
+
def factors
|
94
|
+
perform_request(:factors)
|
79
95
|
end
|
80
96
|
|
81
|
-
|
82
|
-
|
83
|
-
|
97
|
+
# Perform a minFraud Insights request.
|
98
|
+
#
|
99
|
+
# @return [Minfraud::HTTPService::Response]
|
100
|
+
#
|
101
|
+
# @raise [Minfraud::AuthorizationError] If there was an authentication
|
102
|
+
# problem.
|
103
|
+
#
|
104
|
+
# @raise [Minfraud::ClientError] If there was a critical problem with one
|
105
|
+
# of your inputs.
|
106
|
+
#
|
107
|
+
# @raise [Minfraud::ServerError] If the server reported an error of some
|
108
|
+
# kind.
|
109
|
+
def insights
|
110
|
+
perform_request(:insights)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Perform a minFraud Score request.
|
114
|
+
#
|
115
|
+
# @return [Minfraud::HTTPService::Response]
|
116
|
+
#
|
117
|
+
# @raise [Minfraud::AuthorizationError] If there was an authentication
|
118
|
+
# problem.
|
119
|
+
#
|
120
|
+
# @raise [Minfraud::ClientError] If there was a critical problem with one
|
121
|
+
# of your inputs.
|
122
|
+
#
|
123
|
+
# @raise [Minfraud::ServerError] If the server reported an error of some
|
124
|
+
# kind.
|
125
|
+
def score
|
126
|
+
perform_request(:score)
|
127
|
+
end
|
84
128
|
|
85
129
|
private
|
86
|
-
|
87
|
-
|
130
|
+
|
131
|
+
def perform_request(endpoint)
|
132
|
+
raw = request.perform(
|
133
|
+
verb: :post,
|
134
|
+
endpoint: endpoint.to_s,
|
135
|
+
body: request_body,
|
136
|
+
)
|
137
|
+
|
138
|
+
response = ::Minfraud::HTTPService::Response.new(
|
139
|
+
endpoint: endpoint,
|
140
|
+
locales: @locales,
|
141
|
+
status: raw.status.to_i,
|
142
|
+
body: raw.body,
|
143
|
+
headers: raw.headers
|
144
|
+
)
|
145
|
+
|
146
|
+
::Minfraud::ErrorHandler.examine(response)
|
147
|
+
end
|
148
|
+
|
88
149
|
def request_body
|
89
|
-
MAPPING.keys.
|
90
|
-
next mem unless value = send(e)
|
150
|
+
MAPPING.keys.reduce({}) do |mem, e|
|
151
|
+
next mem unless (value = send(e))
|
152
|
+
|
91
153
|
mem.merge!(e.to_s => value.to_json)
|
92
154
|
end
|
93
155
|
end
|
94
156
|
|
95
|
-
# Creates memoized Minfraud::HTTPService::Request instance
|
96
|
-
# @return [Minfraud::HTTPService::Request] Request instance based on configuration params
|
97
157
|
def request
|
98
158
|
@request ||= Request.new(::Minfraud::HTTPService.configuration)
|
99
159
|
end
|
@@ -1,22 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Minfraud
|
2
4
|
module Components
|
5
|
+
# Account corresponds to the account object of a minFraud request.
|
6
|
+
#
|
7
|
+
# @see https://dev.maxmind.com/minfraud/#Account_(/account)
|
3
8
|
class Account < Base
|
4
|
-
|
5
|
-
|
6
|
-
#
|
7
|
-
#
|
9
|
+
include Minfraud::Validates
|
10
|
+
|
11
|
+
# A unique user ID associated with the end-user in your system. If your
|
12
|
+
# system allows the login name for the account to be changed, this should
|
13
|
+
# not be the login name for the account, but rather should be an internal
|
14
|
+
# ID that does not change. This is not your MaxMind account ID. No
|
15
|
+
# specific format is required.
|
16
|
+
#
|
17
|
+
# @return [String, nil]
|
8
18
|
attr_accessor :user_id
|
9
19
|
|
10
|
-
#
|
11
|
-
#
|
20
|
+
# An MD5 hash as a hexadecimal string of the username or login name
|
21
|
+
# associated with the account.
|
22
|
+
#
|
23
|
+
# @return [String, nil]
|
12
24
|
attr_accessor :username_md5
|
13
25
|
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# @return [Minfraud::Components::Account] an Account instance
|
26
|
+
# @param params [Hash] Hash of parameters. Each key/value should
|
27
|
+
# correspond to one of the available attributes.
|
17
28
|
def initialize(params = {})
|
18
29
|
@user_id = params[:user_id]
|
19
30
|
@username_md5 = params[:username_md5]
|
31
|
+
|
32
|
+
validate
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def validate
|
38
|
+
return if !Minfraud.enable_validation
|
39
|
+
|
40
|
+
validate_string('user_id', 255, @user_id)
|
41
|
+
validate_md5('username_md5', @username_md5)
|
20
42
|
end
|
21
43
|
end
|
22
44
|
end
|