Fixes #3022 - Detect ServiceNow follow-ups

This commit is contained in:
Rolf Schmidt 2020-04-17 09:24:57 +02:00 committed by Thorsten Eckel
parent 3f453c31e9
commit afa6b9984a
6 changed files with 178 additions and 0 deletions

View file

@ -0,0 +1,60 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module Channel::Filter::ServiceNowCheck
# This filter will run pre and post
def self.run(_channel, mail, ticket = nil, _article = nil, _session_user = nil)
source_id = self.source_id(from: mail[:from], subject: mail[:subject])
return if source_id.blank?
# check if we can followup by existing service now relation
if ticket.blank?
sync_entry = ExternalSync.find_by(
source: 'ServiceNow',
source_id: source_id,
object: 'Ticket',
)
return if sync_entry.blank?
mail[ 'x-zammad-ticket-id'.to_sym ] = sync_entry.o_id
return
end
ExternalSync.create_with(source_id: source_id).find_or_create_by(source: 'ServiceNow', object: 'Ticket', o_id: ticket.id)
end
=begin
This function returns the source id of the service now email if given.
source_id = Channel::Filter::ServiceNowCheck.source_id(
from: 'test@servicnow.com',
subject: 'Incident INC12345 --- test',
)
returns:
source_id = 'INC12345'
=end
def self.source_id(from: '', subject: '')
# check if data is sent by service now
begin
return if Mail::AddressList.new(from).addresses.none? do |line|
line.address.end_with?('@service-now.com')
end
rescue
Rails.logger.info "Unable to parse email address in '#{from}'"
end
# check if we can find the service now relation
source_id = nil
if subject =~ /\s(INC\d+)\s/
source_id = $1
end
source_id
end
end

View file

@ -0,0 +1,28 @@
class ServiceNowConfig < ActiveRecord::Migration[4.2]
def up
# return if it's a new setup
return if !Setting.find_by(name: 'system_init_done')
Setting.create_if_not_exists(
title: 'Defines postmaster filter.',
name: '5400_postmaster_filter_service_now_check',
area: 'Postmaster::PreFilter',
description: 'Defines postmaster filter to identify service now mails for correct follow-ups.',
options: {},
state: 'Channel::Filter::ServiceNowCheck',
frontend: false
)
Setting.create_if_not_exists(
title: 'Defines postmaster filter.',
name: '5401_postmaster_filter_service_now_check',
area: 'Postmaster::PostFilter',
description: 'Defines postmaster filter to identify service now mails for correct follow-ups.',
options: {},
state: 'Channel::Filter::ServiceNowCheck',
frontend: false
)
end
end

View file

@ -3449,6 +3449,24 @@ Setting.create_if_not_exists(
state: 'Channel::Filter::Monit',
frontend: false
)
Setting.create_if_not_exists(
title: 'Defines postmaster filter.',
name: '5400_postmaster_filter_service_now_check',
area: 'Postmaster::PreFilter',
description: 'Defines postmaster filter to identify service now mails for correct follow-ups.',
options: {},
state: 'Channel::Filter::ServiceNowCheck',
frontend: false
)
Setting.create_if_not_exists(
title: 'Defines postmaster filter.',
name: '5401_postmaster_filter_service_now_check',
area: 'Postmaster::PostFilter',
description: 'Defines postmaster filter to identify service now mails for correct follow-ups.',
options: {},
state: 'Channel::Filter::ServiceNowCheck',
frontend: false
)
Setting.create_if_not_exists(
title: 'Icinga integration',
name: 'icinga_integration',

View file

@ -983,6 +983,34 @@ RSpec.describe Channel::EmailParser, type: :model do
end
end
describe 'ServiceNow handling' do
context 'when emails with service now reference are sent' do
let(:mail_file) { Rails.root.join('test/data/mail/mail089.box') }
let(:mail_file_answer) { Rails.root.join('test/data/mail/mail090.box') }
let(:raw_mail_answer) { File.read(mail_file_answer) }
it 'does create a ticket with external sync reference' do
expect { described_class.new.process({}, raw_mail) }
.to change(Ticket, :count).by(1)
.and change(Ticket::Article, :count).by(1)
.and change(ExternalSync, :count).by(1)
expect(ExternalSync.last.source).to eq('ServiceNow')
expect(ExternalSync.last.source_id).to eq('INC678439')
expect(ExternalSync.last.object).to eq('Ticket')
expect(ExternalSync.last.o_id).to eq(Ticket.last.id)
expect(Ticket.last.articles.last.subject).to eq('Incident INC678439 -- zugewiesen an EXT-XXXINIS')
expect { described_class.new.process({}, raw_mail_answer) }
.to change(Ticket, :count).by(0)
.and change(Ticket::Article, :count).by(1)
.and change(ExternalSync, :count).by(0)
expect(Ticket.last.articles.last.subject).to eq('Incident INC678439 -- Arbeitsnotizen beigefügt')
end
end
end
describe 'XSS protection' do
let(:article) { described_class.new.process({}, raw_mail).second }

View file

@ -0,0 +1,22 @@
From: IT example <example@service-now.com>
To: support@example.com
Message-ID: <18659453.58107.1576665116411@app129169.gva3.service-now.com>
Subject: Incident INC678439 -- zugewiesen an EXT-XXXINIS
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="----=_Part_58105_29005161.1576665006397"
X-ServiceNow-Source: Notification-5555502c4ff4aa0096d7e6101310c734
X-ServiceNow-SysEmail-Version: 2
Precedence: bulk
Auto-Submitted: auto-generated
X-ServiceNow-Generated: true
------=_Part_58105_29005161.1576665006397
Content-Type: multipart/alternative;
boundary="----=_Part_58106_4969544.1576665006398"
------=_Part_58106_4969544.1576665006398
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=UTF-8
Incident content

View file

@ -0,0 +1,22 @@
From: IT example <example@service-now.com>
To: support@example.com
Message-ID: <18659453.58107.1576665116411@app129169.gva3.service-now.com>
Subject: =?UTF-8?Q?Incident_INC678439_--_Arbeitsnotizen_beigef=C3=BCgt?=
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="----=_Part_58105_29005161.1576665006397"
X-ServiceNow-Source: Notification-5555502c4ff4aa0096d7e6101310c734
X-ServiceNow-SysEmail-Version: 2
Precedence: bulk
Auto-Submitted: auto-generated
X-ServiceNow-Generated: true
------=_Part_58105_29005161.1576665006397
Content-Type: multipart/alternative;
boundary="----=_Part_58106_4969544.1576665006398"
------=_Part_58106_4969544.1576665006398
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=UTF-8
Incident content