beskar 0.0.2 → 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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +143 -0
  3. data/README.md +298 -110
  4. data/app/controllers/beskar/application_controller.rb +170 -0
  5. data/app/controllers/beskar/banned_ips_controller.rb +280 -0
  6. data/app/controllers/beskar/dashboard_controller.rb +70 -0
  7. data/app/controllers/beskar/security_events_controller.rb +182 -0
  8. data/app/controllers/concerns/beskar/controllers/security_tracking.rb +6 -6
  9. data/app/models/beskar/banned_ip.rb +68 -27
  10. data/app/models/beskar/security_event.rb +14 -0
  11. data/app/services/beskar/banned_ip_manager.rb +78 -0
  12. data/app/views/beskar/banned_ips/edit.html.erb +259 -0
  13. data/app/views/beskar/banned_ips/index.html.erb +361 -0
  14. data/app/views/beskar/banned_ips/new.html.erb +310 -0
  15. data/app/views/beskar/banned_ips/show.html.erb +310 -0
  16. data/app/views/beskar/dashboard/index.html.erb +280 -0
  17. data/app/views/beskar/security_events/index.html.erb +309 -0
  18. data/app/views/beskar/security_events/show.html.erb +307 -0
  19. data/app/views/layouts/beskar/application.html.erb +647 -5
  20. data/config/routes.rb +41 -0
  21. data/lib/beskar/configuration.rb +24 -10
  22. data/lib/beskar/engine.rb +4 -4
  23. data/lib/beskar/logger.rb +293 -0
  24. data/lib/beskar/middleware/request_analyzer.rb +128 -53
  25. data/lib/beskar/models/security_trackable_authenticable.rb +11 -11
  26. data/lib/beskar/models/security_trackable_devise.rb +5 -5
  27. data/lib/beskar/models/security_trackable_generic.rb +12 -12
  28. data/lib/beskar/services/account_locker.rb +12 -12
  29. data/lib/beskar/services/geolocation_service.rb +8 -8
  30. data/lib/beskar/services/ip_whitelist.rb +2 -2
  31. data/lib/beskar/services/waf.rb +307 -78
  32. data/lib/beskar/version.rb +1 -1
  33. data/lib/beskar.rb +1 -0
  34. data/lib/generators/beskar/install/install_generator.rb +158 -0
  35. data/lib/generators/beskar/install/templates/initializer.rb.tt +177 -0
  36. data/lib/tasks/beskar_tasks.rake +11 -2
  37. metadata +35 -6
  38. data/lib/beskar/templates/beskar_initializer.rb +0 -107
@@ -22,7 +22,7 @@ module Beskar
22
22
  def handle_high_risk_lock(security_event, request)
23
23
  reason = determine_lock_reason(security_event)
24
24
 
25
- Rails.logger.warn "[Beskar] Rails auth high-risk lock detected: #{reason}"
25
+ Beskar::Logger.warn("Rails auth high-risk lock detected: #{reason}")
26
26
 
27
27
  # Destroy all sessions to immediately lock out attacker
28
28
  destroy_all_sessions(except: request.session.id)
@@ -39,18 +39,18 @@ module Beskar
39
39
  if except
40
40
  # Keep current session but destroy all others
41
41
  sessions.where.not(id: except).destroy_all
42
- Rails.logger.info "[Beskar] Destroyed #{sessions.count} sessions except current"
42
+ Beskar::Logger.info("Destroyed #{sessions.count} sessions except current")
43
43
  else
44
44
  # Destroy ALL sessions including current
45
45
  count = sessions.count
46
46
  sessions.destroy_all
47
- Rails.logger.info "[Beskar] Destroyed all #{count} sessions"
47
+ Beskar::Logger.info("Destroyed all #{count} sessions")
48
48
  end
49
49
  else
50
- Rails.logger.warn "[Beskar] Model does not have sessions association, cannot destroy sessions"
50
+ Beskar::Logger.warn("Model does not have sessions association, cannot destroy sessions")
51
51
  end
52
52
  rescue => e
53
- Rails.logger.error "[Beskar] Failed to destroy sessions: #{e.message}"
53
+ Beskar::Logger.error("Failed to destroy sessions: #{e.message}")
54
54
  end
55
55
 
56
56
  # Determine if emergency password reset is warranted
@@ -125,10 +125,10 @@ module Beskar
125
125
  notify_security_team_of_reset(reason, security_event)
126
126
  end
127
127
 
128
- Rails.logger.warn "[Beskar] Emergency password reset performed for user #{id}, reason: #{reason}"
128
+ Beskar::Logger.warn("Emergency password reset performed for user #{id}, reason: #{reason}")
129
129
 
