trackguard 0.29.0 → 0.30.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1372d974514748b7cb553b4299b14de6345339c00947e24102be03ec7ac0be0d
4
- data.tar.gz: 142f9a999003624989fa47ad7a422a5bc76c504dbb8666a8e676d49970490775
3
+ metadata.gz: 49c01824d558ae2b9416ed2451b7ecc9fd864a93979afb91ea1bdce297c198b2
4
+ data.tar.gz: 429ff2b09df7c9d9571ac7a863e3200a80b6ee4a50028284af0132d18b4b2b79
5
5
  SHA512:
6
- metadata.gz: e3f01bd73d5701b53f8e7a3ef37ab3c3a43b016dc72e9388c8b8f9db225412380889b8fd13ae1df8783231fcdb6fde828f7f6807854c6ef9fa0ee6a4924dc774
7
- data.tar.gz: 9ff260dc894d2cbc70e69062bb878d394be1913faf4edd61dad5b9c10c41db2e68805985832ba8f4b7762af6a6483e0b6a0d9690447993d0c3f5ea542d8eb98c
6
+ metadata.gz: 7da72f474a80e8b89b5549de51a3ebefa6cba99da4b9b1045b7d860b2628f10ac9950efcd7acdec94d438e1f72544e7c5189335285025c5c872a64b6e8d616ea
7
+ data.tar.gz: 7ce7519b8998ce10beab775c922e7c965cb017bfa4852a843173cbc586e0c8c3af95cf6df397a12b270869b6de04411c81c958fa53966f8185b75d47d5821a89
@@ -19,6 +19,7 @@ module Trackguard
19
19
  end
20
20
 
21
21
  def track_page_view
22
+ return unless Trackguard.tracking_enabled?
22
23
  return unless request.get? || request.head?
23
24
  return unless request.format.html?
24
25
 
@@ -1,6 +1,8 @@
1
1
  module Trackguard
2
2
  module ApplicationHelper
3
3
  def trackguard_header_tags
4
+ return "".html_safe unless Trackguard.tracking_enabled?
5
+
4
6
  tags = [ tag.meta(name: "trace-id", content: @trace_id) ]
5
7
 
6
8
  case Trackguard.adapter
@@ -40,7 +40,7 @@ module Trackguard
40
40
  def analyze_visitor(visitor, views)
41
41
  count = views.size
42
42
  return if count.zero?
43
- return if visitor.whitelisted_ip&.active?
43
+ return if whitelisted?(visitor)
44
44
 
45
45
  name = name_from_ua(visitor.user_agent)
46
46
 
@@ -183,6 +183,19 @@ module Trackguard
183
183
  )
184
184
  end
185
185
 
186
+ protected
187
+
188
+ def whitelisted?(visitor)
189
+ wl = visitor.whitelisted_ip
190
+ unless wl
191
+ wl = WhitelistedIp.active.find_by(ip: visitor.ip)
192
+ wl&.update!(visitor: visitor)
193
+ end
194
+ wl&.active?
195
+ end
196
+
197
+ private
198
+
186
199
  def name_from_ua(user_agent)
187
200
  BlockedUserAgent.matching_pattern(user_agent)
188
201
  end
data/config/routes.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  Trackguard::Engine.routes.draw do
2
+ next if Trackguard.adapter.is_a?(Trackguard::Adapters::Hub)
3
+
2
4
  post "/page_views", to: "page_views#create"
3
5
 
4
6
  scope "/admin", module: "admin" do
@@ -3,6 +3,8 @@
3
3
  module Trackguard
4
4
  module Adapters
5
5
  class Base
6
+ def validate! = nil
7
+
6
8
  def blocked_user_agent?(user_agent) = raise NotImplementedError, "#{self.class}#blocked_user_agent?"
7
9
  def blocked_path?(path) = raise NotImplementedError, "#{self.class}#blocked_path?"
8
10
  def whitelisted_ip?(ip) = raise NotImplementedError, "#{self.class}#whitelisted_ip?"
