channel_email_account_uniqueness_validator.rb 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class Validations::ChannelEmailAccountUniquenessValidator < ActiveModel::Validator
  3. MATCHED_KEYS = %i[adapter].freeze
  4. MATCHED_OPTIONS_KEYS = %i[host port user folder].freeze
  5. MATCHED_AREAS = %w[Email::Account Google::Account Microsoft365::Account].freeze
  6. def validate(record)
  7. return if MATCHED_AREAS.exclude?(record.area)
  8. return if !matching_changes?(record)
  9. record_data = extract_matched_data(record.options)
  10. return if scope(record).none? { matches?(record_data, _1) }
  11. record.errors.add :base, __('The provided email account is already in use.')
  12. end
  13. private
  14. # https://github.com/zammad/zammad/issues/5111
  15. # Some systems may have duplicate channels created before this validation was added
  16. # Checking uniqueness on any update blocks XOAuth2 token update on such channels
  17. def matching_changes?(record)
  18. extract_matched_data(record.options) != extract_matched_data(record.options_was)
  19. end
  20. def scope(record)
  21. record
  22. .class
  23. .where(area: record.area)
  24. .then { record.persisted? ? _1.where.not(id: record.id) : _1 }
  25. end
  26. def matches?(record_data, other_record)
  27. other_record_data = extract_matched_data(other_record.options)
  28. return false if other_record_data.blank?
  29. record_data == other_record_data
  30. end
  31. def extract_matched_data(options)
  32. server_data = options&.dig(:inbound)
  33. return if !server_data
  34. values = server_data.slice(*MATCHED_KEYS)
  35. options_values = server_data[:options]&.slice(*MATCHED_OPTIONS_KEYS) || {}
  36. values
  37. .merge(options_values)
  38. .transform_values(&:to_s)
  39. end
  40. end