diff --git a/app/assets/javascripts/app/controllers/_integration/sipgate_io.coffee b/app/assets/javascripts/app/controllers/_integration/sipgate_io.coffee index 5bb08853e..ba41f6935 100644 --- a/app/assets/javascripts/app/controllers/_integration/sipgate_io.coffee +++ b/app/assets/javascripts/app/controllers/_integration/sipgate_io.coffee @@ -57,6 +57,7 @@ class Form extends App.Controller @html App.view('integration/sipgate')( config: @config + sipgate_token: App.Setting.get('sipgate_token') ) configure_attributes = [ diff --git a/app/assets/javascripts/app/views/integration/sipgate.jst.eco b/app/assets/javascripts/app/views/integration/sipgate.jst.eco index 5a194891b..4b69d2621 100644 --- a/app/assets/javascripts/app/views/integration/sipgate.jst.eco +++ b/app/assets/javascripts/app/views/integration/sipgate.jst.eco @@ -14,10 +14,10 @@ <%- @T('Inbound') %> - + <%- @T('Outbound') %> - + diff --git a/app/controllers/integration/sipgate_controller.rb b/app/controllers/integration/sipgate_controller.rb index 87144bd04..2d726c90a 100644 --- a/app/controllers/integration/sipgate_controller.rb +++ b/app/controllers/integration/sipgate_controller.rb @@ -4,7 +4,7 @@ require 'builder' class Integration::SipgateController < ApplicationController skip_before_action :verify_csrf_token - before_action :check_configured + before_action :check_configured, :check_token # notify about inbound call / block inbound call def event @@ -38,6 +38,15 @@ class Integration::SipgateController < ApplicationController private + def check_token + if Setting.get('sipgate_token') != params[:token] + response_unauthorized(__('Invalid token, please contact your admin!')) + return + end + + true + end + def check_configured http_log_config facility: 'sipgate.io' @@ -57,13 +66,13 @@ class Integration::SipgateController < ApplicationController @config_integration ||= Setting.get('sipgate_config') end - def xml_error(error) + def xml_error(error, code = 422) xml = Builder::XmlMarkup.new(indent: 2) xml.instruct! content = xml.Response() do xml.Error(error) end - send_data content, type: 'application/xml; charset=UTF-8;', status: 422 + send_data content, type: 'application/xml; charset=UTF-8;', status: code end def base_url @@ -72,13 +81,17 @@ class Integration::SipgateController < ApplicationController if fqdn.blank? fqdn = Setting.get('fqdn') end - "#{http_type}://#{fqdn}/api/v1/sipgate" + "#{http_type}://#{fqdn}/api/v1/sipgate/#{Setting.get('sipgate_token')}" end def url "#{base_url}/#{params['direction']}" end + def response_unauthorized(error) + xml_error(error, 401) + end + def response_reject(_result) xml = Builder::XmlMarkup.new(indent: 2) xml.instruct! diff --git a/config/routes/integration_sipgate.rb b/config/routes/integration_sipgate.rb index d94ab5326..780671aec 100644 --- a/config/routes/integration_sipgate.rb +++ b/config/routes/integration_sipgate.rb @@ -2,7 +2,7 @@ Zammad::Application.routes.draw do - match '/api/v1/sipgate/in', to: 'integration/sipgate#event', via: :post - match '/api/v1/sipgate/out', to: 'integration/sipgate#event', via: :post + match '/api/v1/sipgate/:token/in/', to: 'integration/sipgate#event', via: :post + match '/api/v1/sipgate/:token/out/', to: 'integration/sipgate#event', via: :post end diff --git a/db/migrate/20211118081558_maintenance_improve_sipgate_integration_handling.rb b/db/migrate/20211118081558_maintenance_improve_sipgate_integration_handling.rb new file mode 100644 index 000000000..4517fb9e7 --- /dev/null +++ b/db/migrate/20211118081558_maintenance_improve_sipgate_integration_handling.rb @@ -0,0 +1,29 @@ +# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/ + +class MaintenanceImproveSipgateIntegrationHandling < ActiveRecord::Migration[6.0] + def change + return if !Setting.exists?(name: 'system_init_done') + + Setting.create_if_not_exists( + title: __('sipgate.io Token'), + name: 'sipgate_token', + area: 'Integration::Sipgate', + description: __('Token for Sipgate.'), + options: { + form: [ + { + display: '', + null: false, + name: 'sipgate_token', + tag: 'input', + }, + ], + }, + state: ENV['SIPGATE_TOKEN'] || SecureRandom.urlsafe_base64(20), + preferences: { + permission: ['admin.integration'], + }, + frontend: false + ) + end +end diff --git a/db/seeds/settings.rb b/db/seeds/settings.rb index b5e5f65c7..63c673c03 100644 --- a/db/seeds/settings.rb +++ b/db/seeds/settings.rb @@ -4515,6 +4515,27 @@ Setting.create_if_not_exists( }, frontend: true ) +Setting.create_if_not_exists( + title: __('sipgate.io Token'), + name: 'sipgate_token', + area: 'Integration::Sipgate', + description: __('Token for Sipgate.'), + options: { + form: [ + { + display: '', + null: false, + name: 'sipgate_token', + tag: 'input', + }, + ], + }, + state: ENV['SIPGATE_TOKEN'] || SecureRandom.urlsafe_base64(20), + preferences: { + permission: ['admin.integration'], + }, + frontend: false +) Setting.create_if_not_exists( title: __('sipgate.io config'), name: 'sipgate_config', diff --git a/i18n/zammad.pot b/i18n/zammad.pot index 7ccd63ca6..9b10c3e53 100644 --- a/i18n/zammad.pot +++ b/i18n/zammad.pot @@ -5208,6 +5208,7 @@ msgstr "" #: app/controllers/integration/cti_controller.rb #: app/controllers/integration/placetel_controller.rb +#: app/controllers/integration/sipgate_controller.rb msgid "Invalid token, please contact your admin!" msgstr "" @@ -9491,6 +9492,11 @@ msgstr "" msgid "Token for CTI." msgstr "" +#: db/migrate/20211118081558_maintenance_improve_sipgate_integration_handling.rb +#: db/seeds/settings.rb +msgid "Token for Sipgate." +msgstr "" + #: db/seeds/settings.rb msgid "Token for monitoring." msgstr "" @@ -11340,6 +11346,11 @@ msgstr "" msgid "show more" msgstr "" +#: db/migrate/20211118081558_maintenance_improve_sipgate_integration_handling.rb +#: db/seeds/settings.rb +msgid "sipgate.io Token" +msgstr "" + #: db/seeds/settings.rb msgid "sipgate.io alternative FQDN" msgstr "" diff --git a/spec/requests/integration/sipgate_spec.rb b/spec/requests/integration/sipgate_spec.rb index a7ed69193..bf4ad211a 100644 --- a/spec/requests/integration/sipgate_spec.rb +++ b/spec/requests/integration/sipgate_spec.rb @@ -74,12 +74,25 @@ RSpec.describe 'Integration Sipgate', type: :request do end describe 'request handling' do + it 'does token check' do + params = 'event=newCall&direction=in&from=4912347114711&to=4930600000000&callId=4991155921769858278-1&user%5B%5D=user+1&user%5B%5D=user+2' + post '/api/v1/sipgate/not_existing_token/in', params: params + expect(response).to have_http_status(:unauthorized) + + error = nil + local_response = REXML::Document.new(response.body) + local_response.elements.each('Response/Error') do |element| + error = element.text + end + expect(error).to eq('Invalid token, please contact your admin!') + end it 'does basic call' do + token = Setting.get('sipgate_token') # inbound - I params = 'event=newCall&direction=in&from=4912347114711&to=4930600000000&callId=4991155921769858278-1&user%5B%5D=user+1&user%5B%5D=user+2' - post '/api/v1/sipgate/in', params: params + post "/api/v1/sipgate/#{token}/in", params: params expect(@response).to have_http_status(:ok) on_hangup = nil on_answer = nil @@ -89,12 +102,12 @@ RSpec.describe 'Integration Sipgate', type: :request do on_hangup = element.attributes['onHangup'] on_answer = element.attributes['onAnswer'] end - expect(on_hangup).to eq('http://zammad.example.com/api/v1/sipgate/in') - expect(on_answer).to eq('http://zammad.example.com/api/v1/sipgate/in') + expect(on_hangup).to eq("http://zammad.example.com/api/v1/sipgate/#{token}/in") + expect(on_answer).to eq("http://zammad.example.com/api/v1/sipgate/#{token}/in") # inbound - II - block caller params = 'event=newCall&direction=in&from=491715000000&to=4930600000000&callId=4991155921769858278-2&user%5B%5D=user+1&user%5B%5D=user+2' - post '/api/v1/sipgate/in', params: params + post "/api/v1/sipgate/#{token}/in", params: params expect(@response).to have_http_status(:ok) on_hangup = nil on_answer = nil @@ -104,8 +117,8 @@ RSpec.describe 'Integration Sipgate', type: :request do on_hangup = element.attributes['onHangup'] on_answer = element.attributes['onAnswer'] end - expect(on_hangup).to eq('http://zammad.example.com/api/v1/sipgate/in') - expect(on_answer).to eq('http://zammad.example.com/api/v1/sipgate/in') + expect(on_hangup).to eq("http://zammad.example.com/api/v1/sipgate/#{token}/in") + expect(on_answer).to eq("http://zammad.example.com/api/v1/sipgate/#{token}/in") reason = nil response.elements.each('Response/Reject') do |element| reason = element.attributes['reason'] @@ -114,7 +127,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # outbound - I - set default_caller_id params = 'event=newCall&direction=out&from=4930600000000&to=4912347114711&callId=8621106404543334274-3&user%5B%5D=user+1' - post '/api/v1/sipgate/out', params: params + post "/api/v1/sipgate/#{token}/out", params: params expect(@response).to have_http_status(:ok) on_hangup = nil on_answer = nil @@ -134,12 +147,12 @@ RSpec.describe 'Integration Sipgate', type: :request do end expect(caller_id).to eq('4930777000000') expect(number_to_dail).to eq('4912347114711') - expect(on_hangup).to eq('http://zammad.example.com/api/v1/sipgate/out') - expect(on_answer).to eq('http://zammad.example.com/api/v1/sipgate/out') + expect(on_hangup).to eq("http://zammad.example.com/api/v1/sipgate/#{token}/out") + expect(on_answer).to eq("http://zammad.example.com/api/v1/sipgate/#{token}/out") # outbound - II - set caller_id based on routing_table by explicite number params = 'event=newCall&direction=out&from=4930600000000&to=491714000000&callId=8621106404543334274-4&user%5B%5D=user+1' - post '/api/v1/sipgate/out', params: params + post "/api/v1/sipgate/#{token}/out", params: params expect(@response).to have_http_status(:ok) on_hangup = nil on_answer = nil @@ -159,12 +172,12 @@ RSpec.describe 'Integration Sipgate', type: :request do end expect(caller_id).to eq('41715880339000') expect(number_to_dail).to eq('491714000000') - expect(on_hangup).to eq('http://zammad.example.com/api/v1/sipgate/out') - expect(on_answer).to eq('http://zammad.example.com/api/v1/sipgate/out') + expect(on_hangup).to eq("http://zammad.example.com/api/v1/sipgate/#{token}/out") + expect(on_answer).to eq("http://zammad.example.com/api/v1/sipgate/#{token}/out") # outbound - III - set caller_id based on routing_table by 41* params = 'event=newCall&direction=out&from=4930600000000&to=4147110000000&callId=8621106404543334274-5&user%5B%5D=user+1' - post '/api/v1/sipgate/out', params: params + post "/api/v1/sipgate/#{token}/out", params: params expect(@response).to have_http_status(:ok) on_hangup = nil on_answer = nil @@ -184,13 +197,13 @@ RSpec.describe 'Integration Sipgate', type: :request do end expect(caller_id).to eq('41715880339000') expect(number_to_dail).to eq('4147110000000') - expect(on_hangup).to eq('http://zammad.example.com/api/v1/sipgate/out') - expect(on_answer).to eq('http://zammad.example.com/api/v1/sipgate/out') + expect(on_hangup).to eq("http://zammad.example.com/api/v1/sipgate/#{token}/out") + expect(on_answer).to eq("http://zammad.example.com/api/v1/sipgate/#{token}/out") # no config Setting.set('sipgate_config', {}) params = 'event=newCall&direction=in&from=4912347114711&to=4930600000000&callId=4991155921769858278-6&user%5B%5D=user+1&user%5B%5D=user+2' - post '/api/v1/sipgate/in', params: params + post "/api/v1/sipgate/#{token}/in", params: params expect(@response).to have_http_status(:unprocessable_entity) error = nil content = @response.body @@ -203,10 +216,11 @@ RSpec.describe 'Integration Sipgate', type: :request do end it 'does log call' do + token = Setting.get('sipgate_token') # outbound - I - new call params = 'event=newCall&direction=out&from=4930600000000&to=4912347114711&callId=1234567890-1&user%5B%5D=user+1' - post '/api/v1/sipgate/out', params: params + post "/api/v1/sipgate/#{token}/out", params: params expect(@response).to have_http_status(:ok) log = Cti::Log.find_by(call_id: '1234567890-1') expect(log).to be_truthy @@ -223,7 +237,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # outbound - I - hangup by agent params = 'event=hangup&direction=out&callId=1234567890-1&cause=cancel' - post '/api/v1/sipgate/out', params: params + post "/api/v1/sipgate/#{token}/out", params: params expect(@response).to have_http_status(:ok) log = Cti::Log.find_by(call_id: '1234567890-1') expect(log).to be_truthy @@ -240,7 +254,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # outbound - II - new call params = 'event=newCall&direction=out&from=4930600000000&to=4912347114711&callId=1234567890-2&user%5B%5D=user+1' - post '/api/v1/sipgate/out', params: params + post "/api/v1/sipgate/#{token}/out", params: params expect(@response).to have_http_status(:ok) log = Cti::Log.find_by(call_id: '1234567890-2') expect(log).to be_truthy @@ -257,7 +271,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # outbound - II - answer by customer params = 'event=answer&direction=out&callId=1234567890-2&from=4930600000000&to=4912347114711' - post '/api/v1/sipgate/out', params: params + post "/api/v1/sipgate/#{token}/out", params: params expect(@response).to have_http_status(:ok) log = Cti::Log.find_by(call_id: '1234567890-2') expect(log).to be_truthy @@ -274,7 +288,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # outbound - II - hangup by customer params = 'event=hangup&direction=out&callId=1234567890-2&cause=normalClearing&from=4930600000000&to=4912347114711' - post '/api/v1/sipgate/out', params: params + post "/api/v1/sipgate/#{token}/out", params: params expect(@response).to have_http_status(:ok) log = Cti::Log.find_by(call_id: '1234567890-2') expect(log).to be_truthy @@ -291,7 +305,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # inbound - I - new call params = 'event=newCall&direction=in&to=4930600000000&from=4912347114711&callId=1234567890-3&user%5B%5D=user+1' - post '/api/v1/sipgate/in', params: params + post "/api/v1/sipgate/#{token}/in", params: params expect(@response).to have_http_status(:ok) log = Cti::Log.find_by(call_id: '1234567890-3') expect(log).to be_truthy @@ -308,7 +322,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # inbound - I - answer by customer params = 'event=answer&direction=in&callId=1234567890-3&to=4930600000000&from=4912347114711' - post '/api/v1/sipgate/in', params: params + post "/api/v1/sipgate/#{token}/in", params: params expect(@response).to have_http_status(:ok) log = Cti::Log.find_by(call_id: '1234567890-3') expect(log).to be_truthy @@ -325,7 +339,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # inbound - I - hangup by customer params = 'event=hangup&direction=in&callId=1234567890-3&cause=normalClearing&to=4930600000000&from=4912347114711' - post '/api/v1/sipgate/in', params: params + post "/api/v1/sipgate/#{token}/in", params: params expect(@response).to have_http_status(:ok) log = Cti::Log.find_by(call_id: '1234567890-3') expect(log).to be_truthy @@ -342,7 +356,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # inbound - II - new call params = 'event=newCall&direction=in&to=4930600000000&from=4912347114711&callId=1234567890-4&user%5B%5D=user+1,user+2' - post '/api/v1/sipgate/in', params: params + post "/api/v1/sipgate/#{token}/in", params: params expect(@response).to have_http_status(:ok) log = Cti::Log.find_by(call_id: '1234567890-4') expect(log).to be_truthy @@ -359,7 +373,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # inbound - II - answer by voicemail params = 'event=answer&direction=in&callId=1234567890-4&to=4930600000000&from=4912347114711&user=voicemail' - post '/api/v1/sipgate/in', params: params + post "/api/v1/sipgate/#{token}/in", params: params expect(@response).to have_http_status(:ok) log = Cti::Log.find_by(call_id: '1234567890-4') expect(log).to be_truthy @@ -376,7 +390,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # inbound - II - hangup by customer params = 'event=hangup&direction=in&callId=1234567890-4&cause=normalClearing&to=4930600000000&from=4912347114711' - post '/api/v1/sipgate/in', params: params + post "/api/v1/sipgate/#{token}/in", params: params expect(@response).to have_http_status(:ok) log = Cti::Log.find_by(call_id: '1234567890-4') expect(log).to be_truthy @@ -393,7 +407,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # inbound - III - new call params = 'event=newCall&direction=in&to=4930600000000&from=4912347114711&callId=1234567890-5&user%5B%5D=user+1,user+2' - post '/api/v1/sipgate/in', params: params + post "/api/v1/sipgate/#{token}/in", params: params expect(@response).to have_http_status(:ok) log = Cti::Log.find_by(call_id: '1234567890-5') expect(log).to be_truthy @@ -410,7 +424,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # inbound - III - hangup by customer params = 'event=hangup&direction=in&callId=1234567890-5&cause=normalClearing&to=4930600000000&from=4912347114711' - post '/api/v1/sipgate/in', params: params + post "/api/v1/sipgate/#{token}/in", params: params expect(@response).to have_http_status(:ok) log = Cti::Log.find_by(call_id: '1234567890-5') expect(log).to be_truthy @@ -427,7 +441,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # inbound - IV - new call params = 'event=newCall&direction=in&to=4930600000000&from=49999992222222&callId=1234567890-6&user%5B%5D=user+1,user+2' - post '/api/v1/sipgate/in', params: params + post "/api/v1/sipgate/#{token}/in", params: params expect(@response).to have_http_status(:ok) log = Cti::Log.find_by(call_id: '1234567890-6') expect(log).to be_truthy @@ -471,11 +485,13 @@ RSpec.describe 'Integration Sipgate', type: :request do end it 'alternative fqdn' do + token = Setting.get('sipgate_token') + Setting.set('sipgate_alternative_fqdn', 'external.host.example.com') # inbound - I params = 'event=newCall&direction=in&from=4912347114711&to=4930600000000&callId=4991155921769858278-1&user%5B%5D=user+1&user%5B%5D=user+2' - post '/api/v1/sipgate/in', params: params + post "/api/v1/sipgate/#{token}/in", params: params expect(@response).to have_http_status(:ok) on_hangup = nil on_answer = nil @@ -485,8 +501,8 @@ RSpec.describe 'Integration Sipgate', type: :request do on_hangup = element.attributes['onHangup'] on_answer = element.attributes['onAnswer'] end - expect(on_hangup).to eq('http://external.host.example.com/api/v1/sipgate/in') - expect(on_answer).to eq('http://external.host.example.com/api/v1/sipgate/in') + expect(on_hangup).to eq("http://external.host.example.com/api/v1/sipgate/#{token}/in") + expect(on_answer).to eq("http://external.host.example.com/api/v1/sipgate/#{token}/in") end end end diff --git a/spec/system/sipgate_spec.rb b/spec/system/sipgate_spec.rb index cb2c307a5..d2c314efe 100644 --- a/spec/system/sipgate_spec.rb +++ b/spec/system/sipgate_spec.rb @@ -23,8 +23,10 @@ RSpec.describe 'Caller log', type: :system, authenticated_as: :authenticate do let(:second_params) { params.merge(event: 'hangup') } let(:place_call) do - post "#{Capybara.app_host}/api/v1/sipgate/in", params: first_params - post "#{Capybara.app_host}/api/v1/sipgate/in", params: second_params + token = Setting.get('sipgate_token') + + post "#{Capybara.app_host}/api/v1/sipgate/#{token}/in", params: first_params + post "#{Capybara.app_host}/api/v1/sipgate/#{token}/in", params: second_params end def authenticate