@@ -10,6 +12,7 @@ module Trackguard
10
12
 
11
13
  def track_page_view(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:, tracking_layer:,
12
14
  http_method:, prefetch: false)
15
+ return unless Trackguard.tracking_enabled?
13
16
  return if blocked_user_agent?(user_agent)
14
17
  return if blocked_path?(path)
15
18
  return if path.blank? || path.start_with?(Trackguard.admin_path)
@@ -22,7 +25,10 @@ module Trackguard
22
25
  end
23
26
 
24
27
  def track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
25
- raise NotImplementedError, "#{self.class}#track_blocked_request"
28
+ return unless Trackguard.tracking_enabled?
29
+
30
+ perform_track_blocked_request(ip: ip, user_agent: user_agent, path: path,
31
+ http_method: http_method, block_reason: block_reason)
26
32
  end
27
33
 
28
34
  protected
@@ -31,6 +37,10 @@ module Trackguard
31
37
  tracking_layer:, http_method:, prefetch: false)
32
38
  raise NotImplementedError, "#{self.class}#perform_track_page_view"
33
39
  end
40
+
41
+ def perform_track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
42
+ raise NotImplementedError, "#{self.class}#perform_track_blocked_request"
43
+ end
34
44
  end
35
45
  end
36
46
  end
@@ -10,6 +10,33 @@ module Trackguard
10
10
  STALE_KEY = "trackguard/hub_rules/stale"
11
11
  ETAG_KEY = "trackguard/hub_rules/etag"
12
12
 
13
+ def validate!
14
+ return unless Trackguard.tracking_enabled?
15
+
16
+ missing = []
17
+ missing << "hub_api_key" if Trackguard.hub_api_key.blank?
18
+ missing << "hub_secret_key" if Trackguard.hub_secret_key.blank?
19
+ return if missing.empty?
20
+
21
+ raise Trackguard::ConfigurationError, <<~MSG.strip
22
+ Trackguard Hub adapter is missing required credentials: #{missing.join(', ')}.
23
+
24
+ Set them in your initializer:
25
+
26
+ Trackguard.configure do |c|
27
+ c.adapter = :hub
28
+ c.hub_api_key = ENV["TRACKGUARD_API_KEY"]
29
+ c.hub_secret_key = ENV["TRACKGUARD_SECRET_KEY"]
30
+ end
31
+
32
+ Sign up at https://trackguard.dev to create a project and generate credentials.
33
+
34
+ To use local tracking instead (no external service needed):
35
+
36
+ Trackguard.configure { |c| c.adapter = :local }
37
+ MSG
38
+ end
39
+
13
40
  def blocked_user_agent?(user_agent)
14
41
  rules.fetch(:blocked_user_agents, []).any? do |p|
15
42
  user_agent.to_s.downcase.include?(p.downcase)
@@ -30,14 +57,14 @@ module Trackguard
30
57
  rules.fetch(:flagged_ips, []).include?(ip)
31
58
  end
32
59
 
33
- def track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
60
+ protected
61
+
62
+ def perform_track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
34
63
  Trackguard::Hub::SubmitBlockedRequestJob.perform_later(
35
64
  ip: ip, user_agent: user_agent, path: path, http_method: http_method, block_reason: block_reason
36
65
  )
37
66
  end
38
67
 
39
- protected
40
-
41
68
  def perform_track_page_view(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:,
42
69
  tracking_layer:, http_method:, prefetch: false)
43
70
  return if prefetch
@@ -19,7 +19,9 @@ module Trackguard
19
19
  Visitor.flagged?(ip)
20
20
  end
21
21
 
22
- def track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
22
+ protected
23
+
24
+ def perform_track_blocked_request(ip:, user_agent:, path:, http_method:, block_reason:)
23
25
  TrackBlockedRequestJob.perform_later(
24
26
  ip: ip,
25
27
  user_agent: user_agent,
@@ -29,8 +31,6 @@ module Trackguard
29
31
  )
