PageRenderTime 151ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/app/models/mixins/authentication_mixin.rb

https://gitlab.com/aljesusg/manageiqtest
Ruby | 318 lines | 233 code | 58 blank | 27 comment | 49 complexity | f65f51b29513cf1c2540b25c04ed7d52 MD5 | raw file
  1. module AuthenticationMixin
  2. extend ActiveSupport::Concern
  3. included do
  4. has_many :authentications, :as => :resource, :dependent => :destroy, :autosave => true
  5. virtual_column :authentication_status, :type => :string
  6. def self.authentication_check_schedule
  7. zone = MiqServer.my_server.zone
  8. assoc = name.tableize
  9. assocs = zone.respond_to?(assoc) ? zone.send(assoc) : []
  10. assocs.each(&:authentication_check_types_queue)
  11. end
  12. end
  13. def supported_auth_attributes
  14. %w(userid password)
  15. end
  16. def default_authentication_type
  17. :default
  18. end
  19. def authentication_userid_passwords
  20. authentications.select { |a| a.kind_of?(AuthUseridPassword) }
  21. end
  22. def authentication_tokens
  23. authentications.select { |a| a.kind_of?(AuthToken) }
  24. end
  25. def authentication_key_pairs
  26. authentications.select { |a| a.kind_of?(ManageIQ::Providers::Openstack::InfraManager::AuthKeyPair) }
  27. end
  28. def authentication_for_providers
  29. authentications.where.not(:authtype => nil)
  30. end
  31. def authentication_for_summary
  32. summary = []
  33. authentication_for_providers.each do |a|
  34. summary << {
  35. :authtype => a.authtype,
  36. :status => a.status,
  37. :status_details => a.status_details
  38. }
  39. end
  40. summary
  41. end
  42. def has_authentication_type?(type)
  43. authentication_types.include?(type)
  44. end
  45. def authentication_userid(type = nil)
  46. authentication_component(type, :userid)
  47. end
  48. def authentication_password(type = nil)
  49. authentication_component(type, :password)
  50. end
  51. def authentication_key(type = nil)
  52. authentication_component(type, :auth_key)
  53. end
  54. def authentication_token(type = nil)
  55. authentication_component(type, :auth_key)
  56. end
  57. def authentication_password_encrypted(type = nil)
  58. authentication_component(type, :password_encrypted)
  59. end
  60. def authentication_service_account(type = nil)
  61. authentication_component(type, :service_account)
  62. end
  63. def required_credential_fields(type)
  64. case type.to_s
  65. when "bearer"
  66. [:auth_key]
  67. when "hawkular"
  68. []
  69. else
  70. [:userid]
  71. end
  72. end
  73. def has_credentials?(type = nil)
  74. required_credential_fields(type).all? { |field| authentication_component(type, field) }
  75. end
  76. def missing_credentials?(type = nil)
  77. !has_credentials?(type)
  78. end
  79. def authentication_status
  80. ordered_auths = authentication_for_providers.sort_by(&:status_severity)
  81. ordered_auths.last.try(:status) || "None"
  82. end
  83. def authentication_status_ok?(type = nil)
  84. authentication_best_fit(type).try(:status) == "Valid"
  85. end
  86. def auth_user_pwd(type = nil)
  87. cred = authentication_best_fit(type)
  88. return nil if cred.nil? || cred.userid.blank?
  89. [cred.userid, cred.password]
  90. end
  91. def auth_user_token(type = nil)
  92. cred = authentication_best_fit(type)
  93. return nil if cred.nil? || cred.userid.blank?
  94. [cred.userid, cred.auth_key]
  95. end
  96. def auth_user_keypair(type = nil)
  97. cred = authentication_best_fit(type)
  98. return nil if cred.nil? || cred.userid.blank?
  99. [cred.userid, cred.auth_key]
  100. end
  101. def update_authentication(data, options = {})
  102. return if data.blank?
  103. options.reverse_merge!(:save => true)
  104. @orig_credentials ||= auth_user_pwd || "none"
  105. # Invoke before callback
  106. before_update_authentication if self.respond_to?(:before_update_authentication) && options[:save]
  107. data.each_pair do |type, value|
  108. cred = authentication_type(type)
  109. current = {:new => nil, :old => nil}
  110. unless value.key?(:userid) && value[:userid].blank?
  111. current[:new] = {:user => value[:userid], :password => value[:password], :auth_key => value[:auth_key]}
  112. end
  113. current[:old] = {:user => cred.userid, :password => cred.password, :auth_key => cred.auth_key} if cred
  114. # Raise an error if required fields are blank
  115. Array(options[:required]).each { |field| raise(ArgumentError, "#{field} is required") if value[field].blank? }
  116. # If old and new are the same then there is nothing to do
  117. next if current[:old] == current[:new]
  118. # Check if it is a delete
  119. if value.key?(:userid) && value[:userid].blank?
  120. current[:new] = nil
  121. next if options[:save] == false
  122. authentication_delete(type)
  123. next
  124. end
  125. # Update or create
  126. if cred.nil?
  127. if self.kind_of?(ManageIQ::Providers::Openstack::InfraManager) && value[:auth_key]
  128. # TODO(lsmola) investigate why build throws an exception, that it needs to be subclass of AuthUseridPassword
  129. cred = ManageIQ::Providers::Openstack::InfraManager::AuthKeyPair.new(:name => "#{self.class.name} #{name}", :authtype => type.to_s,
  130. :resource_id => id, :resource_type => "ExtManagementSystem")
  131. authentications << cred
  132. elsif value[:auth_key]
  133. cred = AuthToken.new(:name => "#{self.class.name} #{name}", :authtype => type.to_s,
  134. :resource_id => id, :resource_type => "ExtManagementSystem")
  135. authentications << cred
  136. else
  137. cred = authentications.build(:name => "#{self.class.name} #{name}", :authtype => type.to_s,
  138. :type => "AuthUseridPassword")
  139. end
  140. end
  141. cred.userid = value[:userid]
  142. cred.password = value[:password]
  143. cred.auth_key = value[:auth_key]
  144. cred.save if options[:save] && id
  145. end
  146. # Invoke callback
  147. after_update_authentication if self.respond_to?(:after_update_authentication) && options[:save]
  148. @orig_credentials = nil if options[:save]
  149. end
  150. def credentials_changed?
  151. @orig_credentials ||= auth_user_pwd || "none"
  152. new_credentials = auth_user_pwd || "none"
  153. @orig_credentials != new_credentials
  154. end
  155. def authentication_type(type)
  156. return nil if type.nil?
  157. available_authentications.detect do |a|
  158. a.authentication_type.to_s == type.to_s
  159. end
  160. end
  161. def authentication_check_types_queue(*args)
  162. options = args.extract_options!
  163. types = args.first
  164. role = authentication_check_role if self.respond_to?(:authentication_check_role)
  165. zone = my_zone if self.respond_to?(:my_zone)
  166. # FIXME: Via schedule, a message is created with args = [], so all authentications will be checked,
  167. # while an authentication change will create a message with args [:default] or whatever
  168. # authentication is changed, so you can end up with two messages for the same ci
  169. options = {
  170. :class_name => self.class.base_class.name,
  171. :instance_id => id,
  172. :method_name => 'authentication_check_types',
  173. :args => [types.to_miq_a, options]
  174. }
  175. options[:role] = role if role
  176. options[:zone] = zone if zone
  177. MiqQueue.put_unless_exists(options) do |msg|
  178. # TODO: Refactor the help in this and the ScheduleWorker#queue_work method into the merge method
  179. help = "Check for a running server"
  180. help << " in zone: [#{options[:zone]}]" if options[:zone]
  181. help << " with role: [#{options[:role]}]" if options[:role]
  182. _log.warn("Previous authentication_check_types for [#{name}] [#{id}] with opts: [#{options[:args].inspect}] is still running, skipping...#{help}") unless msg.nil?
  183. end
  184. end
  185. def authentication_check_types(*args)
  186. options = args.extract_options!
  187. types = args.first
  188. # Let the individual classes determine what authentication(s) need to be checked
  189. types = authentications_to_validate if self.respond_to?(:authentications_to_validate) && types.nil?
  190. types = [nil] if types.blank?
  191. types.to_miq_a.each { |t| authentication_check(t, options) }
  192. end
  193. # Returns [boolean check_result, string details]
  194. # check_result is true if and only if:
  195. # * the system is reachable
  196. # * AND we have the required authentication information
  197. # * AND we successfully connected using the authentication
  198. #
  199. # details is a UI friendly message
  200. #
  201. # By default, the authentication's status is updated by the
  202. # validation_successful or validation_failed callbacks.
  203. #
  204. # An optional :save => false can be passed to bypass these callbacks.
  205. #
  206. # TODO: :valid, :incomplete, and friends shouldn't be littered in here and authentication
  207. def authentication_check(*args)
  208. options = args.last.kind_of?(Hash) ? args.last : {}
  209. save = options.fetch(:save, true)
  210. type = args.first
  211. auth = authentication_best_fit(type)
  212. status, details = authentication_check_no_validation(type || auth.authtype, options)
  213. if save
  214. status == :valid ? auth.validation_successful : auth.validation_failed(status, details)
  215. end
  216. return status == :valid, details
  217. end
  218. private
  219. def authentication_check_no_validation(type, options)
  220. header = "type: [#{type.inspect}] for [#{id}] [#{name}]"
  221. status, details =
  222. if self.missing_credentials?(type)
  223. [:incomplete, "Missing credentials"]
  224. else
  225. begin
  226. verify_credentials(type, options) ? [:valid, ""] : [:invalid, "Unknown reason"]
  227. rescue MiqException::MiqUnreachableError => err
  228. [:unreachable, err]
  229. rescue MiqException::MiqInvalidCredentialsError, MiqException::MiqEVMLoginError => err
  230. [:invalid, err]
  231. rescue => err
  232. [:error, err]
  233. end
  234. end
  235. details &&= details.to_s.truncate(200)
  236. _log.warn("#{header} Validation failed: #{status}, #{details}") unless status == :valid
  237. return status, details
  238. end
  239. def authentication_best_fit(type = nil)
  240. # Look for the supplied type and if that is not found return the default credentials
  241. authentication_type(type) || authentication_type(default_authentication_type)
  242. end
  243. def authentication_component(type, method)
  244. cred = authentication_best_fit(type)
  245. return nil if cred.nil?
  246. value = cred.public_send(method)
  247. value.blank? ? nil : value
  248. end
  249. def available_authentications
  250. authentication_userid_passwords + authentication_key_pairs + authentication_tokens
  251. end
  252. def authentication_types
  253. available_authentications.collect(&:authentication_type).uniq
  254. end
  255. def authentication_delete(type)
  256. a = authentication_type(type)
  257. authentications.destroy(a) unless a.nil?
  258. a
  259. end
  260. end