130
130
  rescue => e
131
- Rails.logger.error "[Beskar] Failed to perform emergency password reset: #{e.message}"
131
+ Beskar::Logger.error("Failed to perform emergency password reset: #{e.message}")
132
132
 
133
133
  # Create failed reset event
134
134
  security_events.create!(
@@ -149,18 +149,18 @@ module Beskar
149
149
  def send_emergency_reset_notification(reason)
150
150
  # This should be implemented by the application
151
151
  # Example: UserMailer.emergency_password_reset(self, reason).deliver_later
152
- Rails.logger.info "[Beskar] Would send emergency reset notification to user #{id}"
152
+ Beskar::Logger.info("Would send emergency reset notification to user #{id}")
153
153
  rescue => e
154
- Rails.logger.error "[Beskar] Failed to send emergency reset notification: #{e.message}"
154
+ Beskar::Logger.error("Failed to send emergency reset notification: #{e.message}")
155
155
  end
156
156
 
157
157
  # Notify security team about emergency password reset
158
158
  def notify_security_team_of_reset(reason, security_event)
159
159
  # This should be implemented by the application
160
160
  # Example: SecurityMailer.emergency_reset_alert(self, reason, security_event).deliver_later
161
- Rails.logger.info "[Beskar] Would notify security team about reset for user #{id}"
161
+ Beskar::Logger.info("Would notify security team about reset for user #{id}")
162
162
  rescue => e
163
- Rails.logger.error "[Beskar] Failed to notify security team: #{e.message}"
163
+ Beskar::Logger.error("Failed to notify security team: #{e.message}")
164
164
  end
165
165
  end
166
166
  end
@@ -21,7 +21,7 @@ module Beskar
21
21
  def track_successful_login
22
22
  # Skip tracking if disabled in configuration
23
23
  unless Beskar.configuration.track_successful_logins?
24
- Rails.logger.debug "[Beskar] Successful login tracking disabled in configuration"
24
+ Beskar::Logger.debug("Successful login tracking disabled in configuration")
25
25
  return
26
26
  end
27
27
 
@@ -29,7 +29,7 @@ module Beskar
29
29
  track_authentication_event(current_request, :success)
30
30
  end
31
31
  rescue => e
32
- Rails.logger.warn "[Beskar] Failed to track successful login: #{e.message}"
32
+ Beskar::Logger.warn("Failed to track successful login: #{e.message}")
33
33
  nil
34
34
  end
35
35
 
@@ -45,7 +45,7 @@ module Beskar
45
45
  .exists?
46
46
 
47
47
  if recent_lock
48
- Rails.logger.warn "[Beskar] High-risk lock detected, signing out user #{id}"
48
+ Beskar::Logger.warn("High-risk lock detected, signing out user #{id}")
49
49
  auth.logout
50
50
  throw :warden, message: :account_locked_due_to_high_risk
51
51
  end
@@ -66,14 +66,14 @@ module Beskar
66
66
  Warden::Manager.current_request
67
67
  end
68
68
  rescue => e
69
- Rails.logger.debug "[Beskar] Could not get request from context: #{e.message}"
69
+ Beskar::Logger.debug("Could not get request from context: #{e.message}")
70
70
  nil
71
71
  end
72
72
 
73
73
  # Devise-specific: Handle high risk lock by creating lock event
74
74
  # The actual sign-out is handled by Warden callback in engine.rb
75
75
  def handle_high_risk_lock(security_event, request)
76
- Rails.logger.debug "[Beskar] Devise account locked - Warden callback will handle sign-out"
76
+ Beskar::Logger.debug("Devise account locked - Warden callback will handle sign-out")
77
77
  # The lock event is already created by AccountLocker service
78
78
  # The Warden callback will detect it and perform the actual sign-out
79
79
  end
@@ -16,7 +16,7 @@ module Beskar
16
16
  def track_failed_authentication(request, scope)
17
17
  # Skip tracking if disabled in configuration
18
18
  unless Beskar.configuration.track_failed_logins?
19
- Rails.logger.debug "[Beskar] Failed login tracking disabled in configuration"
19
+ Beskar::Logger.debug("Failed login tracking disabled in configuration")
20
20
  return
21
21
  end
22
22
 
@@ -77,14 +77,14 @@ module Beskar
77
77
 
78
78
  if password&.length.to_i > 50
79
79
  score += 10
80
- Rails.logger.info "[Beskar] Suspicious password length: #{password.length}, adding 10 risk"
80
+ Beskar::Logger.info("Suspicious password length: #{password.length}, adding 10 risk")
81
81
  end
82
82
 
83
83
  # Use geolocation service for location-based risk
84
84
  geolocation_service = Beskar::Services::GeolocationService.new
85
85
  geo_score = geolocation_service.calculate_location_risk(request.ip)
86
86
  score += geo_score
87
- Rails.logger.info "[Beskar] Geolocation risk: #{geo_score}"
87
+ Beskar::Logger.info("Geolocation risk: #{geo_score}")
88
88
 
89
89
  [score, 100].min # Cap at 100
90
90
  end
@@ -96,10 +96,10 @@ module Beskar
96
96
 
97
97
  # Check if tracking is enabled for this event type
98
98
  if result == :success && !Beskar.configuration.track_successful_logins?
99
- Rails.logger.debug "[Beskar] Successful login tracking disabled in configuration"
99
+ Beskar::Logger.debug("Successful login tracking disabled in configuration")
100
100
  return
101
101
  elsif result == :failure && !Beskar.configuration.track_failed_logins?
102
- Rails.logger.debug "[Beskar] Failed login tracking disabled in configuration"
102
+ Beskar::Logger.debug("Failed login tracking disabled in configuration")
103
103
  return
104
104
  end
105
105
 
@@ -134,14 +134,14 @@ module Beskar
134
134
  def analyze_suspicious_patterns_async
135
135
  # Skip analysis if disabled in configuration
136
136
  unless Beskar.configuration.auto_analyze_patterns?
137
- Rails.logger.debug "[Beskar] Auto pattern analysis disabled in configuration"
137
+ Beskar::Logger.debug("Auto pattern analysis disabled in configuration")
138
138
  return
139
139
  end
140
140
 
141
141
  # Queue background job for detailed analysis
142
142
  Beskar::SecurityAnalysisJob.perform_later(self.id, "login_success") if defined?(Beskar::SecurityAnalysisJob)
143
143
  rescue => e
144
- Rails.logger.warn "[Beskar] Failed to queue security analysis: #{e.message}"
144
+ Beskar::Logger.warn("Failed to queue security analysis: #{e.message}")
145
145
  end
146
146
 
147
147
  def recent_failed_attempts(within: 1.hour)
@@ -202,18 +202,18 @@ module Beskar
202
202
  # Mobile device login during late hours
203
203
  if device_detector.mobile?(request.user_agent) && Time.current.hour.between?(22, 6)
204
204
  score += 5
205
- Rails.logger.info "[Beskar] Mobile device login during late hours, adding 5 risk"
205
+ Beskar::Logger.info("Mobile device login during late hours, adding 5 risk")
206
206
  end
207
207
 
208
208
  # Account-specific risk factors
209
209
  if recent_failed_attempts(within: 10.minutes).count >= 2
210
210
  score += 20
211
- Rails.logger.info "[Beskar] Recent failed attempts: #{recent_failed_attempts(within: 10.minutes).count}, adding 20 risk"
211
+ Beskar::Logger.info("Recent failed attempts: #{recent_failed_attempts(within: 10.minutes).count}, adding 20 risk")
212
212
  end
213
213
 
214
214
  # ADAPTIVE LEARNING: Check if this is an established pattern
215
215
  if result == :success && established_pattern?(request)
216
- Rails.logger.info "[Beskar] Established pattern detected, reducing risk score"
216
+ Beskar::Logger.info("Established pattern detected, reducing risk score")
217
217
  score = [score * 0.3, 25].min.to_i # Reduce to 30% of original, cap at 25
218
218
  end
219
219
 
@@ -260,7 +260,7 @@ module Beskar
260
260
  )
