reputable 0.1.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/Gemfile +9 -0
- data/README.md +512 -0
- data/lib/reputable/configuration.rb +127 -0
- data/lib/reputable/connection.rb +150 -0
- data/lib/reputable/middleware.rb +266 -0
- data/lib/reputable/rails.rb +151 -0
- data/lib/reputable/reputation.rb +306 -0
- data/lib/reputable/tracker.rb +109 -0
- data/lib/reputable/version.rb +5 -0
- data/lib/reputable.rb +154 -0
- metadata +147 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Reputable
|
|
6
|
+
# Reputation management functionality
|
|
7
|
+
#
|
|
8
|
+
# RESILIENCE: All reputation methods are designed to fail silently.
|
|
9
|
+
# - Write operations return false on any failure
|
|
10
|
+
# - Read operations return nil on any failure
|
|
11
|
+
# - Boolean checks (trusted_ip?, blocked_ip?) return false on failure
|
|
12
|
+
# - Never raises exceptions during normal operation
|
|
13
|
+
module Reputation
|
|
14
|
+
VALID_STATUSES = %i[
|
|
15
|
+
trusted_verified
|
|
16
|
+
trusted_behavior
|
|
17
|
+
untrusted_challenge
|
|
18
|
+
untrusted_block
|
|
19
|
+
untrusted_ignore
|
|
20
|
+
].freeze
|
|
21
|
+
|
|
22
|
+
VALID_ENTITY_TYPES = %i[ip asn ja4 session user].freeze
|
|
23
|
+
|
|
24
|
+
class << self
|
|
25
|
+
# Apply a reputation status to an entity
|
|
26
|
+
#
|
|
27
|
+
# @param entity_type [Symbol] Type of entity (:ip, :asn, :ja4, :session, :user)
|
|
28
|
+
# @param entity_id [String] The entity identifier (IP address, ASN, etc.)
|
|
29
|
+
# @param status [Symbol] Reputation status to apply
|
|
30
|
+
# @param options [Hash] Additional options
|
|
31
|
+
# @option options [String] :reason Human-readable reason for the status
|
|
32
|
+
# @option options [Integer] :ttl TTL in seconds (0 = forever, nil = use default)
|
|
33
|
+
# @option options [Hash] :metadata Additional metadata for audit
|
|
34
|
+
# @return [Boolean] true if successfully pushed to buffer, false otherwise
|
|
35
|
+
#
|
|
36
|
+
# @example Trust an IP after payment
|
|
37
|
+
# Reputable::Reputation.apply(
|
|
38
|
+
# entity_type: :ip,
|
|
39
|
+
# entity_id: "203.0.113.45",
|
|
40
|
+
# status: :trusted_verified,
|
|
41
|
+
# reason: "payment_completed",
|
|
42
|
+
# ttl: 0,
|
|
43
|
+
# metadata: { order_id: "order_123", amount: 150.00 }
|
|
44
|
+
# )
|
|
45
|
+
#
|
|
46
|
+
# @example Block a suspicious IP
|
|
47
|
+
# Reputable::Reputation.apply(
|
|
48
|
+
# entity_type: :ip,
|
|
49
|
+
# entity_id: "198.51.100.23",
|
|
50
|
+
# status: :untrusted_block,
|
|
51
|
+
# reason: "abuse_detected"
|
|
52
|
+
# )
|
|
53
|
+
def apply(entity_type:, entity_id:, status:, **options)
|
|
54
|
+
return false unless Reputable.enabled?
|
|
55
|
+
return false unless valid_entity_type?(entity_type)
|
|
56
|
+
return false unless valid_status?(status)
|
|
57
|
+
|
|
58
|
+
entry = build_reputation_entry(entity_type, entity_id, status, options)
|
|
59
|
+
push_to_buffer(entry)
|
|
60
|
+
rescue StandardError => e
|
|
61
|
+
Reputable.logger&.debug("Reputable apply error: #{e.class} - #{e.message}")
|
|
62
|
+
false
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Convenience method: Trust an IP (verified status, forever TTL)
|
|
66
|
+
def trust_ip(ip, reason: "manual_trust", **metadata)
|
|
67
|
+
apply(
|
|
68
|
+
entity_type: :ip,
|
|
69
|
+
entity_id: ip,
|
|
70
|
+
status: :trusted_verified,
|
|
71
|
+
reason: reason,
|
|
72
|
+
ttl: 0,
|
|
73
|
+
metadata: metadata
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Convenience method: Block an IP
|
|
78
|
+
def block_ip(ip, reason: "manual_block", ttl: nil, **metadata)
|
|
79
|
+
apply(
|
|
80
|
+
entity_type: :ip,
|
|
81
|
+
entity_id: ip,
|
|
82
|
+
status: :untrusted_block,
|
|
83
|
+
reason: reason,
|
|
84
|
+
ttl: ttl,
|
|
85
|
+
metadata: metadata
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Convenience method: Challenge an IP (CAPTCHA, etc.)
|
|
90
|
+
def challenge_ip(ip, reason: "suspicious_activity", ttl: nil, **metadata)
|
|
91
|
+
apply(
|
|
92
|
+
entity_type: :ip,
|
|
93
|
+
entity_id: ip,
|
|
94
|
+
status: :untrusted_challenge,
|
|
95
|
+
reason: reason,
|
|
96
|
+
ttl: ttl,
|
|
97
|
+
metadata: metadata
|
|
98
|
+
)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Convenience method: Mark IP to be ignored in analytics
|
|
102
|
+
def ignore_ip(ip, reason: "internal_traffic", ttl: nil, **metadata)
|
|
103
|
+
apply(
|
|
104
|
+
entity_type: :ip,
|
|
105
|
+
entity_id: ip,
|
|
106
|
+
status: :untrusted_ignore,
|
|
107
|
+
reason: reason,
|
|
108
|
+
ttl: ttl,
|
|
109
|
+
metadata: metadata
|
|
110
|
+
)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Trust a user (by internal user ID)
|
|
114
|
+
def trust_user(user_id, reason: "verified_user", **metadata)
|
|
115
|
+
apply(
|
|
116
|
+
entity_type: :user,
|
|
117
|
+
entity_id: user_id.to_s,
|
|
118
|
+
status: :trusted_verified,
|
|
119
|
+
reason: reason,
|
|
120
|
+
ttl: 0,
|
|
121
|
+
metadata: metadata
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Trust a session
|
|
126
|
+
def trust_session(session_id, reason: "verified_session", ttl: nil, **metadata)
|
|
127
|
+
apply(
|
|
128
|
+
entity_type: :session,
|
|
129
|
+
entity_id: session_id,
|
|
130
|
+
status: :trusted_verified,
|
|
131
|
+
reason: reason,
|
|
132
|
+
ttl: ttl || (24 * 3600), # Default 24 hours for sessions
|
|
133
|
+
metadata: metadata
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# ========================================================================
|
|
138
|
+
# LOOKUP METHODS (O(1) Redis hash lookups)
|
|
139
|
+
# ========================================================================
|
|
140
|
+
|
|
141
|
+
# Lookup the current reputation status for an entity
|
|
142
|
+
# This is an O(1) Redis HGETALL operation
|
|
143
|
+
#
|
|
144
|
+
# @param entity_type [Symbol] Type of entity (:ip, :asn, :ja4, :session, :user)
|
|
145
|
+
# @param entity_id [String] The entity identifier
|
|
146
|
+
# @return [Hash, nil] Reputation data or nil if not found/expired/error
|
|
147
|
+
#
|
|
148
|
+
# @example Check IP reputation
|
|
149
|
+
# rep = Reputable::Reputation.lookup(:ip, "203.0.113.45")
|
|
150
|
+
# if rep && rep[:status] == "trusted_verified"
|
|
151
|
+
# # Allow through
|
|
152
|
+
# end
|
|
153
|
+
def lookup(entity_type, entity_id)
|
|
154
|
+
return nil unless Reputable.enabled?
|
|
155
|
+
return nil unless valid_entity_type?(entity_type)
|
|
156
|
+
|
|
157
|
+
key = "reputation:#{entity_type}:#{entity_id}"
|
|
158
|
+
|
|
159
|
+
data = Connection.safe_with(default: nil, context: "reputation_lookup") do |redis|
|
|
160
|
+
redis.hgetall(key)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
return nil if data.nil? || data.empty?
|
|
164
|
+
|
|
165
|
+
# Check if expired (Redis TTL should handle this, but double-check)
|
|
166
|
+
expires_at = data["expires_at"].to_i
|
|
167
|
+
if expires_at > 0 && expires_at < (Time.now.to_f * 1000).to_i
|
|
168
|
+
return nil
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
{
|
|
172
|
+
status: data["status"],
|
|
173
|
+
reason: data["reason"],
|
|
174
|
+
source: data["source"],
|
|
175
|
+
updated_at: data["updated_at"].to_i,
|
|
176
|
+
expires_at: expires_at,
|
|
177
|
+
metadata: safe_parse_json(data["metadata"])
|
|
178
|
+
}
|
|
179
|
+
rescue StandardError => e
|
|
180
|
+
Reputable.logger&.debug("Reputable lookup error: #{e.class} - #{e.message}")
|
|
181
|
+
nil
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Quick lookup for IP reputation status
|
|
185
|
+
# Returns just the status string (or nil)
|
|
186
|
+
#
|
|
187
|
+
# @param ip [String] IP address
|
|
188
|
+
# @return [String, nil] Status string or nil
|
|
189
|
+
#
|
|
190
|
+
# @example
|
|
191
|
+
# status = Reputable::Reputation.lookup_ip("203.0.113.45")
|
|
192
|
+
# # => "trusted_verified" or nil
|
|
193
|
+
def lookup_ip(ip)
|
|
194
|
+
result = lookup(:ip, ip)
|
|
195
|
+
result&.dig(:status)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Check if an IP is trusted (any trusted_* status)
|
|
199
|
+
#
|
|
200
|
+
# @param ip [String] IP address
|
|
201
|
+
# @return [Boolean]
|
|
202
|
+
#
|
|
203
|
+
# @example
|
|
204
|
+
# if Reputable::Reputation.trusted_ip?("203.0.113.45")
|
|
205
|
+
# # Skip CAPTCHA
|
|
206
|
+
# end
|
|
207
|
+
def trusted_ip?(ip)
|
|
208
|
+
status = lookup_ip(ip)
|
|
209
|
+
status&.start_with?("trusted")
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Check if an IP should be blocked
|
|
213
|
+
#
|
|
214
|
+
# @param ip [String] IP address
|
|
215
|
+
# @return [Boolean]
|
|
216
|
+
def blocked_ip?(ip)
|
|
217
|
+
lookup_ip(ip) == "untrusted_block"
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Check if an IP should be challenged
|
|
221
|
+
#
|
|
222
|
+
# @param ip [String] IP address
|
|
223
|
+
# @return [Boolean]
|
|
224
|
+
def challenged_ip?(ip)
|
|
225
|
+
lookup_ip(ip) == "untrusted_challenge"
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Lookup user reputation
|
|
229
|
+
#
|
|
230
|
+
# @param user_id [String, Integer] User ID
|
|
231
|
+
# @return [String, nil] Status string or nil
|
|
232
|
+
def lookup_user(user_id)
|
|
233
|
+
result = lookup(:user, user_id.to_s)
|
|
234
|
+
result&.dig(:status)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Check if a user is trusted
|
|
238
|
+
#
|
|
239
|
+
# @param user_id [String, Integer] User ID
|
|
240
|
+
# @return [Boolean]
|
|
241
|
+
def trusted_user?(user_id)
|
|
242
|
+
status = lookup_user(user_id)
|
|
243
|
+
status&.start_with?("trusted")
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Lookup session reputation
|
|
247
|
+
#
|
|
248
|
+
# @param session_id [String] Session ID
|
|
249
|
+
# @return [String, nil] Status string or nil
|
|
250
|
+
def lookup_session(session_id)
|
|
251
|
+
result = lookup(:session, session_id)
|
|
252
|
+
result&.dig(:status)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Check if a session is trusted
|
|
256
|
+
#
|
|
257
|
+
# @param session_id [String] Session ID
|
|
258
|
+
# @return [Boolean]
|
|
259
|
+
def trusted_session?(session_id)
|
|
260
|
+
status = lookup_session(session_id)
|
|
261
|
+
status&.start_with?("trusted")
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
private
|
|
265
|
+
|
|
266
|
+
def valid_entity_type?(entity_type)
|
|
267
|
+
VALID_ENTITY_TYPES.include?(entity_type.to_sym)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def valid_status?(status)
|
|
271
|
+
VALID_STATUSES.include?(status.to_sym)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def safe_parse_json(str)
|
|
275
|
+
return nil if str.nil? || str.empty?
|
|
276
|
+
JSON.parse(str)
|
|
277
|
+
rescue JSON::ParserError
|
|
278
|
+
nil
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def build_reputation_entry(entity_type, entity_id, status, options)
|
|
282
|
+
ttl = options[:ttl]
|
|
283
|
+
ttl = Reputable.configuration.default_ttl_for(status) if ttl.nil?
|
|
284
|
+
|
|
285
|
+
{
|
|
286
|
+
ts: (Time.now.to_f * 1000).to_i,
|
|
287
|
+
entity_type: entity_type.to_s,
|
|
288
|
+
entity_id: entity_id.to_s,
|
|
289
|
+
status: status.to_s,
|
|
290
|
+
reason: options[:reason] || "manual",
|
|
291
|
+
ttl: ttl,
|
|
292
|
+
metadata: options[:metadata]
|
|
293
|
+
}.compact
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def push_to_buffer(entry)
|
|
297
|
+
key = Reputable.configuration.reputation_buffer_key
|
|
298
|
+
|
|
299
|
+
Connection.safe_with(default: false, context: "reputation_push") do |redis|
|
|
300
|
+
redis.lpush(key, entry.to_json)
|
|
301
|
+
true
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Reputable
|
|
6
|
+
# Request tracking functionality
|
|
7
|
+
#
|
|
8
|
+
# RESILIENCE: All tracking methods are designed to fail silently.
|
|
9
|
+
# - Returns false on any failure
|
|
10
|
+
# - Never raises exceptions
|
|
11
|
+
# - Async mode runs in separate thread
|
|
12
|
+
module Tracker
|
|
13
|
+
class << self
|
|
14
|
+
# Track a request for behavioral analysis
|
|
15
|
+
#
|
|
16
|
+
# @param ip [String] Client IP address (required)
|
|
17
|
+
# @param path [String] Request path (required)
|
|
18
|
+
# @param options [Hash] Additional options
|
|
19
|
+
# @option options [String] :query Query string
|
|
20
|
+
# @option options [String] :method HTTP method (GET, POST, etc.)
|
|
21
|
+
# @option options [String] :session_id Session identifier
|
|
22
|
+
# @option options [Boolean] :session_present Whether a session cookie exists
|
|
23
|
+
# @option options [String] :user_id Internal user ID (for logged-in users)
|
|
24
|
+
# @option options [String] :fingerprint Browser fingerprint hash
|
|
25
|
+
# @option options [String] :user_agent User-Agent header
|
|
26
|
+
# @option options [String] :referer Referer header
|
|
27
|
+
# @option options [String] :country Country code (ISO 3166-1 alpha-2)
|
|
28
|
+
# @option options [Array<String>] :tags Custom classification tags
|
|
29
|
+
# @option options [Hash] :metadata Additional metadata
|
|
30
|
+
# @return [Boolean] true if successfully pushed to buffer, false otherwise
|
|
31
|
+
#
|
|
32
|
+
# @example Basic usage
|
|
33
|
+
# Reputable::Tracker.track_request(
|
|
34
|
+
# ip: "203.0.113.45",
|
|
35
|
+
# path: "/products/123"
|
|
36
|
+
# )
|
|
37
|
+
#
|
|
38
|
+
# @example Full usage
|
|
39
|
+
# Reputable::Tracker.track_request(
|
|
40
|
+
# ip: request.ip,
|
|
41
|
+
# path: request.path,
|
|
42
|
+
# query: request.query_string,
|
|
43
|
+
# method: request.request_method,
|
|
44
|
+
# session_id: session.id,
|
|
45
|
+
# session_present: true,
|
|
46
|
+
# user_agent: request.user_agent,
|
|
47
|
+
# referer: request.referer,
|
|
48
|
+
# tags: ["ctx:page:product", "trust:channel:organic"]
|
|
49
|
+
# )
|
|
50
|
+
def track_request(ip:, path:, **options)
|
|
51
|
+
return false unless Reputable.enabled?
|
|
52
|
+
|
|
53
|
+
entry = build_request_entry(ip, path, options)
|
|
54
|
+
push_to_buffer(:request, entry)
|
|
55
|
+
rescue StandardError => e
|
|
56
|
+
Reputable.logger&.debug("Reputable track_request error: #{e.class} - #{e.message}")
|
|
57
|
+
false
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Track a request asynchronously (fire-and-forget)
|
|
61
|
+
# Uses a thread to avoid blocking the request
|
|
62
|
+
# @return [Boolean] Always returns true (async)
|
|
63
|
+
def track_request_async(ip:, path:, **options)
|
|
64
|
+
return true unless Reputable.enabled?
|
|
65
|
+
|
|
66
|
+
Thread.new do
|
|
67
|
+
track_request(ip: ip, path: path, **options)
|
|
68
|
+
rescue StandardError
|
|
69
|
+
# Silently ignore all errors in async thread
|
|
70
|
+
end
|
|
71
|
+
true
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def build_request_entry(ip, path, options)
|
|
77
|
+
{
|
|
78
|
+
ts: (Time.now.to_f * 1000).to_i,
|
|
79
|
+
ip: ip,
|
|
80
|
+
path: path,
|
|
81
|
+
query: options[:query],
|
|
82
|
+
method: options[:method] || "GET",
|
|
83
|
+
session_id: options[:session_id],
|
|
84
|
+
session_present: options[:session_present],
|
|
85
|
+
user_id: options[:user_id],
|
|
86
|
+
fingerprint: options[:fingerprint],
|
|
87
|
+
user_agent: options[:user_agent],
|
|
88
|
+
referer: options[:referer],
|
|
89
|
+
country: options[:country],
|
|
90
|
+
tags: options[:tags] || [],
|
|
91
|
+
metadata: options[:metadata]
|
|
92
|
+
}.compact
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def push_to_buffer(type, entry)
|
|
96
|
+
key = case type
|
|
97
|
+
when :request then Reputable.configuration.request_buffer_key
|
|
98
|
+
when :reputation then Reputable.configuration.reputation_buffer_key
|
|
99
|
+
else return false
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
Connection.safe_with(default: false, context: "track_request") do |redis|
|
|
103
|
+
redis.lpush(key, entry.to_json)
|
|
104
|
+
true
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
data/lib/reputable.rb
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "reputable/version"
|
|
4
|
+
require_relative "reputable/configuration"
|
|
5
|
+
require_relative "reputable/connection"
|
|
6
|
+
require_relative "reputable/tracker"
|
|
7
|
+
require_relative "reputable/reputation"
|
|
8
|
+
require_relative "reputable/middleware"
|
|
9
|
+
|
|
10
|
+
# Optional Rails integration
|
|
11
|
+
begin
|
|
12
|
+
require_relative "reputable/rails"
|
|
13
|
+
rescue LoadError
|
|
14
|
+
# Rails not available, skip
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Reputable - Bot detection and reputation scoring client
|
|
18
|
+
#
|
|
19
|
+
# RESILIENCE: This gem is designed to fail silently and never break your app.
|
|
20
|
+
# - All methods return safe defaults (false/nil) on any error
|
|
21
|
+
# - Can be disabled via REPUTABLE_ENABLED=false
|
|
22
|
+
# - Circuit breaker prevents connection storms
|
|
23
|
+
# - All Redis timeouts are configurable via ENV
|
|
24
|
+
#
|
|
25
|
+
# @example Basic setup
|
|
26
|
+
# Reputable.configure do |config|
|
|
27
|
+
# config.redis_url = "rediss://user:pass@dragonfly.example.com:6379"
|
|
28
|
+
# config.tenant_id = "my-tenant"
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# @example Track a request
|
|
32
|
+
# Reputable.track_request(
|
|
33
|
+
# ip: "203.0.113.45",
|
|
34
|
+
# path: "/products/123",
|
|
35
|
+
# session_present: true
|
|
36
|
+
# )
|
|
37
|
+
#
|
|
38
|
+
# @example Apply reputation after payment
|
|
39
|
+
# Reputable.trust_ip("203.0.113.45", reason: "payment_completed")
|
|
40
|
+
#
|
|
41
|
+
# @example Disable completely via ENV
|
|
42
|
+
# # In your environment: REPUTABLE_ENABLED=false
|
|
43
|
+
#
|
|
44
|
+
module Reputable
|
|
45
|
+
class Error < StandardError; end
|
|
46
|
+
class ConfigurationError < Error; end
|
|
47
|
+
class ConnectionError < Error; end
|
|
48
|
+
|
|
49
|
+
class << self
|
|
50
|
+
attr_accessor :logger
|
|
51
|
+
|
|
52
|
+
def configuration
|
|
53
|
+
@configuration ||= Configuration.new
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def configure
|
|
57
|
+
yield(configuration)
|
|
58
|
+
Connection.reset! # Reset pool on reconfiguration
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def reset!
|
|
62
|
+
@configuration = nil
|
|
63
|
+
Connection.reset!
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Check if Reputable is enabled
|
|
67
|
+
# Can be disabled via REPUTABLE_ENABLED=false environment variable
|
|
68
|
+
# @return [Boolean]
|
|
69
|
+
def enabled?
|
|
70
|
+
configuration.enabled?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Check if Reputable is disabled
|
|
74
|
+
# @return [Boolean]
|
|
75
|
+
def disabled?
|
|
76
|
+
!enabled?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Delegate tracking methods to Tracker
|
|
80
|
+
def track_request(ip:, path:, **options)
|
|
81
|
+
Tracker.track_request(ip: ip, path: path, **options)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def track_request_async(ip:, path:, **options)
|
|
85
|
+
Tracker.track_request_async(ip: ip, path: path, **options)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Delegate reputation methods to Reputation module
|
|
89
|
+
def apply_reputation(entity_type:, entity_id:, status:, **options)
|
|
90
|
+
Reputation.apply(entity_type: entity_type, entity_id: entity_id, status: status, **options)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def trust_ip(ip, reason: "manual_trust", **metadata)
|
|
94
|
+
Reputation.trust_ip(ip, reason: reason, **metadata)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def block_ip(ip, reason: "manual_block", **metadata)
|
|
98
|
+
Reputation.block_ip(ip, reason: reason, **metadata)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def challenge_ip(ip, reason: "suspicious_activity", **metadata)
|
|
102
|
+
Reputation.challenge_ip(ip, reason: reason, **metadata)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def ignore_ip(ip, reason: "internal_traffic", **metadata)
|
|
106
|
+
Reputation.ignore_ip(ip, reason: reason, **metadata)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def trust_user(user_id, reason: "verified_user", **metadata)
|
|
110
|
+
Reputation.trust_user(user_id, reason: reason, **metadata)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def trust_session(session_id, reason: "verified_session", **metadata)
|
|
114
|
+
Reputation.trust_session(session_id, reason: reason, **metadata)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Delegate lookup methods to Reputation module (O(1) Redis lookups)
|
|
118
|
+
def lookup_reputation(entity_type, entity_id)
|
|
119
|
+
Reputation.lookup(entity_type, entity_id)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def lookup_ip(ip)
|
|
123
|
+
Reputation.lookup_ip(ip)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def trusted_ip?(ip)
|
|
127
|
+
Reputation.trusted_ip?(ip)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def blocked_ip?(ip)
|
|
131
|
+
Reputation.blocked_ip?(ip)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def challenged_ip?(ip)
|
|
135
|
+
Reputation.challenged_ip?(ip)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def lookup_user(user_id)
|
|
139
|
+
Reputation.lookup_user(user_id)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def trusted_user?(user_id)
|
|
143
|
+
Reputation.trusted_user?(user_id)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def lookup_session(session_id)
|
|
147
|
+
Reputation.lookup_session(session_id)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def trusted_session?(session_id)
|
|
151
|
+
Reputation.trusted_session?(session_id)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|