beskar 0.0.2 → 0.2.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/CHANGELOG.md +274 -0
- data/README.md +412 -204
- data/app/channels/concerns/beskar/channels/session_security.rb +46 -0
- data/app/controllers/beskar/administrative_actions_controller.rb +16 -0
- data/app/controllers/beskar/application_controller.rb +214 -0
- data/app/controllers/beskar/banned_ips_controller.rb +255 -0
- data/app/controllers/beskar/dashboard_controller.rb +62 -0
- data/app/controllers/beskar/security_events_controller.rb +164 -0
- data/app/controllers/concerns/beskar/controllers/audit_export.rb +54 -0
- data/app/controllers/concerns/beskar/controllers/security_tracking.rb +76 -48
- data/app/controllers/concerns/beskar/controllers/session_security.rb +29 -0
- data/app/jobs/beskar/notification_job.rb +33 -0
- data/app/mailers/beskar/security_mailer.rb +59 -0
- data/app/models/beskar/administrative_action.rb +41 -0
- data/app/models/beskar/banned_ip.rb +105 -105
- data/app/models/beskar/security_event.rb +51 -4
- data/app/models/beskar/security_state.rb +58 -0
- data/app/services/beskar/banned_ip_manager.rb +88 -0
- data/app/views/beskar/administrative_actions/index.html.erb +33 -0
- data/app/views/beskar/administrative_actions/show.html.erb +21 -0
- data/app/views/beskar/banned_ips/edit.html.erb +195 -0
- data/app/views/beskar/banned_ips/index.html.erb +319 -0
- data/app/views/beskar/banned_ips/new.html.erb +190 -0
- data/app/views/beskar/banned_ips/review.html.erb +24 -0
- data/app/views/beskar/banned_ips/show.html.erb +304 -0
- data/app/views/beskar/dashboard/index.html.erb +280 -0
- data/app/views/beskar/security_events/index.html.erb +302 -0
- data/app/views/beskar/security_events/show.html.erb +293 -0
- data/app/views/beskar/shared/_export_form.html.erb +10 -0
- data/app/views/layouts/beskar/_behavior.html.erb +121 -0
- data/app/views/layouts/beskar/application.html.erb +581 -6
- data/config/routes.rb +30 -0
- data/db/migrate/20251016000001_create_beskar_security_events.rb +3 -3
- data/db/migrate/20260910000001_create_beskar_security_states.rb +14 -0
- data/db/migrate/20260911000001_create_beskar_administrative_actions.rb +22 -0
- data/db/migrate/20260911000002_expand_administrative_action_targets.rb +6 -0
- data/docs/README.md +73 -0
- data/docs/archive/project-documentation.md +659 -0
- data/docs/audits/project-review.md +437 -0
- data/docs/audits/repair-status.md +216 -0
- data/docs/guides/audit-and-waf.md +175 -0
- data/docs/guides/audit-lifecycle.md +172 -0
- data/docs/guides/authentication.md +213 -0
- data/docs/guides/configuration.md +182 -0
- data/docs/guides/dashboard-and-search.md +251 -0
- data/docs/guides/notifications-and-recovery.md +157 -0
- data/docs/guides/risk-scoring.md +116 -0
- data/docs/operations/monitor-only-mode.md +85 -0
- data/docs/operations/security-hardening.md +167 -0
- data/docs/operations/state-storage.md +144 -0
- data/docs/research/rust-performance-assessment.md +69 -0
- data/lib/beskar/configuration.rb +105 -20
- data/lib/beskar/configuration_validator.rb +188 -0
- data/lib/beskar/devise_authentication.rb +24 -0
- data/lib/beskar/engine.rb +21 -88
- data/lib/beskar/logger.rb +288 -0
- data/lib/beskar/middleware/request_analyzer.rb +133 -99
- data/lib/beskar/models/security_trackable_authenticable.rb +76 -97
- data/lib/beskar/models/security_trackable_devise.rb +34 -25
- data/lib/beskar/models/security_trackable_generic.rb +171 -214
- data/lib/beskar/risk_level.rb +22 -0
- data/lib/beskar/services/account_locker.rb +90 -81
- data/lib/beskar/services/administrative_audit.rb +36 -0
- data/lib/beskar/services/administrative_bans.rb +104 -0
- data/lib/beskar/services/audit_data.rb +72 -0
- data/lib/beskar/services/authentication.rb +31 -0
- data/lib/beskar/services/authentication_attempt.rb +141 -0
- data/lib/beskar/services/ban_expiry.rb +28 -0
- data/lib/beskar/services/device_detector.rb +32 -41
- data/lib/beskar/services/event_search.rb +58 -0
- data/lib/beskar/services/geolocation_service.rb +83 -114
- data/lib/beskar/services/ip_whitelist.rb +31 -40
- data/lib/beskar/services/location_assessment.rb +109 -0
- data/lib/beskar/services/native_account_lock.rb +82 -0
- data/lib/beskar/services/notifications.rb +46 -0
- data/lib/beskar/services/rate_limiter.rb +99 -125
- data/lib/beskar/services/request_context.rb +64 -0
- data/lib/beskar/services/risk_assessment.rb +58 -0
- data/lib/beskar/services/session_revocation.rb +62 -0
- data/lib/beskar/services/waf.rb +311 -198
- data/lib/beskar/services/waf_request.rb +60 -0
- data/lib/beskar/version.rb +1 -1
- data/lib/beskar/warden_authentication.rb +53 -0
- data/lib/beskar.rb +54 -4
- data/lib/generators/beskar/install/install_generator.rb +158 -0
- data/lib/generators/beskar/install/templates/initializer.rb.tt +261 -0
- data/lib/tasks/beskar_tasks.rake +25 -20
- metadata +93 -12
- data/lib/beskar/templates/beskar_initializer.rb +0 -107
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Beskar
|
|
2
|
+
module Services
|
|
3
|
+
# Account-lifetime state. Never expire or clear generations on unlock: doing
|
|
4
|
+
# so would resurrect credentials issued before a lock. No cache authority.
|
|
5
|
+
class SessionRevocation
|
|
6
|
+
def self.token(user)
|
|
7
|
+
SecurityState.uncached { SecurityState.read(NativeAccountLock.key(user)).fetch("generation", "0") }
|
|
8
|
+
rescue ActiveRecord::ActiveRecordError
|
|
9
|
+
raise AuthenticationAttempt::Unavailable, "Session state unavailable"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.revoke!(user)
|
|
13
|
+
SecurityState.mutate(NativeAccountLock.key(user), ttl: nil) do |state|
|
|
14
|
+
rotate!(state.fetch(NativeAccountLock.key(user)))
|
|
15
|
+
NativeAccountLock.revoke_sessions!(user) if user.respond_to?(:beskar_access_locked?)
|
|
16
|
+
end
|
|
17
|
+
rescue ActiveRecord::ActiveRecordError
|
|
18
|
+
raise AuthenticationAttempt::Unavailable, "Session revocation unavailable"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.rotate!(data)
|
|
22
|
+
data["generation"] = SecureRandom.uuid
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Rails-native database sessions need no new session column. Check the
|
|
26
|
+
# persisted row as well as the account lock, including stale Current objects.
|
|
27
|
+
def self.native_session_allowed?(record, request:)
|
|
28
|
+
return false unless record&.persisted?
|
|
29
|
+
record.reload
|
|
30
|
+
user = record.user
|
|
31
|
+
user&.beskar_access_allowed?(request)
|
|
32
|
+
rescue ActiveRecord::RecordNotFound
|
|
33
|
+
false
|
|
34
|
+
rescue ActiveRecord::ActiveRecordError
|
|
35
|
+
raise AuthenticationAttempt::Unavailable, "Session state unavailable"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Tokens must carry the generation captured at issuance inside their
|
|
39
|
+
# authenticated server-side record or signed claims, never a request param.
|
|
40
|
+
# Call on every protected request / WebSocket message, not just connect.
|
|
41
|
+
def self.allowed?(user, request:, token:)
|
|
42
|
+
return false unless user&.persisted? && token.is_a?(String)
|
|
43
|
+
# Cable connections can retain a model object after its database row is
|
|
44
|
+
# deleted. persisted? alone only describes that stale Ruby object.
|
|
45
|
+
user = user.class.uncached { user.class.find_by(user.class.primary_key => user.id) }
|
|
46
|
+
return false unless user
|
|
47
|
+
data = SecurityState.uncached { SecurityState.read(NativeAccountLock.key(user)) }
|
|
48
|
+
return false unless ActiveSupport::SecurityUtils.secure_compare(data.fetch("generation", "0"), token)
|
|
49
|
+
return true unless RequestContext.enforce?(RequestContext.ip(request))
|
|
50
|
+
if user.respond_to?(:beskar_access_locked?)
|
|
51
|
+
!NativeAccountLock.locked_data?(data)
|
|
52
|
+
elsif user.respond_to?(:access_locked?)
|
|
53
|
+
!user.access_locked?
|
|
54
|
+
else
|
|
55
|
+
true
|
|
56
|
+
end
|
|
57
|
+
rescue ActiveRecord::ActiveRecordError
|
|
58
|
+
raise AuthenticationAttempt::Unavailable, "Session state unavailable"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|