261
261
 
262
262
  if locker.lock_if_necessary!
263
- Rails.logger.warn "[Beskar] Account locked due to high risk score: #{security_event.risk_score}"
263
+ Beskar::Logger.warn("Account locked due to high risk score: #{security_event.risk_score}")
264
264
 
265
265
  # Trigger auth-system-specific lock handling
266
266
  handle_high_risk_lock(security_event, request)
@@ -294,7 +294,7 @@ module Beskar
294
294
 
295
295
  # Override this in auth-system-specific modules
296
296
  def handle_high_risk_lock(security_event, request)
297
- Rails.logger.debug "[Beskar] High risk lock handled - override in specific module"
297
+ Beskar::Logger.debug("High risk lock handled - override in specific module")
298
298
  end
299
299
 
300
300
  # ADAPTIVE LEARNING: Check if this login pattern is established
@@ -66,7 +66,7 @@ module Beskar
66
66
  when :custom
67
67
  lock_with_custom_strategy
68
68
  else
69
- Rails.logger.warn "[Beskar::AccountLocker] Unknown lock strategy: #{strategy}"
69
+ Beskar::Logger.warn("Unknown lock strategy: #{strategy}", component: :AccountLocker)
70
70
  false
71
71
  end
72
72
 
@@ -133,7 +133,7 @@ module Beskar
133
133
  # Lock account using Devise's lockable module
