form_controller.rb 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. # Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
  2. class FormController < ApplicationController
  3. prepend_before_action -> { authorize! }, only: %i[configuration submit]
  4. skip_before_action :verify_csrf_token
  5. before_action :cors_preflight_check
  6. after_action :set_access_control_headers_execute
  7. skip_before_action :user_device_check
  8. def configuration
  9. return if !fingerprint_exists?
  10. return if limit_reached?
  11. api_path = Rails.configuration.api_path
  12. http_type = Setting.get('http_type')
  13. fqdn = Setting.get('fqdn')
  14. endpoint = "#{http_type}://#{fqdn}#{api_path}/form_submit"
  15. result = {
  16. enabled: Setting.get('form_ticket_create'),
  17. endpoint: endpoint,
  18. token: token_gen(params[:fingerprint])
  19. }
  20. if authorized?(policy_record, :test?)
  21. result[:enabled] = true
  22. end
  23. render json: result, status: :ok
  24. end
  25. def submit
  26. return if !fingerprint_exists?
  27. return if !token_valid?(params[:token], params[:fingerprint])
  28. return if limit_reached?
  29. # validate input
  30. errors = {}
  31. if params[:name].blank?
  32. errors['name'] = 'required'
  33. end
  34. if params[:title].blank?
  35. errors['title'] = 'required'
  36. end
  37. if params[:body].blank?
  38. errors['body'] = 'required'
  39. end
  40. if params[:email].blank?
  41. errors['email'] = 'required'
  42. else
  43. begin
  44. email_address_validation = EmailAddressValidation.new(params[:email])
  45. if !email_address_validation.valid_format? || !email_address_validation.valid_mx?
  46. errors['email'] = 'invalid'
  47. end
  48. rescue => e
  49. message = e.to_s
  50. Rails.logger.info "Can't verify email #{params[:email]}: #{message}"
  51. # ignore 450, graylistings
  52. errors['email'] = message if !message.include?('450')
  53. end
  54. end
  55. if errors.present?
  56. render json: {
  57. errors: errors
  58. }, status: :ok
  59. return
  60. end
  61. name = params[:name].strip
  62. email = params[:email].strip.downcase
  63. customer = User.find_by(email: email)
  64. if !customer
  65. role_ids = Role.signup_role_ids
  66. customer = User.create(
  67. firstname: name,
  68. lastname: '',
  69. email: email,
  70. active: true,
  71. role_ids: role_ids,
  72. updated_by_id: 1,
  73. created_by_id: 1,
  74. )
  75. end
  76. # set current user
  77. UserInfo.current_user_id = customer.id
  78. group = Group.find_by(id: Setting.get('form_ticket_create_group_id'))
  79. if !group
  80. group = Group.where(active: true).first
  81. if !group
  82. group = Group.first
  83. end
  84. end
  85. ticket = Ticket.create!(
  86. group_id: group.id,
  87. customer_id: customer.id,
  88. title: params[:title],
  89. preferences: {
  90. form: {
  91. remote_ip: request.remote_ip,
  92. fingerprint_md5: Digest::MD5.hexdigest(params[:fingerprint]),
  93. }
  94. }
  95. )
  96. article = Ticket::Article.create!(
  97. ticket_id: ticket.id,
  98. type_id: Ticket::Article::Type.find_by(name: 'web').id,
  99. sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id,
  100. body: params[:body],
  101. subject: params[:title],
  102. internal: false,
  103. )
  104. params[:file]&.each do |file|
  105. Store.add(
  106. object: 'Ticket::Article',
  107. o_id: article.id,
  108. data: file.read,
  109. filename: file.original_filename,
  110. preferences: {
  111. 'Mime-Type' => file.content_type,
  112. }
  113. )
  114. end
  115. UserInfo.current_user_id = 1
  116. result = {
  117. ticket: {
  118. id: ticket.id,
  119. number: ticket.number
  120. }
  121. }
  122. render json: result, status: :ok
  123. end
  124. private
  125. # we don't wann to tell what the cause for the authorization error is
  126. # so we capture the exception and raise an anonymized one
  127. def authorize!(*)
  128. super
  129. rescue Pundit::NotAuthorizedError
  130. raise Exceptions::NotAuthorized
  131. end
  132. def token_gen(fingerprint)
  133. crypt = ActiveSupport::MessageEncryptor.new(Setting.get('application_secret')[0, 32])
  134. fingerprint = "#{Base64.strict_encode64(Setting.get('fqdn'))}:#{Time.zone.now.to_i}:#{Base64.strict_encode64(fingerprint)}"
  135. Base64.strict_encode64(crypt.encrypt_and_sign(fingerprint))
  136. end
  137. def token_valid?(token, fingerprint)
  138. if token.blank?
  139. Rails.logger.info 'No token for form!'
  140. raise Exceptions::NotAuthorized
  141. end
  142. begin
  143. crypt = ActiveSupport::MessageEncryptor.new(Setting.get('application_secret')[0, 32])
  144. result = crypt.decrypt_and_verify(Base64.decode64(token))
  145. rescue
  146. Rails.logger.info 'Invalid token for form!'
  147. raise Exceptions::NotAuthorized
  148. end
  149. if result.blank?
  150. Rails.logger.info 'Invalid token for form!'
  151. raise Exceptions::NotAuthorized
  152. end
  153. parts = result.split(/:/)
  154. if parts.count != 3
  155. Rails.logger.info "Invalid token for form (need to have 3 parts, only #{parts.count} found)!"
  156. raise Exceptions::NotAuthorized
  157. end
  158. fqdn_local = Base64.decode64(parts[0])
  159. if fqdn_local != Setting.get('fqdn')
  160. Rails.logger.info "Invalid token for form (invalid fqdn found #{fqdn_local} != #{Setting.get('fqdn')})!"
  161. raise Exceptions::NotAuthorized
  162. end
  163. fingerprint_local = Base64.decode64(parts[2])
  164. if fingerprint_local != fingerprint
  165. Rails.logger.info "Invalid token for form (invalid fingerprint found #{fingerprint_local} != #{fingerprint})!"
  166. raise Exceptions::NotAuthorized
  167. end
  168. if parts[1].to_i < (Time.zone.now.to_i - 60 * 60 * 24)
  169. Rails.logger.info 'Invalid token for form (token expired})!'
  170. raise Exceptions::NotAuthorized
  171. end
  172. true
  173. end
  174. def limit_reached?
  175. return false if !SearchIndexBackend.enabled?
  176. # quote ipv6 ip'
  177. remote_ip = request.remote_ip.gsub(':', '\\:')
  178. # in elasticsearch7 "created_at:>now-1h" is not working. Needed to catch -2h
  179. form_limit_by_ip_per_hour = Setting.get('form_ticket_create_by_ip_per_hour') || 20
  180. result = SearchIndexBackend.search("preferences.form.remote_ip:'#{remote_ip}' AND created_at:>now-2h", 'Ticket', limit: form_limit_by_ip_per_hour)
  181. raise Exceptions::NotAuthorized if result.count >= form_limit_by_ip_per_hour.to_i
  182. form_limit_by_ip_per_day = Setting.get('form_ticket_create_by_ip_per_day') || 240
  183. result = SearchIndexBackend.search("preferences.form.remote_ip:'#{remote_ip}' AND created_at:>now-1d", 'Ticket', limit: form_limit_by_ip_per_day)
  184. raise Exceptions::NotAuthorized if result.count >= form_limit_by_ip_per_day.to_i
  185. form_limit_per_day = Setting.get('form_ticket_create_per_day') || 5000
  186. result = SearchIndexBackend.search('preferences.form.remote_ip:* AND created_at:>now-1d', 'Ticket', limit: form_limit_per_day)
  187. raise Exceptions::NotAuthorized if result.count >= form_limit_per_day.to_i
  188. false
  189. end
  190. def fingerprint_exists?
  191. return true if params[:fingerprint].present? && params[:fingerprint].length > 30
  192. Rails.logger.info 'No fingerprint given!'
  193. raise Exceptions::NotAuthorized
  194. end
  195. end