Improved bot detection - issue #1207. Added ticket limits per hour and day.
This commit is contained in:
parent
4aa1ec84be
commit
1d4ce42c67
11 changed files with 392 additions and 62 deletions
|
@ -281,6 +281,7 @@ test:integration:es_mysql:
|
||||||
- ruby -I test/ test/integration/elasticsearch_test.rb
|
- ruby -I test/ test/integration/elasticsearch_test.rb
|
||||||
- ruby -I test/ test/controllers/search_controller_test.rb
|
- ruby -I test/ test/controllers/search_controller_test.rb
|
||||||
- ruby -I test/ test/integration/report_test.rb
|
- ruby -I test/ test/integration/report_test.rb
|
||||||
|
- ruby -I test/ test/controllers/form_controller_test.rb
|
||||||
- rake db:drop
|
- rake db:drop
|
||||||
|
|
||||||
test:integration:es_postgresql:
|
test:integration:es_postgresql:
|
||||||
|
@ -297,6 +298,7 @@ test:integration:es_postgresql:
|
||||||
- ruby -I test/ test/integration/elasticsearch_test.rb
|
- ruby -I test/ test/integration/elasticsearch_test.rb
|
||||||
- ruby -I test/ test/controllers/search_controller_test.rb
|
- ruby -I test/ test/controllers/search_controller_test.rb
|
||||||
- ruby -I test/ test/integration/report_test.rb
|
- ruby -I test/ test/integration/report_test.rb
|
||||||
|
- ruby -I test/ test/controllers/form_controller_test.rb
|
||||||
- rake db:drop
|
- rake db:drop
|
||||||
|
|
||||||
test:integration:zendesk_mysql:
|
test:integration:zendesk_mysql:
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -45,7 +45,7 @@ gem 'twitter'
|
||||||
gem 'telegramAPI'
|
gem 'telegramAPI'
|
||||||
gem 'koala'
|
gem 'koala'
|
||||||
gem 'mail'
|
gem 'mail'
|
||||||
gem 'email_verifier'
|
gem 'valid_email2'
|
||||||
gem 'htmlentities'
|
gem 'htmlentities'
|
||||||
|
|
||||||
gem 'mime-types'
|
gem 'mime-types'
|
||||||
|
|
|
@ -93,7 +93,6 @@ GEM
|
||||||
delayed_job (>= 3.0, < 5)
|
delayed_job (>= 3.0, < 5)
|
||||||
diff-lcs (1.2.5)
|
diff-lcs (1.2.5)
|
||||||
diffy (3.1.0)
|
diffy (3.1.0)
|
||||||
dnsruby (1.59.3)
|
|
||||||
docile (1.1.5)
|
docile (1.1.5)
|
||||||
domain_name (0.5.20170404)
|
domain_name (0.5.20170404)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
|
@ -107,8 +106,6 @@ GEM
|
||||||
em-websocket (0.5.1)
|
em-websocket (0.5.1)
|
||||||
eventmachine (>= 0.12.9)
|
eventmachine (>= 0.12.9)
|
||||||
http_parser.rb (~> 0.6.0)
|
http_parser.rb (~> 0.6.0)
|
||||||
email_verifier (0.1.0)
|
|
||||||
dnsruby (>= 1.5)
|
|
||||||
equalizer (0.0.10)
|
equalizer (0.0.10)
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
eventmachine (1.2.3)
|
eventmachine (1.2.3)
|
||||||
|
@ -403,6 +400,9 @@ GEM
|
||||||
unicorn (5.2.0)
|
unicorn (5.2.0)
|
||||||
kgio (~> 2.6)
|
kgio (~> 2.6)
|
||||||
raindrops (~> 0.7)
|
raindrops (~> 0.7)
|
||||||
|
valid_email2 (1.2.17)
|
||||||
|
activemodel (>= 3.2)
|
||||||
|
mail (~> 2.5)
|
||||||
webmock (2.3.2)
|
webmock (2.3.2)
|
||||||
addressable (>= 2.3.6)
|
addressable (>= 2.3.6)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
|
@ -439,7 +439,6 @@ DEPENDENCIES
|
||||||
doorkeeper
|
doorkeeper
|
||||||
eco
|
eco
|
||||||
em-websocket
|
em-websocket
|
||||||
email_verifier
|
|
||||||
eventmachine
|
eventmachine
|
||||||
execjs
|
execjs
|
||||||
factory_girl_rails
|
factory_girl_rails
|
||||||
|
@ -491,6 +490,7 @@ DEPENDENCIES
|
||||||
twitter
|
twitter
|
||||||
uglifier
|
uglifier
|
||||||
unicorn
|
unicorn
|
||||||
|
valid_email2
|
||||||
webmock
|
webmock
|
||||||
writeexcel
|
writeexcel
|
||||||
zendesk_api
|
zendesk_api
|
||||||
|
|
|
@ -7,6 +7,8 @@ class FormController < ApplicationController
|
||||||
|
|
||||||
def config
|
def config
|
||||||
return if !enabled?
|
return if !enabled?
|
||||||
|
return if !fingerprint_exists?
|
||||||
|
return if limit_reached?
|
||||||
|
|
||||||
api_path = Rails.configuration.api_path
|
api_path = Rails.configuration.api_path
|
||||||
http_type = Setting.get('http_type')
|
http_type = Setting.get('http_type')
|
||||||
|
@ -17,6 +19,7 @@ class FormController < ApplicationController
|
||||||
config = {
|
config = {
|
||||||
enabled: Setting.get('form_ticket_create'),
|
enabled: Setting.get('form_ticket_create'),
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
|
token: token_gen(params[:fingerprint])
|
||||||
}
|
}
|
||||||
|
|
||||||
if params[:test] && current_user && current_user.permissions?('admin.channel_formular')
|
if params[:test] && current_user && current_user.permissions?('admin.channel_formular')
|
||||||
|
@ -28,35 +31,35 @@ class FormController < ApplicationController
|
||||||
|
|
||||||
def submit
|
def submit
|
||||||
return if !enabled?
|
return if !enabled?
|
||||||
|
return if !fingerprint_exists?
|
||||||
|
return if !token_valid?(params[:token], params[:fingerprint])
|
||||||
|
return if limit_reached?
|
||||||
|
|
||||||
# validate input
|
# validate input
|
||||||
errors = {}
|
errors = {}
|
||||||
if !params[:name] || params[:name].empty?
|
if params[:name].blank?
|
||||||
errors['name'] = 'required'
|
errors['name'] = 'required'
|
||||||
end
|
end
|
||||||
if !params[:email] || params[:email].empty?
|
if params[:email].blank?
|
||||||
errors['email'] = 'required'
|
errors['email'] = 'required'
|
||||||
end
|
elsif params[:email] !~ /@/
|
||||||
if params[:email] !~ /@/
|
errors['email'] = 'invalid'
|
||||||
|
elsif params[:email] =~ /(>|<|\||\!|"|§|'|\$|%|&|\(|\)|\?|\s|\.\.)/
|
||||||
errors['email'] = 'invalid'
|
errors['email'] = 'invalid'
|
||||||
end
|
end
|
||||||
if params[:email] =~ /(>|<|\||\!|"|§|'|\$|%|&|\(|\)|\?|\s)/
|
if params[:title].blank?
|
||||||
errors['email'] = 'invalid'
|
|
||||||
end
|
|
||||||
if !params[:title] || params[:title].empty?
|
|
||||||
errors['title'] = 'required'
|
errors['title'] = 'required'
|
||||||
end
|
end
|
||||||
if !params[:body] || params[:body].empty?
|
if params[:body].blank?
|
||||||
errors['body'] = 'required'
|
errors['body'] = 'required'
|
||||||
end
|
end
|
||||||
|
|
||||||
# realtime verify
|
# realtime verify
|
||||||
if !errors['email']
|
if errors['email'].blank?
|
||||||
begin
|
begin
|
||||||
checker = EmailVerifier::Checker.new(params[:email])
|
address = ValidEmail2::Address.new(params[:email])
|
||||||
checker.connect
|
if !address || !address.valid? || !address.valid_mx?
|
||||||
if !checker.verify
|
errors['email'] = 'invalid'
|
||||||
errors['email'] = "Unable to send to '#{params[:email]}'"
|
|
||||||
end
|
end
|
||||||
rescue => e
|
rescue => e
|
||||||
message = e.to_s
|
message = e.to_s
|
||||||
|
@ -69,7 +72,7 @@ class FormController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if errors && !errors.empty?
|
if errors.present?
|
||||||
render json: {
|
render json: {
|
||||||
errors: errors
|
errors: errors
|
||||||
}, status: :ok
|
}, status: :ok
|
||||||
|
@ -86,7 +89,6 @@ class FormController < ApplicationController
|
||||||
firstname: name,
|
firstname: name,
|
||||||
lastname: '',
|
lastname: '',
|
||||||
email: email,
|
email: email,
|
||||||
password: '',
|
|
||||||
active: true,
|
active: true,
|
||||||
role_ids: role_ids,
|
role_ids: role_ids,
|
||||||
updated_by_id: 1,
|
updated_by_id: 1,
|
||||||
|
@ -97,10 +99,20 @@ class FormController < ApplicationController
|
||||||
# set current user
|
# set current user
|
||||||
UserInfo.current_user_id = customer.id
|
UserInfo.current_user_id = customer.id
|
||||||
|
|
||||||
|
group = Group.where(active: true).first
|
||||||
|
if !group
|
||||||
|
group = Group.first
|
||||||
|
end
|
||||||
ticket = Ticket.create!(
|
ticket = Ticket.create!(
|
||||||
group_id: 1,
|
group_id: group.id,
|
||||||
customer_id: customer.id,
|
customer_id: customer.id,
|
||||||
title: params[:title],
|
title: params[:title],
|
||||||
|
preferences: {
|
||||||
|
form: {
|
||||||
|
remote_ip: request.remote_ip,
|
||||||
|
fingerprint_md5: Digest::MD5.hexdigest(params[:fingerprint]),
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
article = Ticket::Article.create!(
|
article = Ticket::Article.create!(
|
||||||
ticket_id: ticket.id,
|
ticket_id: ticket.id,
|
||||||
|
@ -138,6 +150,91 @@ class FormController < ApplicationController
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def token_gen(fingerprint)
|
||||||
|
crypt = ActiveSupport::MessageEncryptor.new(Setting.get('application_secret'))
|
||||||
|
fingerprint = "#{Base64.strict_encode64(Setting.get('fqdn'))}:#{Time.zone.now.to_i}:#{Base64.strict_encode64(fingerprint)}"
|
||||||
|
Base64.strict_encode64(crypt.encrypt_and_sign(fingerprint))
|
||||||
|
end
|
||||||
|
|
||||||
|
def token_valid?(token, fingerprint)
|
||||||
|
if token.blank?
|
||||||
|
Rails.logger.info 'No token for form!'
|
||||||
|
response_access_deny
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
begin
|
||||||
|
crypt = ActiveSupport::MessageEncryptor.new(Setting.get('application_secret'))
|
||||||
|
result = crypt.decrypt_and_verify(Base64.decode64(token))
|
||||||
|
rescue
|
||||||
|
Rails.logger.info 'Invalid token for form!'
|
||||||
|
response_access_deny
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if result.blank?
|
||||||
|
Rails.logger.info 'Invalid token for form!'
|
||||||
|
response_access_deny
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
parts = result.split(/:/)
|
||||||
|
if parts.count != 3
|
||||||
|
Rails.logger.info "Invalid token for form (need to have 3 parts, only #{parts.count} found)!"
|
||||||
|
response_access_deny
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
fqdn_local = Base64.decode64(parts[0])
|
||||||
|
if fqdn_local != Setting.get('fqdn')
|
||||||
|
Rails.logger.info "Invalid token for form (invalid fqdn found #{fqdn_local} != #{Setting.get('fqdn')})!"
|
||||||
|
response_access_deny
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
fingerprint_local = Base64.decode64(parts[2])
|
||||||
|
if fingerprint_local != fingerprint
|
||||||
|
Rails.logger.info "Invalid token for form (invalid fingerprint found #{fingerprint_local} != #{fingerprint})!"
|
||||||
|
response_access_deny
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if parts[1].to_i < (Time.zone.now.to_i - 60 * 60 * 24)
|
||||||
|
Rails.logger.info 'Invalid token for form (token expired})!'
|
||||||
|
response_access_deny
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def limit_reached?
|
||||||
|
return false if !SearchIndexBackend.enabled?
|
||||||
|
|
||||||
|
form_limit_by_ip_per_hour = Setting.get('form_ticket_create_by_ip_per_hour') || 20
|
||||||
|
result = SearchIndexBackend.search("preferences.form.remote_ip:'#{request.remote_ip}' AND created_at:>now-1h", form_limit_by_ip_per_hour, 'Ticket')
|
||||||
|
if result.count >= form_limit_by_ip_per_hour.to_i
|
||||||
|
response_access_deny
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
form_limit_by_ip_per_day = Setting.get('form_ticket_create_by_ip_per_day') || 240
|
||||||
|
result = SearchIndexBackend.search("preferences.form.remote_ip:'#{request.remote_ip}' AND created_at:>now-1d", form_limit_by_ip_per_day, 'Ticket')
|
||||||
|
if result.count >= form_limit_by_ip_per_day.to_i
|
||||||
|
response_access_deny
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
form_limit_per_day = Setting.get('form_ticket_create_per_day') || 5000
|
||||||
|
result = SearchIndexBackend.search('preferences.form.remote_ip:* AND created_at:>now-1d', form_limit_per_day, 'Ticket')
|
||||||
|
if result.count >= form_limit_per_day.to_i
|
||||||
|
response_access_deny
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def fingerprint_exists?
|
||||||
|
return true if params[:fingerprint].present? && params[:fingerprint].length > 30
|
||||||
|
Rails.logger.info 'No fingerprint given!'
|
||||||
|
response_access_deny
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
def enabled?
|
def enabled?
|
||||||
return true if params[:test] && current_user && current_user.permissions?('admin.channel_formular')
|
return true if params[:test] && current_user && current_user.permissions?('admin.channel_formular')
|
||||||
return true if Setting.get('form_ticket_create')
|
return true if Setting.get('form_ticket_create')
|
||||||
|
|
|
@ -3,6 +3,6 @@ Zammad::Application.routes.draw do
|
||||||
|
|
||||||
# forms
|
# forms
|
||||||
match api_path + '/form_submit', to: 'form#submit', via: :post
|
match api_path + '/form_submit', to: 'form#submit', via: :post
|
||||||
match api_path + '/form_config', to: 'form#config', via: :get
|
match api_path + '/form_config', to: 'form#config', via: :post
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -56,7 +56,7 @@ or
|
||||||
def self.email(params)
|
def self.email(params)
|
||||||
|
|
||||||
# send verify email
|
# send verify email
|
||||||
subject = if !params[:subject] || params[:subject].empty?
|
subject = if params[:subject].blank?
|
||||||
'#' + rand(99_999_999_999).to_s
|
'#' + rand(99_999_999_999).to_s
|
||||||
else
|
else
|
||||||
params[:subject]
|
params[:subject]
|
||||||
|
|
|
@ -427,8 +427,7 @@ return true if backend is configured
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def self.enabled?
|
def self.enabled?
|
||||||
return if !Setting.get('es_url')
|
return false if Setting.get('es_url').blank?
|
||||||
return if Setting.get('es_url').empty?
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<div id="feedback-form-inline"></div>
|
<div id="feedback-form-inline"></div>
|
||||||
|
|
||||||
|
|
||||||
<div class="js-logDisplay"></div>
|
<div class="js-logDisplay" style="overflow-x: hidden;"></div>
|
||||||
|
|
||||||
<p>Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.</p>
|
<p>Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.</p>
|
||||||
|
|
||||||
|
|
|
@ -204,8 +204,14 @@ $(function() {
|
||||||
if (this.options.test) {
|
if (this.options.test) {
|
||||||
params.test = true
|
params.test = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params.fingerprint = this.fingerprint()
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
method: 'post',
|
||||||
url: _this.endpoint_config,
|
url: _this.endpoint_config,
|
||||||
|
cache: false,
|
||||||
|
processData: true,
|
||||||
data: params
|
data: params
|
||||||
}).done(function(data) {
|
}).done(function(data) {
|
||||||
_this.log('debug', 'config:', data)
|
_this.log('debug', 'config:', data)
|
||||||
|
@ -256,7 +262,7 @@ $(function() {
|
||||||
_this.log('debug', 'currentTime', currentTime)
|
_this.log('debug', 'currentTime', currentTime)
|
||||||
_this.log('debug', 'modalOpenTime', _this.modalOpenTime.getTime())
|
_this.log('debug', 'modalOpenTime', _this.modalOpenTime.getTime())
|
||||||
_this.log('debug', 'diffTime', diff)
|
_this.log('debug', 'diffTime', diff)
|
||||||
if (diff < 1000*8) {
|
if (diff < 1000*10) {
|
||||||
alert('Sorry, you look like an robot!')
|
alert('Sorry, you look like an robot!')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -317,7 +323,10 @@ $(function() {
|
||||||
formData.append('test', true)
|
formData.append('test', true)
|
||||||
}
|
}
|
||||||
formData.append('token', this._config.token)
|
formData.append('token', this._config.token)
|
||||||
|
|
||||||
|
formData.append('fingerprint', this.fingerprint())
|
||||||
_this.log('debug', 'formData', formData)
|
_this.log('debug', 'formData', formData)
|
||||||
|
|
||||||
return formData
|
return formData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,6 +472,22 @@ $(function() {
|
||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Plugin.prototype.fingerprint = function () {
|
||||||
|
var canvas = document.createElement('canvas')
|
||||||
|
var ctx = canvas.getContext('2d')
|
||||||
|
var txt = 'https://zammad.com'
|
||||||
|
ctx.textBaseline = 'top'
|
||||||
|
ctx.font = '12px \'Arial\''
|
||||||
|
ctx.textBaseline = 'alphabetic'
|
||||||
|
ctx.fillStyle = '#f60'
|
||||||
|
ctx.fillRect(125,1,62,20)
|
||||||
|
ctx.fillStyle = '#069'
|
||||||
|
ctx.fillText(txt, 2, 15)
|
||||||
|
ctx.fillStyle = 'rgba(100, 200, 0, 0.7)'
|
||||||
|
ctx.fillText(txt, 4, 17)
|
||||||
|
return canvas.toDataURL()
|
||||||
|
}
|
||||||
|
|
||||||
$.fn[pluginName] = function (options) {
|
$.fn[pluginName] = function (options) {
|
||||||
return this.each(function () {
|
return this.each(function () {
|
||||||
var instance = $.data(this, 'plugin_' + pluginName)
|
var instance = $.data(this, 'plugin_' + pluginName)
|
||||||
|
|
|
@ -82,23 +82,6 @@ class FormTest < TestCase
|
||||||
browser: agent,
|
browser: agent,
|
||||||
css: 'body div.zammad-form-modal button[type="submit"][disabled]',
|
css: 'body div.zammad-form-modal button[type="submit"][disabled]',
|
||||||
)
|
)
|
||||||
set(
|
|
||||||
browser: agent,
|
|
||||||
css: 'body div.zammad-form-modal [name="email"]',
|
|
||||||
value: 'notexistinginanydomainspacealsonothere@znuny.com',
|
|
||||||
)
|
|
||||||
click(
|
|
||||||
browser: agent,
|
|
||||||
css: 'body div.zammad-form-modal button[type="submit"]',
|
|
||||||
)
|
|
||||||
watch_for(
|
|
||||||
browser: agent,
|
|
||||||
css: 'body div.zammad-form-modal .has-error [name="email"]',
|
|
||||||
)
|
|
||||||
watch_for_disappear(
|
|
||||||
browser: agent,
|
|
||||||
css: 'body div.zammad-form-modal button[type="submit"][disabled]',
|
|
||||||
)
|
|
||||||
set(
|
set(
|
||||||
browser: agent,
|
browser: agent,
|
||||||
css: 'body div.zammad-form-modal [name="email"]',
|
css: 'body div.zammad-form-modal [name="email"]',
|
||||||
|
@ -315,23 +298,6 @@ class FormTest < TestCase
|
||||||
browser: customer,
|
browser: customer,
|
||||||
css: 'body div.zammad-form-modal button[type="submit"][disabled]',
|
css: 'body div.zammad-form-modal button[type="submit"][disabled]',
|
||||||
)
|
)
|
||||||
set(
|
|
||||||
browser: customer,
|
|
||||||
css: 'body div.zammad-form-modal [name="email"]',
|
|
||||||
value: 'notexistinginanydomainspacealsonothere@znuny.com',
|
|
||||||
)
|
|
||||||
click(
|
|
||||||
browser: customer,
|
|
||||||
css: 'body div.zammad-form-modal button[type="submit"]',
|
|
||||||
)
|
|
||||||
watch_for(
|
|
||||||
browser: customer,
|
|
||||||
css: 'body div.zammad-form-modal .has-error [name="email"]',
|
|
||||||
)
|
|
||||||
watch_for_disappear(
|
|
||||||
browser: customer,
|
|
||||||
css: 'body div.zammad-form-modal button[type="submit"][disabled]',
|
|
||||||
)
|
|
||||||
set(
|
set(
|
||||||
browser: customer,
|
browser: customer,
|
||||||
css: 'body div.zammad-form-modal [name="email"]',
|
css: 'body div.zammad-form-modal [name="email"]',
|
||||||
|
|
241
test/controllers/form_controller_test.rb
Normal file
241
test/controllers/form_controller_test.rb
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
require 'test_helper'
|
||||||
|
require 'rake'
|
||||||
|
|
||||||
|
class FormControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
setup do
|
||||||
|
@headers = { 'ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json', 'REMOTE_ADDR' => '1.2.3.4' }
|
||||||
|
|
||||||
|
if ENV['ES_URL'].present?
|
||||||
|
|
||||||
|
#fail "ERROR: Need ES_URL - hint ES_URL='http://127.0.0.1:9200'"
|
||||||
|
Setting.set('es_url', ENV['ES_URL'])
|
||||||
|
|
||||||
|
# Setting.set('es_url', 'http://127.0.0.1:9200')
|
||||||
|
# Setting.set('es_index', 'estest.local_zammad')
|
||||||
|
# Setting.set('es_user', 'elasticsearch')
|
||||||
|
# Setting.set('es_password', 'zammad')
|
||||||
|
|
||||||
|
if ENV['ES_INDEX_RAND'].present?
|
||||||
|
ENV['ES_INDEX'] = "es_index_#{rand(999_999_999)}"
|
||||||
|
end
|
||||||
|
if ENV['ES_INDEX'].blank?
|
||||||
|
raise "ERROR: Need ES_INDEX - hint ES_INDEX='estest.local_zammad'"
|
||||||
|
end
|
||||||
|
Setting.set('es_index', ENV['ES_INDEX'])
|
||||||
|
end
|
||||||
|
|
||||||
|
Ticket.destroy_all
|
||||||
|
|
||||||
|
# drop/create indexes
|
||||||
|
Setting.reload
|
||||||
|
Rake::Task.clear
|
||||||
|
Zammad::Application.load_tasks
|
||||||
|
Rake::Task['searchindex:rebuild'].execute
|
||||||
|
end
|
||||||
|
|
||||||
|
test '01 - get config call' do
|
||||||
|
post '/api/v1/form_config', {}.to_json, @headers
|
||||||
|
assert_response(401)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
assert_equal(result['error'], 'Not authorized')
|
||||||
|
end
|
||||||
|
|
||||||
|
test '02 - get config call' do
|
||||||
|
Setting.set('form_ticket_create', true)
|
||||||
|
post '/api/v1/form_config', {}.to_json, @headers
|
||||||
|
assert_response(401)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
assert_equal(result['error'], 'Not authorized')
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
test '03 - get config call & do submit' do
|
||||||
|
Setting.set('form_ticket_create', true)
|
||||||
|
fingerprint = SecureRandom.hex(40)
|
||||||
|
post '/api/v1/form_config', { fingerprint: fingerprint }.to_json, @headers
|
||||||
|
|
||||||
|
assert_response(200)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
assert_equal(result['enabled'], true)
|
||||||
|
assert_equal(result['endpoint'], 'http://zammad.example.com/api/v1/form_submit')
|
||||||
|
assert(result['token'])
|
||||||
|
token = result['token']
|
||||||
|
|
||||||
|
post '/api/v1/form_submit', { fingerprint: fingerprint, token: 'invalid' }.to_json, @headers
|
||||||
|
assert_response(401)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
assert_equal(result['error'], 'Not authorized')
|
||||||
|
|
||||||
|
post '/api/v1/form_submit', { fingerprint: fingerprint, token: token }.to_json, @headers
|
||||||
|
assert_response(200)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
|
||||||
|
assert(result['errors'])
|
||||||
|
assert_equal(result['errors']['name'], 'required')
|
||||||
|
assert_equal(result['errors']['email'], 'required')
|
||||||
|
assert_equal(result['errors']['title'], 'required')
|
||||||
|
assert_equal(result['errors']['body'], 'required')
|
||||||
|
|
||||||
|
post '/api/v1/form_submit', { fingerprint: fingerprint, token: token, email: 'some' }.to_json, @headers
|
||||||
|
assert_response(200)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
|
||||||
|
assert(result['errors'])
|
||||||
|
assert_equal(result['errors']['name'], 'required')
|
||||||
|
assert_equal(result['errors']['email'], 'invalid')
|
||||||
|
assert_equal(result['errors']['title'], 'required')
|
||||||
|
assert_equal(result['errors']['body'], 'required')
|
||||||
|
|
||||||
|
post '/api/v1/form_submit', { fingerprint: fingerprint, token: token, name: 'Bob Smith', email: 'discard@znuny.com', title: 'test', body: 'hello' }.to_json, @headers
|
||||||
|
assert_response(200)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
|
||||||
|
assert_not(result['errors'])
|
||||||
|
assert(result['ticket'])
|
||||||
|
assert(result['ticket']['id'])
|
||||||
|
assert(result['ticket']['number'])
|
||||||
|
|
||||||
|
travel 5.hours
|
||||||
|
|
||||||
|
post '/api/v1/form_submit', { fingerprint: fingerprint, token: token, name: 'Bob Smith', email: 'discard@znuny.com', title: 'test', body: 'hello' }.to_json, @headers
|
||||||
|
|
||||||
|
assert_response(200)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
|
||||||
|
assert_not(result['errors'])
|
||||||
|
assert(result['ticket'])
|
||||||
|
assert(result['ticket']['id'])
|
||||||
|
assert(result['ticket']['number'])
|
||||||
|
|
||||||
|
travel 20.hours
|
||||||
|
|
||||||
|
post '/api/v1/form_submit', { fingerprint: fingerprint, token: token, name: 'Bob Smith', email: 'discard@znuny.com', title: 'test', body: 'hello' }.to_json, @headers
|
||||||
|
assert_response(401)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
test '04 - get config call & do submit' do
|
||||||
|
Setting.set('form_ticket_create', true)
|
||||||
|
fingerprint = SecureRandom.hex(40)
|
||||||
|
post '/api/v1/form_config', { fingerprint: fingerprint }.to_json, @headers
|
||||||
|
|
||||||
|
assert_response(200)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
assert_equal(result['enabled'], true)
|
||||||
|
assert_equal(result['endpoint'], 'http://zammad.example.com/api/v1/form_submit')
|
||||||
|
assert(result['token'])
|
||||||
|
token = result['token']
|
||||||
|
|
||||||
|
post '/api/v1/form_submit', { fingerprint: fingerprint, token: 'invalid' }.to_json, @headers
|
||||||
|
assert_response(401)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
assert_equal(result['error'], 'Not authorized')
|
||||||
|
|
||||||
|
post '/api/v1/form_submit', { fingerprint: fingerprint, token: token }.to_json, @headers
|
||||||
|
assert_response(200)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
|
||||||
|
assert(result['errors'])
|
||||||
|
assert_equal(result['errors']['name'], 'required')
|
||||||
|
assert_equal(result['errors']['email'], 'required')
|
||||||
|
assert_equal(result['errors']['title'], 'required')
|
||||||
|
assert_equal(result['errors']['body'], 'required')
|
||||||
|
|
||||||
|
post '/api/v1/form_submit', { fingerprint: fingerprint, token: token, email: 'some' }.to_json, @headers
|
||||||
|
assert_response(200)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
|
||||||
|
assert(result['errors'])
|
||||||
|
assert_equal(result['errors']['name'], 'required')
|
||||||
|
assert_equal(result['errors']['email'], 'invalid')
|
||||||
|
assert_equal(result['errors']['title'], 'required')
|
||||||
|
assert_equal(result['errors']['body'], 'required')
|
||||||
|
|
||||||
|
post '/api/v1/form_submit', { fingerprint: fingerprint, token: token, name: 'Bob Smith', email: 'somebody@example.com', title: 'test', body: 'hello' }.to_json, @headers
|
||||||
|
assert_response(200)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
|
||||||
|
assert(result['errors'])
|
||||||
|
assert_equal(result['errors']['email'], 'invalid')
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
test '05 - limits' do
|
||||||
|
return if !SearchIndexBackend.enabled?
|
||||||
|
|
||||||
|
Setting.set('form_ticket_create', true)
|
||||||
|
fingerprint = SecureRandom.hex(40)
|
||||||
|
post '/api/v1/form_config', { fingerprint: fingerprint }.to_json, @headers
|
||||||
|
|
||||||
|
assert_response(200)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
assert_equal(result['enabled'], true)
|
||||||
|
assert_equal(result['endpoint'], 'http://zammad.example.com/api/v1/form_submit')
|
||||||
|
assert(result['token'])
|
||||||
|
token = result['token']
|
||||||
|
|
||||||
|
(1..20).each { |count|
|
||||||
|
travel 10.seconds
|
||||||
|
post '/api/v1/form_submit', { fingerprint: fingerprint, token: token, name: 'Bob Smith', email: 'discard@znuny.com', title: "test#{count}", body: 'hello' }.to_json, @headers
|
||||||
|
assert_response(200)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
|
||||||
|
assert_not(result['errors'])
|
||||||
|
assert(result['ticket'])
|
||||||
|
assert(result['ticket']['id'])
|
||||||
|
assert(result['ticket']['number'])
|
||||||
|
Scheduler.worker(true)
|
||||||
|
sleep 1 # wait until elasticsearch is index
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep 10 # wait until elasticsearch is index
|
||||||
|
|
||||||
|
post '/api/v1/form_submit', { fingerprint: fingerprint, token: token, name: 'Bob Smith', email: 'discard@znuny.com', title: 'test-last', body: 'hello' }.to_json, @headers
|
||||||
|
assert_response(401)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
assert(result['error'])
|
||||||
|
|
||||||
|
@headers = { 'ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json', 'REMOTE_ADDR' => '1.2.3.5' }
|
||||||
|
|
||||||
|
(1..20).each { |count|
|
||||||
|
travel 10.seconds
|
||||||
|
post '/api/v1/form_submit', { fingerprint: fingerprint, token: token, name: 'Bob Smith', email: 'discard@znuny.com', title: "test-2-#{count}", body: 'hello' }.to_json, @headers
|
||||||
|
assert_response(200)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
|
||||||
|
assert_not(result['errors'])
|
||||||
|
assert(result['ticket'])
|
||||||
|
assert(result['ticket']['id'])
|
||||||
|
assert(result['ticket']['number'])
|
||||||
|
Scheduler.worker(true)
|
||||||
|
sleep 1 # wait until elasticsearch is index
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep 10 # wait until elasticsearch is index
|
||||||
|
|
||||||
|
post '/api/v1/form_submit', { fingerprint: fingerprint, token: token, name: 'Bob Smith', email: 'discard@znuny.com', title: 'test-2-last', body: 'hello' }.to_json, @headers
|
||||||
|
assert_response(401)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(result.class, Hash)
|
||||||
|
assert(result['error'])
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in a new issue