Class: SpreeCmCommissioner::FraudCheck

Inherits:
Object
  • Object
show all
Includes:
Spree::ServiceModule::Base
Defined in:
app/services/spree_cm_commissioner/fraud_check.rb

Constant Summary collapse

DEFAULTS =
{
  'max_votes_per_minute_per_user' => 5,
  'max_votes_per_minute_per_ip' => 10,
  'max_accounts_per_device' => 3,
  'block_vpn' => false
}.freeze
RATE_WINDOW =

seconds

60
VPN_CACHE_TTL =

1 hour

3600

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#contestantObject (readonly)

Returns the value of attribute contestant.



27
28
29
# File 'app/services/spree_cm_commissioner/fraud_check.rb', line 27

def contestant
  @contestant
end

#paramsObject (readonly)

Returns the value of attribute params.



27
28
29
# File 'app/services/spree_cm_commissioner/fraud_check.rb', line 27

def params
  @params
end

#requestObject (readonly)

Returns the value of attribute request.



27
28
29
# File 'app/services/spree_cm_commissioner/fraud_check.rb', line 27

def request
  @request
end

#userObject (readonly)

Returns the value of attribute user.



27
28
29
# File 'app/services/spree_cm_commissioner/fraud_check.rb', line 27

def user
  @user
end

#voting_sessionObject (readonly)

Returns the value of attribute voting_session.



27
28
29
# File 'app/services/spree_cm_commissioner/fraud_check.rb', line 27

def voting_session
  @voting_session
end

Instance Method Details

#call(voting_session:, user:, params:, request:, contestant: nil) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'app/services/spree_cm_commissioner/fraud_check.rb', line 29

def call(voting_session:, user:, params:, request:, contestant: nil)
  @voting_session = voting_session
  @contestant     = contestant
  @user           = user
  @params         = params
  @request        = request

  return success(nil) if fraud_config.blank?

  check_user_rate_limit!
  check_ip_rate_limit!
  
  check_vpn_block!

  success(nil)
rescue Redis::BaseError, ConnectionPool::TimeoutError => e
  # Fail-open: a Redis outage should not take down voting entirely.
  # Catches both Redis protocol errors and connection pool exhaustion/timeout.
  # Must be rescued BEFORE RuntimeError because Redis::BaseError < RuntimeError
  # in redis gem v4.x — placing it after RuntimeError would let the wrong clause win.
  # Log the error so it is visible in monitoring, but let the vote through.
  # If the policy should be fail-closed instead, replace the rescue body
  # with: failure(I18n.t('voting.errors.service_unavailable'))
  CmAppLogger.error(
    label: 'FraudCheck Redis error — failing open',
    data: {
      voting_session_id: voting_session.id,
      user_id: user&.id,
      error_class: e.class.name,
      error_message: e.message
    }
  )
  success(nil)
rescue RuntimeError => e
  failure(nil, e.message)
end