134
134
  def lock_with_devise_lockable
135
135
  unless devise_lockable_available?
136
- Rails.logger.warn "[Beskar::AccountLocker] Devise lockable not available for #{user.class.name}"
136
+ Beskar::Logger.warn("Devise lockable not available for #{user.class.name}", component: :AccountLocker)
137
137
  return false
138
138
  end
139
139
 
@@ -146,10 +146,10 @@ module Beskar
146
146
  user.update_column(:locked_at, Time.current)
147
147
  end
148
148
 
149
- Rails.logger.info "[Beskar::AccountLocker] Locked account #{user.id} (#{user.class.name}) - Risk: #{risk_score}, Reason: #{reason}"
149
+ Beskar::Logger.info("Locked account #{user.id} (#{user.class.name}) - Risk: #{risk_score}, Reason: #{reason}", component: :AccountLocker)
150
150
  true
151
151
  rescue => e
152
- Rails.logger.error "[Beskar::AccountLocker] Failed to lock account: #{e.message}"
152
+ Beskar::Logger.error("Failed to lock account: #{e.message}", component: :AccountLocker)
153
153
  false
154
154
  end
155
155
  end
@@ -157,16 +157,16 @@ module Beskar
157
157
  # Unlock account using Devise's lockable module
158
158
  def unlock_with_devise_lockable
159
159
  unless devise_lockable_available?
160
- Rails.logger.warn "[Beskar::AccountLocker] Devise lockable not available for #{user.class.name}"
160
+ Beskar::Logger.warn("Devise lockable not available for #{user.class.name}", component: :AccountLocker)
161
161
  return false
162
162
  end
163
163
 
164
164
  begin
165
165
  user.unlock_access!
166
- Rails.logger.info "[Beskar::AccountLocker] Unlocked account #{user.id} (#{user.class.name})"
166
+ Beskar::Logger.info("Unlocked account #{user.id} (#{user.class.name})", component: :AccountLocker)
167
167
  true
168
168
  rescue => e
169
- Rails.logger.error "[Beskar::AccountLocker] Failed to unlock account: #{e.message}"
169
+ Beskar::Logger.error("Failed to unlock account: #{e.message}", component: :AccountLocker)
170
170
  false
171
171
  end
172
172
  end
@@ -178,13 +178,13 @@ module Beskar
178
178
  # 2. Checking this in authentication callbacks
179
179
  # 3. Implementing unlock logic
180
180
 
181
- Rails.logger.warn "[Beskar::AccountLocker] Custom lock strategy not implemented"
181
+ Beskar::Logger.warn("Custom lock strategy not implemented", component: :AccountLocker)
182
182
  false
183
183
  end
184
184
 
185
185
  # Unlock using custom strategy
186
186
  def unlock_with_custom_strategy
187
- Rails.logger.warn "[Beskar::AccountLocker] Custom unlock strategy not implemented"
187
+ Beskar::Logger.warn("Custom unlock strategy not implemented", component: :AccountLocker)
188
188
  false
189
189
  end
190
190
 
@@ -220,7 +220,7 @@ module Beskar
220
220
  }
221
221
  )
222
222
  rescue => e
223
- Rails.logger.warn "[Beskar::AccountLocker] Failed to log lock event: #{e.message}"
223
+ Beskar::Logger.warn("Failed to log lock event: #{e.message}", component: :AccountLocker)
224
224
  end
225
225
  end
226
226
 
@@ -243,7 +243,7 @@ module Beskar
243
243
  }
244
244
  )
245
245
  rescue => e
246
- Rails.logger.warn "[Beskar::AccountLocker] Failed to log unlock event: #{e.message}"
246
+ Beskar::Logger.warn("Failed to log unlock event: #{e.message}", component: :AccountLocker)
247
247
  end
248
248
  end
249
249
 
@@ -251,7 +251,7 @@ module Beskar
251
251
  def notify_user
252
252
  # This would integrate with ActionMailer or notification system
253
253
  # For now, just log it
254
- Rails.logger.info "[Beskar::AccountLocker] User #{user.id} should be notified of account lock"
254
+ Beskar::Logger.info("User #{user.id} should be notified of account lock", component: :AccountLocker)
255
255
 
256
256
  # Future implementation:
257
257
  # if defined?(Beskar::AccountLockMailer)
