Separate Forward auth into it's own models + controller
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled

This commit is contained in:
Dan Milne
2025-10-24 10:56:27 +11:00
parent ee4af20000
commit fc9afcd1b7
20 changed files with 696 additions and 16 deletions

View File

@@ -8,7 +8,7 @@ class Application < ApplicationRecord
validates :slug, presence: true, uniqueness: { case_sensitive: false },
format: { with: /\A[a-z0-9\-]+\z/, message: "only lowercase letters, numbers, and hyphens" }
validates :app_type, presence: true,
inclusion: { in: %w[oidc trusted_header saml] }
inclusion: { in: %w[oidc saml] }
validates :client_id, uniqueness: { allow_nil: true }
normalizes :slug, with: ->(slug) { slug.strip.downcase }
@@ -18,7 +18,6 @@ class Application < ApplicationRecord
# Scopes
scope :active, -> { where(active: true) }
scope :oidc, -> { where(app_type: "oidc") }
scope :trusted_header, -> { where(app_type: "trusted_header") }
scope :saml, -> { where(app_type: "saml") }
# Type checks
@@ -26,10 +25,6 @@ class Application < ApplicationRecord
app_type == "oidc"
end
def trusted_header?
app_type == "trusted_header"
end
def saml?
app_type == "saml"
end

View File

@@ -0,0 +1,53 @@
class ForwardAuthRule < ApplicationRecord
has_many :forward_auth_rule_groups, dependent: :destroy
has_many :allowed_groups, through: :forward_auth_rule_groups, source: :group
validates :domain_pattern, presence: true, uniqueness: { case_sensitive: false }
validates :active, inclusion: { in: [true, false] }
normalizes :domain_pattern, with: ->(pattern) { pattern.strip.downcase }
# Scopes
scope :active, -> { where(active: true) }
scope :ordered, -> { order(domain_pattern: :asc) }
# Check if a domain matches this rule
def matches_domain?(domain)
return false if domain.blank?
pattern = domain_pattern.gsub('.', '\.')
pattern = pattern.gsub('*', '[^.]*')
regex = Regexp.new("^#{pattern}$", Regexp::IGNORECASE)
regex.match?(domain.downcase)
end
# Access control for forward auth
def user_allowed?(user)
return false unless active?
return false unless user.active?
# If no groups are specified, allow all active users (bypass)
return true if allowed_groups.empty?
# Otherwise, user must be in at least one of the allowed groups
(user.groups & allowed_groups).any?
end
# Policy determination based on user status and rule configuration
def policy_for_user(user)
return 'deny' unless active?
return 'deny' unless user.active?
# If no groups specified, bypass authentication
return 'bypass' if allowed_groups.empty?
# If user is in allowed groups, determine auth level
if user_allowed?(user)
# Require 2FA if user has TOTP configured, otherwise one factor
user.totp_enabled? ? 'two_factor' : 'one_factor'
else
'deny'
end
end
end

View File

@@ -0,0 +1,6 @@
class ForwardAuthRuleGroup < ApplicationRecord
belongs_to :forward_auth_rule
belongs_to :group
validates :forward_auth_rule_id, uniqueness: { scope: :group_id }
end