form_controller.rb 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://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_log
  8. def configuration
  9. return if !fingerprint_exists?
  10. api_path = Rails.configuration.api_path
  11. http_type = Setting.get('http_type')
  12. fqdn = Setting.get('fqdn')
  13. endpoint = "#{http_type}://#{fqdn}#{api_path}/form_submit"
  14. result = {
  15. enabled: Setting.get('form_ticket_create'),
  16. endpoint: endpoint,
  17. token: token_gen(params[:fingerprint])
  18. }
  19. if authorized?(policy_record, :test?)
  20. result[:enabled] = true
  21. end
  22. render json: result, status: :ok
  23. end
  24. def submit
  25. return if !fingerprint_exists?
  26. return if !token_valid?(params[:token], params[:fingerprint])
  27. # validate input
  28. errors = {}
  29. if params[:name].blank?
  30. errors['name'] = 'required'
  31. end
  32. if params[:title].blank?
  33. errors['title'] = 'required'
  34. end
  35. if params[:body].blank?
  36. errors['body'] = 'required'
  37. end
  38. if params[:email].blank?
  39. errors['email'] = 'required'
  40. else
  41. begin
  42. email_address_validation = EmailAddressValidation.new(params[:email])
  43. if !email_address_validation.valid?(check_mx: true)
  44. errors['email'] = 'invalid'
  45. end
  46. rescue => e
  47. message = e.to_s
  48. Rails.logger.info "Can't verify email #{params[:email]}: #{message}"
  49. # ignore 450, graylistings
  50. errors['email'] = message if message.exclude?('450')
  51. end
  52. end
  53. if errors.present?
  54. render json: {
  55. errors: errors
  56. }, status: :ok
  57. return
  58. end
  59. name = params[:name].strip
  60. email = params[:email].strip.downcase
  61. customer = User.find_by(email: email)
  62. if !customer
  63. role_ids = Role.signup_role_ids
  64. customer = User.create(
  65. firstname: name,
  66. lastname: '',
  67. email: email,
  68. active: true,
  69. role_ids: role_ids,
  70. updated_by_id: 1,
  71. created_by_id: 1,
  72. )
  73. end
  74. ticket = nil
  75. # set current user
  76. UserInfo.current_user_id = customer.id
  77. ApplicationHandleInfo.in_context('form') do # rubocop:disable Metrics/BlockLength
  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.create!(
  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. end
  116. UserInfo.current_user_id = 1
  117. result = {
  118. ticket: {
  119. id: ticket.id,
  120. number: ticket.number
  121. }
  122. }
  123. render json: result, status: :ok
  124. end
  125. private
  126. # we don't wann to tell what the cause for the authorization error is
  127. # so we capture the exception and raise an anonymized one
  128. def authorize!(...)
  129. super
  130. rescue Pundit::NotAuthorizedError
  131. raise Exceptions::Forbidden
  132. end
  133. def token_gen(fingerprint)
  134. crypt = ActiveSupport::MessageEncryptor.new(Setting.get('application_secret')[0, 32], serializer: JSON)
  135. fingerprint = "#{Base64.strict_encode64(Setting.get('fqdn'))}:#{Time.zone.now.to_i}:#{Base64.strict_encode64(fingerprint)}"
  136. Base64.strict_encode64(crypt.encrypt_and_sign(fingerprint))
  137. end
  138. def token_valid?(token, fingerprint)
  139. if token.blank?
  140. Rails.logger.info 'No token for form!'
  141. raise Exceptions::Forbidden
  142. end
  143. begin
  144. crypt = ActiveSupport::MessageEncryptor.new(Setting.get('application_secret')[0, 32], serializer: JSON)
  145. result = crypt.decrypt_and_verify(Base64.decode64(token))
  146. rescue
  147. Rails.logger.info 'Invalid token for form!'
  148. raise Exceptions::NotAuthorized
  149. end
  150. if result.blank?
  151. Rails.logger.info 'Invalid token for form!'
  152. raise Exceptions::NotAuthorized
  153. end
  154. parts = result.split(':')
  155. if parts.count != 3
  156. Rails.logger.info "Invalid token for form (need to have 3 parts, only #{parts.count} found)!"
  157. raise Exceptions::NotAuthorized
  158. end
  159. fqdn_local = Base64.decode64(parts[0])
  160. if fqdn_local != Setting.get('fqdn')
  161. Rails.logger.info "Invalid token for form (invalid fqdn found #{fqdn_local} != #{Setting.get('fqdn')})!"
  162. raise Exceptions::NotAuthorized
  163. end
  164. fingerprint_local = Base64.decode64(parts[2])
  165. if fingerprint_local != fingerprint
  166. Rails.logger.info "Invalid token for form (invalid fingerprint found #{fingerprint_local} != #{fingerprint})!"
  167. raise Exceptions::NotAuthorized
  168. end
  169. if parts[1].to_i < (Time.zone.now.to_i - (60 * 60 * 24))
  170. Rails.logger.info 'Invalid token for form (token expired})!'
  171. raise Exceptions::NotAuthorized
  172. end
  173. true
  174. end
  175. def fingerprint_exists?
  176. return true if params[:fingerprint].present? && params[:fingerprint].length > 30
  177. Rails.logger.info "The required parameter 'fingerprint' is missing or invalid."
  178. raise Exceptions::Forbidden
  179. end
  180. end