require 'kill_switch/version' require 'singleton' class ClientBlacklist include Singleton def self.configure(&block) raise ArgumentError, "Block not provided" unless block_given? yield instance end def self.rules instance.rules end def self.clear_rules instance.instance_variable_set(:@rules, {}) end def rules @rules ||= {} end def self.user_agent_valid?(user_agent_string) UserAgent.new(user_agent_string).valid? end def self.agent_blacklisted?(user_agent_string) agent = UserAgent.new(user_agent_string) raise RuntimeError, "User Agent Invalid" unless agent.valid? base_rules = rules.fetch("blacklist", {}).fetch(agent.app_version, {}).fetch(agent.device_platform, nil) # no rules found for version and device return false if base_rules.nil? if device_os_version_rules = base_rules[agent.device_os_version] if device_os_version_rules.empty? # No further criteria to match on. Found final match on device_os_version return true end if device_os_version_rules[agent.device_model] # Found a match on the nested device model return true end end if base_rules[agent.device_model] # 3 rules, excluding devise_os_version return true end if base_rules.any? # this means we have nested rules, not just the version and platform return false # we have nested rules, nothing matched - good to go end return true # If we made it this far, we matched on the base_rules only end def blacklist(app_version, device_platform, device_os_version = nil, device_model = nil) rules["blacklist"] ||= {} rules["blacklist"][app_version] ||= {} rules["blacklist"][app_version][device_platform] ||= {} if device_os_version rules["blacklist"][app_version][device_platform][device_os_version] ||= {} end if device_model if device_os_version # all 4 rules included - nest under device_os_version rules["blacklist"][app_version][device_platform][device_os_version][device_model] ||= {} else # device_os_version excluded - rule is to exclude a particular device_platform rules["blacklist"][app_version][device_platform][device_model] ||= {} end end end # Rails Before Filter Class Implementation class BlacklistFilter def self.before(controller) user_agent = controller.request.user_agent unless ClientBlacklist.user_agent_valid?(user_agent) controller.render( json: { data: { message: "User agent string is missing or invalid. Received [#{user_agent}]" }, metadata: nil, }, status: 400, ) return false end if ClientBlacklist.agent_blacklisted?(user_agent) controller.render( json: { data: { message: "This version [#{user_agent}] is no longer supported. Please visit the App Store to upgrade to the most recent version." }, metadata: nil, }, status: 403, ) return false end return true # All checks passed end end end