PageRenderTime 26ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/app/models/setting.rb

https://github.com/lzap/foreman
Ruby | 370 lines | 291 code | 64 blank | 15 comment | 59 complexity | 5f412cf1416daeed66d7b7853e565044 MD5 | raw file
  1. require 'resolv'
  2. class Setting < ApplicationRecord
  3. audited :except => [:name, :description, :category, :settings_type, :full_name, :encrypted], :on => [:update]
  4. extend FriendlyId
  5. friendly_id :name
  6. include ActiveModel::Validations
  7. include EncryptValue
  8. include PermissionName
  9. self.inheritance_column = 'category'
  10. graphql_type '::Types::Setting'
  11. TYPES = %w{integer boolean hash array string}
  12. FROZEN_ATTRS = %w{name category}
  13. NONZERO_ATTRS = %w{puppet_interval idle_timeout entries_per_page outofsync_interval}
  14. BLANK_ATTRS = %w{ host_owner trusted_hosts login_delegation_logout_url root_pass default_location default_organization websockets_ssl_key websockets_ssl_cert oauth_consumer_key oauth_consumer_secret login_text oidc_audience oidc_issuer oidc_algorithm
  15. smtp_address smtp_domain smtp_user_name smtp_password smtp_openssl_verify_mode smtp_authentication sendmail_arguments sendmail_location http_proxy http_proxy_except_list default_locale default_timezone ssl_certificate ssl_ca_file ssl_priv_key default_pxe_item_global default_pxe_item_local oidc_jwks_url instance_title }
  16. ARRAY_HOSTNAMES = %w{trusted_hosts}
  17. URI_ATTRS = %w{foreman_url unattended_url}
  18. URI_BLANK_ATTRS = %w{login_delegation_logout_url}
  19. IP_ATTRS = %w{libvirt_default_console_address}
  20. REGEXP_ATTRS = %w{remote_addr}
  21. EMAIL_ATTRS = %w{administrator email_reply_address}
  22. NOT_STRIPPED = %w{}
  23. class ValueValidator < ActiveModel::Validator
  24. def validate(record)
  25. record.send("validate_#{record.name}", record)
  26. end
  27. end
  28. validates_lengths_from_database
  29. validates :name, :presence => true, :uniqueness => true
  30. validates :description, :presence => true
  31. validates :default, :presence => true, :unless => proc { |s| s.settings_type == "boolean" || BLANK_ATTRS.include?(s.name) }
  32. validates :default, :inclusion => {:in => [true, false]}, :if => proc { |s| s.settings_type == "boolean" }
  33. validates :value, :numericality => true, :length => {:maximum => 8}, :if => proc { |s| s.settings_type == "integer" }
  34. validates :value, :numericality => {:greater_than => 0}, :if => proc { |s| NONZERO_ATTRS.include?(s.name) }
  35. validates :value, :inclusion => {:in => [true, false]}, :if => proc { |s| s.settings_type == "boolean" }
  36. validates :value, :presence => true, :if => proc { |s| s.settings_type == "array" && !BLANK_ATTRS.include?(s.name) }
  37. validates :settings_type, :inclusion => {:in => TYPES}, :allow_nil => true, :allow_blank => true
  38. validates :value, :url_schema => ['http', 'https'], :if => proc { |s| URI_ATTRS.include?(s.name) }
  39. validates :value, :url_schema => ['http', 'https'], :if => proc { |s| URI_BLANK_ATTRS.include?(s.name) && s.value.present? }
  40. validate :validate_host_owner, :if => proc { |s| s.name == "host_owner" }
  41. validates :value, :format => { :with => Resolv::AddressRegex }, :if => proc { |s| IP_ATTRS.include? s.name }
  42. validates :value, :regexp => true, :if => proc { |s| REGEXP_ATTRS.include? s.name }
  43. validates :value, :array_type => true, :if => proc { |s| s.settings_type == "array" }
  44. validates_with ValueValidator, :if => proc { |s| s.respond_to?("validate_#{s.name}") }
  45. validates :value, :array_hostnames_ips => true, :if => proc { |s| ARRAY_HOSTNAMES.include? s.name }
  46. validates :value, :email => true, :if => proc { |s| EMAIL_ATTRS.include? s.name }
  47. before_validation :set_setting_type_from_value
  48. before_save :clear_value_when_default
  49. before_save :clear_cache
  50. validate :validate_frozen_attributes
  51. after_find :readonly_when_overridden
  52. default_scope -> { order(:name) }
  53. # Filer out settings from disabled plugins
  54. scope :disabled_plugins, -> { where(:category => descendants.map(&:to_s)) unless Rails.env.development? }
  55. scope :order_by, ->(attr) { except(:order).order(attr) }
  56. scoped_search :on => :name, :complete_value => :true
  57. scoped_search :on => :description, :complete_value => :true
  58. def self.config_file
  59. 'settings.yaml'
  60. end
  61. def self.live_descendants
  62. disabled_plugins.order_by(:full_name)
  63. end
  64. def self.stick_general_first
  65. sticky_setting = 'Setting::General'
  66. (where(:category => sticky_setting) + where.not(:category => sticky_setting)).group_by(&:category)
  67. end
  68. # can't use our own settings
  69. def self.per_page
  70. 20
  71. end
  72. def self.humanized_category
  73. nil
  74. end
  75. def self.cache_key(name)
  76. "settings/#{name}"
  77. end
  78. def self.[](name)
  79. name = name.to_s
  80. cache.fetch(cache_key(name)) do
  81. find_by(:name => name)&.value
  82. end
  83. end
  84. def self.[]=(name, value)
  85. name = name.to_s
  86. record = where(:name => name).first!
  87. record.value = value
  88. record.save!
  89. end
  90. def self.setting_type_from_value(value_for_type)
  91. t = value_for_type.class.to_s.downcase
  92. return t if TYPES.include?(t)
  93. return "integer" if value_for_type.is_a?(Integer)
  94. return "boolean" if value_for_type.is_a?(TrueClass) || value_for_type.is_a?(FalseClass)
  95. end
  96. def value=(v)
  97. v = v.to_yaml unless v.nil?
  98. # the has_attribute is for enabling DB migrations on older versions
  99. if has_attribute?(:encrypted) && encrypted
  100. # Don't re-write the attribute if the current encrypted value is identical to the new one
  101. current_value = self[:value]
  102. unless is_decryptable?(current_value) && decrypt_field(current_value) == v
  103. self[:value] = encrypt_field(v)
  104. end
  105. else
  106. self[:value] = v
  107. end
  108. end
  109. def value
  110. v = self[:value]
  111. v = decrypt_field(v)
  112. v.nil? ? default : YAML.load(v)
  113. end
  114. alias_method :value_before_type_cast, :value
  115. def default
  116. d = self[:default]
  117. d.nil? ? nil : YAML.load(d)
  118. end
  119. def default=(v)
  120. self[:default] = v.to_yaml
  121. end
  122. alias_method :default_before_type_cast, :default
  123. def parse_string_value(val)
  124. case settings_type
  125. when "boolean"
  126. boolean = Foreman::Cast.to_bool(val)
  127. if boolean.nil?
  128. invalid_value_error _("must be boolean")
  129. return false
  130. end
  131. self.value = boolean
  132. when "integer"
  133. if val.to_s =~ /\A\d+\Z/
  134. self.value = val.to_i
  135. else
  136. invalid_value_error _("must be integer")
  137. return false
  138. end
  139. when "array"
  140. if val =~ /\A\[.*\]\Z/
  141. begin
  142. self.value = YAML.load(val.gsub(/(\,)(\S)/, "\\1 \\2"))
  143. rescue => e
  144. invalid_value_error e.to_s
  145. return false
  146. end
  147. else
  148. invalid_value_error _("must be an array")
  149. return false
  150. end
  151. when "string", nil
  152. # string is taken as default setting type for parsing
  153. self.value = NOT_STRIPPED.include?(name) ? val : val.to_s.strip
  154. when "hash"
  155. raise Foreman::Exception, "parsing hash from string is not supported"
  156. else
  157. raise Foreman::Exception.new(N_("parsing settings type '%s' from string is not defined"), settings_type)
  158. end
  159. true
  160. end
  161. # in order to avoid code duplication, this method was introduced
  162. def self.create_find_by_name(opts)
  163. # self.name can be set by default scope, e.g. from first_or_create use
  164. opts ||= { name: new.name }
  165. opts.symbolize_keys!
  166. s = Setting.find_by_name(opts[:name].to_s)
  167. return create_existing(s, opts) if s
  168. column_check(opts)
  169. if block_given?
  170. yield opts.merge!(value: readonly_value(opts[:name].to_sym) || opts[:value])
  171. end
  172. end
  173. def self.create(opts)
  174. create_find_by_name(opts) { super }
  175. end
  176. def self.create!(opts)
  177. create_find_by_name(opts) { super }
  178. end
  179. def self.regexp_expand_wildcard_string(string, options = {})
  180. prefix = options[:prefix] || '\A'
  181. suffix = options[:suffix] || '\Z'
  182. prefix + Regexp.escape(string).gsub('\*', '.*') + suffix
  183. end
  184. def self.convert_array_to_regexp(array, regexp_options = {})
  185. Regexp.new(array.map { |string| regexp_expand_wildcard_string(string, regexp_options) }.join('|'))
  186. end
  187. def has_readonly_value?
  188. SETTINGS.key?(name.to_sym)
  189. end
  190. def self.readonly_value(name)
  191. SETTINGS[name]
  192. end
  193. def read_attribute_before_type_cast(attr_name)
  194. return value if attr_name == :value
  195. super(attr_name)
  196. end
  197. def self.create_existing(s, opts)
  198. bypass_readonly(s) do
  199. attrs = column_check([:default, :description, :full_name, :encrypted])
  200. to_update = Hash[opts.select { |k, v| attrs.include? k }]
  201. to_update[:value] = readonly_value(s.name.to_sym) if s.has_readonly_value?
  202. # default is converted to yaml so we need to convert the yaml here too,
  203. # in order to check, if the update of default is needed
  204. # if it is the same, we don't try to update default, it would trigger
  205. # update query for every setting
  206. to_update.delete(:default) if to_update[:default].to_yaml.strip == s[:default]
  207. s.attributes = to_update
  208. s.save if s.changed? # to bypass name uniqueness validator to query db
  209. s.update_column :category, opts[:category] if s.category != opts[:category]
  210. s.update_column :full_name, opts[:full_name] if column_check([:full_name]).present? && s.full_name != opts[:full_name]
  211. raw_value = s.read_attribute(:value)
  212. if s.is_encryptable?(raw_value) && attrs.include?(:encrypted) && opts[:encrypted]
  213. s.update_column :value, s.encrypt_field(raw_value)
  214. end
  215. if s.is_decryptable?(raw_value) && attrs.include?(:encrypted) && !opts[:encrypted]
  216. s.update_column :value, s.decrypt_field(raw_value)
  217. end
  218. end
  219. s
  220. end
  221. def self.bypass_readonly(s, &block)
  222. s.instance_variable_set("@readonly", false) if (old_readonly = s.readonly?)
  223. yield(s)
  224. ensure
  225. s.readonly! if old_readonly
  226. end
  227. def self.cache
  228. Rails.cache
  229. end
  230. # Methods for loading default settings
  231. def self.default_settings
  232. []
  233. end
  234. def self.load_defaults
  235. return false unless table_exists?
  236. dbcache = Hash[Setting.where(:category => name).map { |s| [s.name, s] }]
  237. transaction do
  238. default_settings.compact.each do |s|
  239. val = s.update(:category => name).symbolize_keys
  240. dbcache.key?(val[:name]) ? create_existing(dbcache[val[:name]], s) : create!(s)
  241. end
  242. end
  243. true
  244. end
  245. def self.select_collection_registry
  246. @@select_collection ||= SettingSelectCollection.new
  247. end
  248. def self.set(name, description, default, full_name = nil, value = nil, options = {})
  249. if options.has_key? :collection
  250. select_collection_registry.add(name, options)
  251. end
  252. options[:encrypted] ||= false
  253. {:name => name, :value => value, :description => description, :default => default, :full_name => full_name, :encrypted => options[:encrypted]}
  254. end
  255. def select_collection
  256. self.class.select_collection_registry.collection_for self
  257. end
  258. def self.model_name
  259. ActiveModel::Name.new(Setting)
  260. end
  261. def self.column_check(opts)
  262. opts.keep_if { |k, v| Setting.column_names.include?(k.to_s) }
  263. end
  264. # End methods for loading default settings
  265. private
  266. def validate_host_owner
  267. owner_type_and_id = value
  268. return if owner_type_and_id.blank?
  269. owner = OwnerClassifier.new(owner_type_and_id).user_or_usergroup
  270. errors.add(:value, _("Host owner is invalid")) if owner.nil?
  271. end
  272. def invalid_value_error(error)
  273. errors.add(:value, _("is invalid: %s") % error)
  274. end
  275. def set_setting_type_from_value
  276. self.settings_type ||= self.class.setting_type_from_value(default)
  277. end
  278. def validate_frozen_attributes
  279. return true if new_record?
  280. changed_attributes.each do |c, old|
  281. # Allow settings_type to change at first (from nil) since it gets populated during validation
  282. if FROZEN_ATTRS.include?(c.to_s) || (c.to_s == :settings_type && !old.nil?)
  283. errors.add(c, _("is not allowed to change"))
  284. return false
  285. end
  286. end
  287. true
  288. end
  289. def clear_value_when_default
  290. if self[:value] == self[:default]
  291. self[:value] = nil
  292. end
  293. end
  294. def clear_cache
  295. # Rails cache returns false if the delete failed and nil if the key is missing
  296. if Setting.cache.delete(cache_key) == false
  297. Rails.logger.warn "Failed to remove #{name} from cache"
  298. end
  299. end
  300. def cache_key
  301. Setting.cache_key(name)
  302. end
  303. def readonly_when_overridden
  304. readonly! if !new_record? && has_readonly_value?
  305. end
  306. end