@@ -122,14 +122,14 @@ module Beskar
122
122
  db_path = Beskar.configuration.maxmind_city_db_path
123
123
  if File.exist?(db_path)
124
124
  @city_reader = MaxMindDB.new(db_path)
125
- Rails.logger.info "[Beskar::GeolocationService] MaxMind City database loaded from #{db_path}"
125
+ Beskar::Logger.info("MaxMind City database loaded from #{db_path}", component: :GeolocationService)
126
126
  else
127
- Rails.logger.warn "[Beskar::GeolocationService] MaxMind City database not found at #{db_path}"
127
+ Beskar::Logger.warn("MaxMind City database not found at #{db_path}", component: :GeolocationService)
128
128
  end
129
129
  @city_reader
130
130
  end
131
131
  rescue => e
132
- Rails.logger.error "[Beskar::GeolocationService] Failed to load MaxMind City database: #{e.message}"
132
+ Beskar::Logger.error("Failed to load MaxMind City database: #{e.message}", component: :GeolocationService)
133
133
  nil
134
134
  end
135
135
 
@@ -178,7 +178,7 @@ module Beskar
178
178
 
179
179
  result
180
180
  rescue => e
181
- Rails.logger.warn "[Beskar::GeolocationService] Failed to locate IP #{ip_address}: #{e.message}"
181
+ Beskar::Logger.warn("Failed to locate IP #{ip_address}: #{e.message}", component: :GeolocationService)
182
182
  unknown_location(ip_address)
183
183
  end
184
184
 
@@ -339,7 +339,7 @@ module Beskar
339
339
  result.merge!(unknown_location(ip_address).except(:ip, :provider, :private_ip))
340
340
  end
341
341
  rescue => e
342
- Rails.logger.warn "[Beskar::GeolocationService] MaxMind City lookup failed for #{ip_address}: #{e.message}"
342
+ Beskar::Logger.warn("MaxMind City lookup failed for #{ip_address}: #{e.message}", component: :GeolocationService)
343
343
  result.merge!(unknown_location(ip_address).except(:ip, :provider, :private_ip))
344
344
  end
345
345
  else
@@ -349,7 +349,7 @@ module Beskar
349
349
 
350
350
  result
351
351
  rescue => e
352
- Rails.logger.error "[Beskar::GeolocationService] MaxMind lookup failed for #{ip_address}: #{e.message}"
352
+ Beskar::Logger.error("MaxMind lookup failed for #{ip_address}: #{e.message}", component: :GeolocationService)
353
353
  unknown_location(ip_address)
354
354
  end
355
355
 
@@ -373,7 +373,7 @@ module Beskar
373
373
  cache_key = "#{@cache_key_prefix}:#{ip_address}"
374
374
  Rails.cache.read(cache_key)
375
375
  rescue => e
376
- Rails.logger.debug "[Beskar::GeolocationService] Cache read failed: #{e.message}"
376
+ Beskar::Logger.debug("Cache read failed: #{e.message}", component: :GeolocationService)
377
377
  nil
378
378
  end
379
379
 
@@ -385,7 +385,7 @@ module Beskar
385
385
  cache_key = "#{@cache_key_prefix}:#{ip_address}"
386
386
  Rails.cache.write(cache_key, result, expires_in: @cache_ttl)
387
387
  rescue => e
388
- Rails.logger.debug "[Beskar::GeolocationService] Cache write failed: #{e.message}"
388
+ Beskar::Logger.debug("Cache write failed: #{e.message}", component: :GeolocationService)
389
389
  end
390
390
  end
391
391
  end
@@ -16,7 +16,7 @@ module Beskar
16
16
  match_entry?(ip, entry)
17
17
  end
18
18
  rescue IPAddr::InvalidAddressError, ArgumentError => e
19
- Rails.logger.warn "[Beskar::IpWhitelist] Invalid IP address: #{ip_address} - #{e.message}"
19
+ Beskar::Logger.warn("Invalid IP address: #{ip_address} - #{e.message}", component: :IpWhitelist)
20
20
  false
21
21
  end
22
22
 
@@ -98,7 +98,7 @@ module Beskar
98
98
  begin
99
99
  entries[entry] = parse_entry(entry)
100
100
  rescue IPAddr::InvalidAddressError, ArgumentError => e
101
- Rails.logger.warn "[Beskar::IpWhitelist] Skipping invalid entry: #{entry} - #{e.message}"
101
+ Beskar::Logger.warn("Skipping invalid entry: #{entry} - #{e.message}", component: :IpWhitelist)
102
102
  end
103
103
  end
104
104
  entries