30
32
  end
31
33
 
32
- protected
33
-
34
34
  def perform_track_page_view(path:, ip:, user_agent:, referer:, session_id:, trace_id:, source:,
35
35
  tracking_layer:, http_method:, prefetch: false)
36
36
  TrackPageViewJob.perform_later(
@@ -21,6 +21,7 @@ module Trackguard
21
21
  end
22
22
 
23
23
  config.after_initialize do
24
+ Trackguard.adapter.validate!
24
25
  Trackguard::RackAttack.configure
25
26
  end
26
27
  end
@@ -4,28 +4,25 @@ require "rack/attack"
4
4
 
5
5
  module Trackguard
6
6
  module RackAttack
7
- # rubocop:disable Metrics/MethodLength
8
7
  def self.configure
9
- adapter = Trackguard.adapter
10
-
11
8
  ::Rack::Attack.safelist("trackguard/allow local") do |req|
12
9
  [ "127.0.0.1", "::1" ].include?(req.ip)
13
10
  end
14
11
 
15
12
  ::Rack::Attack.safelist("trackguard/allow whitelisted ips") do |req|
16
- adapter.whitelisted_ip?(req.ip)
13
+ Trackguard.adapter.whitelisted_ip?(req.ip)
17
14
  end
18
15
 
19
16
  ::Rack::Attack.blocklist("trackguard/block known scanners") do |req|
20
- adapter.blocked_user_agent?(req.user_agent)
17
+ Trackguard.adapter.blocked_user_agent?(req.user_agent)
21
18
  end
22
19
 
23
20
  ::Rack::Attack.blocklist("trackguard/block known paths") do |req|
24
- adapter.blocked_path?(req.path)
21
+ Trackguard.adapter.blocked_path?(req.path)
25
22
  end
26
23
 
27
24
  ::Rack::Attack.blocklist("trackguard/flagged visitors") do |req|
28
- adapter.flagged_visitor?(req.ip)
25
+ Trackguard.adapter.flagged_visitor?(req.ip)
29
26
  end
30
27
 
31
28
  ::Rack::Attack.throttle(
@@ -34,16 +31,15 @@ module Trackguard
34
31
  period: Trackguard.throttle_period, &:ip
35
32
  )
36
33
 
37
- subscribe_to_blocked_requests(adapter)
34
+ subscribe_to_blocked_requests
38
35
  end
39
- # rubocop:enable Metrics/MethodLength
40
36
 
41
- def self.subscribe_to_blocked_requests(adapter)
37
+ def self.subscribe_to_blocked_requests
42
38
  @subscribe_to_blocked_requests ||= ActiveSupport::Notifications.subscribe("rack.attack") do |*, payload|
43
39
  req = payload[:request]
44
40
  next unless req.env["rack.attack.match_type"] == :blocklist
45
41
 
46
- adapter.track_blocked_request(
42
+ Trackguard.adapter.track_blocked_request(
47
43
  ip: req.ip,
48
44
  user_agent: req.user_agent.to_s,
49
45
  path: req.path,
@@ -1,3 +1,3 @@
1
1
  module Trackguard
2
- VERSION = "0.29.0".freeze
2
+ VERSION = "0.30.0".freeze
3
3
  end
data/lib/trackguard.rb CHANGED
@@ -9,10 +9,84 @@ require "trackguard/adapters/local"
9
9
  require "trackguard/adapters/hub"
10
10
 
11
11
  module Trackguard
12
+ class ConfigurationError < StandardError; end
13
+
12
14
  class << self
13
- attr_writer :authenticate_admin_with, :admin_layout, :admin_path, :back_label, :local_api_token, :throttle_limit,
14
- :throttle_period, :hub_secret_key, :hub_api_key, :hub_rules_ttl
15
- attr_accessor :hub_url
15
+ def configure
16
+ @_configuring = true
17
+ yield self
18
+ ensure
19
+ @_configuring = false
20
+ end
21
+
22
+ # Setters — use configure block; direct assignment is deprecated.
23
+
24
+ def authenticate_admin_with=(value)
25
+ deprecated_direct_setter(:authenticate_admin_with)
26
+ @authenticate_admin_with = value
27
+ end
28
+
29
+ def admin_layout=(value)
30
+ deprecated_direct_setter(:admin_layout)
31
+ @admin_layout = value
32
+ end
33
+
34
+ def admin_path=(value)
35
+ deprecated_direct_setter(:admin_path)
36
+ @admin_path = value
37
+ end
38
+
39
+ def back_label=(value)
40
+ deprecated_direct_setter(:back_label)
41
+ @back_label = value
42
+ end
43
+
44
+ def local_api_token=(value)
45
+ deprecated_direct_setter(:local_api_token)
46
+ @local_api_token = value
47
+ end
48
+
49
+ def throttle_limit=(value)
50
+ deprecated_direct_setter(:throttle_limit)
51
+ @throttle_limit = value
52
+ end
53
+
54
+ def throttle_period=(value)
55
+ deprecated_direct_setter(:throttle_period)
56
+ @throttle_period = value
57
+ end
58
+
59
+ def hub_url=(value)
60
+ deprecated_direct_setter(:hub_url)
61
+ @hub_url = value
62
+ end
63
+
64
+ def hub_secret_key=(value)
65
+ deprecated_direct_setter(:hub_secret_key)
66
+ @hub_secret_key = value
67
+ end
68
+
69
+ def hub_api_key=(value)
70
+ deprecated_direct_setter(:hub_api_key)
71
+ @hub_api_key = value
72
+ end
73
+
74
+ def hub_rules_ttl=(value)
75
+ deprecated_direct_setter(:hub_rules_ttl)
76
+ @hub_rules_ttl = value
77
+ end
78
+
79
+ def adapter=(value)
80
+ deprecated_direct_setter(:adapter)
81
+ @adapter = resolve_adapter(value)
82
+ end
83
+
84
+ def disable_on_development=(value)
85
+ deprecated_direct_setter(:disable_on_development)
86
+ @disable_on_development = value
87
+ end
88
+
89
+ # Readers
16
90
 
17
91
  def authenticate_admin_with
18
92
  @authenticate_admin_with ||= proc {}
@@ -42,20 +116,36 @@ module Trackguard
42
116
  @throttle_period ||= 60
43
117
  end
44
118
 
119
+ def hub_url
120
+ @hub_url ||= "https://app.trackguard.dev"
121
+ end
122
+
45
123
  def hub_secret_key = @hub_secret_key.to_s
46
124
  def hub_api_key = @hub_api_key.to_s
47
- def hub_rules_ttl = @hub_rules_ttl ||= 5.minutes
125
+ def hub_rules_ttl = @hub_rules_ttl ||= 5.minutes
48
126
 
49
127
  def adapter
50
128
  @adapter ||= Trackguard::Adapters::Local.new
51
129
  end
52
130
 
53
- def adapter=(value)
54
- @adapter = resolve_adapter(value)
131
+ def disable_on_development
132
+ @disable_on_development.nil? || @disable_on_development
133
+ end
134
+
135
+ def tracking_enabled?
136
+ !disable_on_development || !Rails.env.development?
55
137
  end
56
138
 
57
139
  private
58
140
 
141
+ def deprecated_direct_setter(attr)
142
+ return if @_configuring
143
+
144
+ warn "[DEPRECATED] Trackguard.#{attr}= called directly. " \
145
+ "Use `Trackguard.configure { |c| c.#{attr} = ... }` instead. " \
146
+ "Direct assignment will be removed in a future version."
147
+ end
148
+
59
149
  def resolve_adapter(value)
60
150
  case value
61
151
  when Symbol then Trackguard::Adapters.const_get(value.to_s.camelize).new
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trackguard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.29.0
4
+ version: 0.30.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Krzysztof Rygielski