diff options
Diffstat (limited to 'extensions/Push/lib/Connector.disabled/ServiceNow.pm')
-rw-r--r-- | extensions/Push/lib/Connector.disabled/ServiceNow.pm | 690 |
1 files changed, 349 insertions, 341 deletions
diff --git a/extensions/Push/lib/Connector.disabled/ServiceNow.pm b/extensions/Push/lib/Connector.disabled/ServiceNow.pm index d0ebdcf10..032e47dde 100644 --- a/extensions/Push/lib/Connector.disabled/ServiceNow.pm +++ b/extensions/Push/lib/Connector.disabled/ServiceNow.pm @@ -32,403 +32,411 @@ use MIME::Base64; use Net::LDAP; use constant SEND_COMPONENTS => ( - { - product => 'mozilla.org', - component => 'Server Operations: Desktop Issues', - }, + {product => 'mozilla.org', component => 'Server Operations: Desktop Issues',}, ); sub options { - return ( - { - name => 'bugzilla_user', - label => 'Bugzilla Service-Now User', - type => 'string', - default => 'service.now@bugzilla.tld', - required => 1, - validate => sub { - Bugzilla::User->new({ name => $_[0] }) - || die "Invalid Bugzilla user ($_[0])\n"; - }, - }, - { - name => 'ldap_scheme', - label => 'Mozilla LDAP Scheme', - type => 'select', - values => [ 'LDAP', 'LDAPS' ], - default => 'LDAPS', - required => 1, - }, - { - name => 'ldap_host', - label => 'Mozilla LDAP Host', - type => 'string', - default => '', - required => 1, - }, - { - name => 'ldap_user', - label => 'Mozilla LDAP Bind Username', - type => 'string', - default => '', - required => 1, - }, - { - name => 'ldap_pass', - label => 'Mozilla LDAP Password', - type => 'password', - default => '', - required => 1, - }, - { - name => 'ldap_poll', - label => 'Mozilla LDAP Poll Frequency', - type => 'string', - default => '3', - required => 1, - help => 'minutes', - validate => sub { - $_[0] =~ /\D/ - && die "LDAP Poll Frequency must be an integer\n"; - $_[0] == 0 - && die "LDAP Poll Frequency cannot be less than one minute\n"; - }, - }, - { - name => 'service_now_url', - label => 'Service Now JSON URL', - type => 'string', - default => 'https://mozilladev.service-now.com', - required => 1, - help => "Must start with https:// and end with ?JSON", - validate => sub { - $_[0] =~ m#^https://[^\.\/]+\.service-now\.com\/# - || die "Invalid Service Now JSON URL\n"; - $_[0] =~ m#\?JSON$# - || die "Invalid Service Now JSON URL (must end with ?JSON)\n"; - }, - }, - { - name => 'service_now_user', - label => 'Service Now JSON Username', - type => 'string', - default => '', - required => 1, - }, - { - name => 'service_now_pass', - label => 'Service Now JSON Password', - type => 'password', - default => '', - required => 1, - }, - ); + return ( + { + name => 'bugzilla_user', + label => 'Bugzilla Service-Now User', + type => 'string', + default => 'service.now@bugzilla.tld', + required => 1, + validate => sub { + Bugzilla::User->new({name => $_[0]}) || die "Invalid Bugzilla user ($_[0])\n"; + }, + }, + { + name => 'ldap_scheme', + label => 'Mozilla LDAP Scheme', + type => 'select', + values => ['LDAP', 'LDAPS'], + default => 'LDAPS', + required => 1, + }, + { + name => 'ldap_host', + label => 'Mozilla LDAP Host', + type => 'string', + default => '', + required => 1, + }, + { + name => 'ldap_user', + label => 'Mozilla LDAP Bind Username', + type => 'string', + default => '', + required => 1, + }, + { + name => 'ldap_pass', + label => 'Mozilla LDAP Password', + type => 'password', + default => '', + required => 1, + }, + { + name => 'ldap_poll', + label => 'Mozilla LDAP Poll Frequency', + type => 'string', + default => '3', + required => 1, + help => 'minutes', + validate => sub { + $_[0] =~ /\D/ && die "LDAP Poll Frequency must be an integer\n"; + $_[0] == 0 && die "LDAP Poll Frequency cannot be less than one minute\n"; + }, + }, + { + name => 'service_now_url', + label => 'Service Now JSON URL', + type => 'string', + default => 'https://mozilladev.service-now.com', + required => 1, + help => "Must start with https:// and end with ?JSON", + validate => sub { + $_[0] =~ m#^https://[^\.\/]+\.service-now\.com\/# + || die "Invalid Service Now JSON URL\n"; + $_[0] =~ m#\?JSON$# + || die "Invalid Service Now JSON URL (must end with ?JSON)\n"; + }, + }, + { + name => 'service_now_user', + label => 'Service Now JSON Username', + type => 'string', + default => '', + required => 1, + }, + { + name => 'service_now_pass', + label => 'Service Now JSON Password', + type => 'password', + default => '', + required => 1, + }, + ); } sub options_validate { - my ($self, $config) = @_; - my $host = $config->{ldap_host}; - trick_taint($host); - my $scheme = lc($config->{ldap_scheme}); - eval { - my $ldap = Net::LDAP->new($host, scheme => $scheme, onerror => 'die', timeout => 5) - or die $!; - $ldap->bind($config->{ldap_user}, password => $config->{ldap_pass}); - }; - if ($@) { - die sprintf("Failed to connect to %s://%s/: %s\n", $scheme, $host, $@); - } + my ($self, $config) = @_; + my $host = $config->{ldap_host}; + trick_taint($host); + my $scheme = lc($config->{ldap_scheme}); + eval { + my $ldap + = Net::LDAP->new($host, scheme => $scheme, onerror => 'die', timeout => 5) + or die $!; + $ldap->bind($config->{ldap_user}, password => $config->{ldap_pass}); + }; + if ($@) { + die sprintf("Failed to connect to %s://%s/: %s\n", $scheme, $host, $@); + } } my $_instance; sub init { - my ($self) = @_; - $_instance = $self; + my ($self) = @_; + $_instance = $self; } sub load_config { - my ($self) = @_; - $self->SUPER::load_config(@_); - $self->{bugzilla_user} ||= Bugzilla::User->new({ name => $self->config->{bugzilla_user} }); + my ($self) = @_; + $self->SUPER::load_config(@_); + $self->{bugzilla_user} + ||= Bugzilla::User->new({name => $self->config->{bugzilla_user}}); } sub should_send { - my ($self, $message) = @_; - - my $data = $message->payload_decoded; - my $bug_data = $self->_get_bug_data($data) - || return 0; - - # we don't want to send the initial comment in a separate message - # because we inject it into the inital message - if (exists $data->{comment} && $data->{comment}->{number} == 0) { - return 0; - } - - my $target = $data->{event}->{target}; - unless ($target eq 'bug' || $target eq 'comment' || $target eq 'attachment') { - return 0; - } - - # ensure the service-now user can see the bug - if (!$self->{bugzilla_user} || !$self->{bugzilla_user}->is_enabled) { - return 0; - } - $self->{bugzilla_user}->can_see_bug($bug_data->{id}) - || return 0; - - # don't push changes made by the service-now account - $data->{event}->{user}->{id} == $self->{bugzilla_user}->id - && return 0; - - # filter based on the component - my $bug = Bugzilla::Bug->new($bug_data->{id}); - my $send = 0; - foreach my $rh (SEND_COMPONENTS) { - if ($bug->product eq $rh->{product} && $bug->component eq $rh->{component}) { - $send = 1; - last; - } + my ($self, $message) = @_; + + my $data = $message->payload_decoded; + my $bug_data = $self->_get_bug_data($data) || return 0; + + # we don't want to send the initial comment in a separate message + # because we inject it into the inital message + if (exists $data->{comment} && $data->{comment}->{number} == 0) { + return 0; + } + + my $target = $data->{event}->{target}; + unless ($target eq 'bug' || $target eq 'comment' || $target eq 'attachment') { + return 0; + } + + # ensure the service-now user can see the bug + if (!$self->{bugzilla_user} || !$self->{bugzilla_user}->is_enabled) { + return 0; + } + $self->{bugzilla_user}->can_see_bug($bug_data->{id}) || return 0; + + # don't push changes made by the service-now account + $data->{event}->{user}->{id} == $self->{bugzilla_user}->id && return 0; + + # filter based on the component + my $bug = Bugzilla::Bug->new($bug_data->{id}); + my $send = 0; + foreach my $rh (SEND_COMPONENTS) { + if ($bug->product eq $rh->{product} && $bug->component eq $rh->{component}) { + $send = 1; + last; } - return $send; + } + return $send; } sub send { - my ($self, $message) = @_; - my $logger = Bugzilla->push_ext->logger; - my $config = $self->config; - - # should_send intiailises bugzilla_user; make sure we return a useful error message - if (!$self->{bugzilla_user}) { - return (PUSH_RESULT_TRANSIENT, "Invalid bugzilla-user (" . $self->config->{bugzilla_user} . ")"); - } - - # load the bug - my $data = $message->payload_decoded; - my $bug_data = $self->_get_bug_data($data); - my $bug = Bugzilla::Bug->new($bug_data->{id}); - - if ($message->routing_key eq 'bug.create') { - # inject the comment into the data for new bugs - my $comment = shift @{ $bug->comments }; - if ($comment->body ne '') { - $bug_data->{comment} = Bugzilla::Extension::Push::Serialise->instance->object_to_hash($comment, 1); - } - - } elsif ($message->routing_key eq 'attachment.create') { - # inject the attachment payload - my $attachment = Bugzilla::Attachment->new($data->{attachment}->{id}); - $data->{attachment}->{data} = encode_base64($attachment->data); - } - - # map bmo login to ldap login and insert into json payload - $self->_add_ldap_logins($data, {}); - - # flatten json data - $self->_flatten($data); - - # add sysparm_action - $data->{sysparm_action} = 'insert'; - - if ($logger->debugging) { - $logger->debug(to_json(ref($data) ? $data : from_json($data), 1)); - } - - # send to service-now - my $request = HTTP::Request->new(POST => $self->config->{service_now_url}); - $request->content_type('application/json'); - $request->content(to_json($data)); - $request->authorization_basic($self->config->{service_now_user}, $self->config->{service_now_pass}); - - $self->{lwp} ||= LWP::UserAgent->new(agent => Bugzilla->localconfig->{urlbase}); - my $result = $self->{lwp}->request($request); - - # http level errors - if (!$result->is_success) { - # treat these as transient - return (PUSH_RESULT_TRANSIENT, $result->status_line); - } - - # empty response - if (length($result->content) == 0) { - # malformed request, treat as transient to allow code to fix - # may also be misconfiguration on servicenow, also transient - return (PUSH_RESULT_TRANSIENT, "Empty response"); + my ($self, $message) = @_; + my $logger = Bugzilla->push_ext->logger; + my $config = $self->config; + +# should_send intiailises bugzilla_user; make sure we return a useful error message + if (!$self->{bugzilla_user}) { + return (PUSH_RESULT_TRANSIENT, + "Invalid bugzilla-user (" . $self->config->{bugzilla_user} . ")"); + } + + # load the bug + my $data = $message->payload_decoded; + my $bug_data = $self->_get_bug_data($data); + my $bug = Bugzilla::Bug->new($bug_data->{id}); + + if ($message->routing_key eq 'bug.create') { + + # inject the comment into the data for new bugs + my $comment = shift @{$bug->comments}; + if ($comment->body ne '') { + $bug_data->{comment} + = Bugzilla::Extension::Push::Serialise->instance->object_to_hash($comment, 1); } - # json errors - my $result_data; - eval { - $result_data = from_json($result->content); - }; - if ($@) { - return (PUSH_RESULT_TRANSIENT, clean_error($@)); - } - if ($logger->debugging) { - $logger->debug(to_json($result_data, 1)); - } - if (exists $result_data->{error}) { - return (PUSH_RESULT_ERROR, $result_data->{error}); - }; - - # malformed/unexpected json response - if (!exists $result_data->{records} - || ref($result_data->{records}) ne 'ARRAY' - || scalar(@{$result_data->{records}}) == 0 - ) { - return (PUSH_RESULT_ERROR, "Malformed JSON response from ServiceNow: missing or empty 'records' array"); - } - - my $record = $result_data->{records}->[0]; - if (ref($record) ne 'HASH') { - return (PUSH_RESULT_ERROR, "Malformed JSON response from ServiceNow: 'records' array does not contain an object"); - } + } + elsif ($message->routing_key eq 'attachment.create') { + + # inject the attachment payload + my $attachment = Bugzilla::Attachment->new($data->{attachment}->{id}); + $data->{attachment}->{data} = encode_base64($attachment->data); + } + + # map bmo login to ldap login and insert into json payload + $self->_add_ldap_logins($data, {}); + + # flatten json data + $self->_flatten($data); + + # add sysparm_action + $data->{sysparm_action} = 'insert'; + + if ($logger->debugging) { + $logger->debug(to_json(ref($data) ? $data : from_json($data), 1)); + } + + # send to service-now + my $request = HTTP::Request->new(POST => $self->config->{service_now_url}); + $request->content_type('application/json'); + $request->content(to_json($data)); + $request->authorization_basic($self->config->{service_now_user}, + $self->config->{service_now_pass}); + + $self->{lwp} ||= LWP::UserAgent->new(agent => Bugzilla->localconfig->{urlbase}); + my $result = $self->{lwp}->request($request); + + # http level errors + if (!$result->is_success) { + + # treat these as transient + return (PUSH_RESULT_TRANSIENT, $result->status_line); + } + + # empty response + if (length($result->content) == 0) { + + # malformed request, treat as transient to allow code to fix + # may also be misconfiguration on servicenow, also transient + return (PUSH_RESULT_TRANSIENT, "Empty response"); + } + + # json errors + my $result_data; + eval { $result_data = from_json($result->content); }; + if ($@) { + return (PUSH_RESULT_TRANSIENT, clean_error($@)); + } + if ($logger->debugging) { + $logger->debug(to_json($result_data, 1)); + } + if (exists $result_data->{error}) { + return (PUSH_RESULT_ERROR, $result_data->{error}); + } + + # malformed/unexpected json response + if (!exists $result_data->{records} + || ref($result_data->{records}) ne 'ARRAY' + || scalar(@{$result_data->{records}}) == 0) + { + return (PUSH_RESULT_ERROR, + "Malformed JSON response from ServiceNow: missing or empty 'records' array"); + } + + my $record = $result_data->{records}->[0]; + if (ref($record) ne 'HASH') { + return (PUSH_RESULT_ERROR, + "Malformed JSON response from ServiceNow: 'records' array does not contain an object" + ); + } - # sys_id is the unique identifier for this action - if (!exists $record->{sys_id} || $record->{sys_id} eq '') { - return (PUSH_RESULT_ERROR, "Malformed JSON response from ServiceNow: 'records object' does not contain a valid sys_id"); - } + # sys_id is the unique identifier for this action + if (!exists $record->{sys_id} || $record->{sys_id} eq '') { + return (PUSH_RESULT_ERROR, + "Malformed JSON response from ServiceNow: 'records object' does not contain a valid sys_id" + ); + } - # success - return (PUSH_RESULT_OK, "sys_id: " . $record->{sys_id}); + # success + return (PUSH_RESULT_OK, "sys_id: " . $record->{sys_id}); } sub _get_bug_data { - my ($self, $data) = @_; - my $target = $data->{event}->{target}; - if ($target eq 'bug') { - return $data->{bug}; - } elsif (exists $data->{$target}->{bug}) { - return $data->{$target}->{bug}; - } else { - return; - } + my ($self, $data) = @_; + my $target = $data->{event}->{target}; + if ($target eq 'bug') { + return $data->{bug}; + } + elsif (exists $data->{$target}->{bug}) { + return $data->{$target}->{bug}; + } + else { + return; + } } sub _flatten { - # service-now expects a flat json object - my ($self, $data) = @_; - my $target = $data->{event}->{target}; + # service-now expects a flat json object + my ($self, $data) = @_; - # delete unnecessary deep objects - if ($target eq 'comment' || $target eq 'attachment') { - $data->{$target}->{bug_id} = $data->{$target}->{bug}->{id}; - delete $data->{$target}->{bug}; - } - delete $data->{event}->{changes}; + my $target = $data->{event}->{target}; - $self->_flatten_hash($data, $data, 'u'); + # delete unnecessary deep objects + if ($target eq 'comment' || $target eq 'attachment') { + $data->{$target}->{bug_id} = $data->{$target}->{bug}->{id}; + delete $data->{$target}->{bug}; + } + delete $data->{event}->{changes}; + + $self->_flatten_hash($data, $data, 'u'); } sub _flatten_hash { - my ($self, $base_hash, $hash, $prefix) = @_; - foreach my $key (keys %$hash) { - if (ref($hash->{$key}) eq 'HASH') { - $self->_flatten_hash($base_hash, $hash->{$key}, $prefix . "_$key"); - } elsif (ref($hash->{$key}) ne 'ARRAY') { - $base_hash->{$prefix . "_$key"} = $hash->{$key}; - } - delete $hash->{$key}; + my ($self, $base_hash, $hash, $prefix) = @_; + foreach my $key (keys %$hash) { + if (ref($hash->{$key}) eq 'HASH') { + $self->_flatten_hash($base_hash, $hash->{$key}, $prefix . "_$key"); + } + elsif (ref($hash->{$key}) ne 'ARRAY') { + $base_hash->{$prefix . "_$key"} = $hash->{$key}; } + delete $hash->{$key}; + } } sub _add_ldap_logins { - my ($self, $rh, $cache) = @_; - if (exists $rh->{login}) { - my $login = $rh->{login}; - $cache->{$login} ||= $self->_bmo_to_ldap($login); - Bugzilla->push_ext->logger->debug("BMO($login) --> LDAP(" . $cache->{$login} . ")"); - $rh->{ldap} = $cache->{$login}; - } - foreach my $key (keys %$rh) { - next unless ref($rh->{$key}) eq 'HASH'; - $self->_add_ldap_logins($rh->{$key}, $cache); - } + my ($self, $rh, $cache) = @_; + if (exists $rh->{login}) { + my $login = $rh->{login}; + $cache->{$login} ||= $self->_bmo_to_ldap($login); + Bugzilla->push_ext->logger->debug( + "BMO($login) --> LDAP(" . $cache->{$login} . ")"); + $rh->{ldap} = $cache->{$login}; + } + foreach my $key (keys %$rh) { + next unless ref($rh->{$key}) eq 'HASH'; + $self->_add_ldap_logins($rh->{$key}, $cache); + } } sub _bmo_to_ldap { - my ($self, $login) = @_; - my $ldap = $self->_ldap_cache(); + my ($self, $login) = @_; + my $ldap = $self->_ldap_cache(); - return '' unless $login =~ /\@mozilla\.(?:com|org)$/; + return '' unless $login =~ /\@mozilla\.(?:com|org)$/; - foreach my $check ($login, canon_email($login)) { - # check for matching bugmail entry - foreach my $mail (keys %$ldap) { - next unless $ldap->{$mail}{bugmail_canon} eq $check; - return $mail; - } + foreach my $check ($login, canon_email($login)) { - # check for matching mail - if (exists $ldap->{$check}) { - return $check; - } + # check for matching bugmail entry + foreach my $mail (keys %$ldap) { + next unless $ldap->{$mail}{bugmail_canon} eq $check; + return $mail; + } - # check for matching email alias - foreach my $mail (sort keys %$ldap) { - next unless grep { $check eq $_ } @{$ldap->{$mail}{aliases}}; - return $mail; - } + # check for matching mail + if (exists $ldap->{$check}) { + return $check; + } + + # check for matching email alias + foreach my $mail (sort keys %$ldap) { + next unless grep { $check eq $_ } @{$ldap->{$mail}{aliases}}; + return $mail; } + } - return ''; + return ''; } sub _ldap_cache { - my ($self) = @_; - my $logger = Bugzilla->push_ext->logger; - my $config = $self->config; - - # cache of all ldap entries; updated infrequently - if (!$self->{ldap_cache_time} || (time) - $self->{ldap_cache_time} > $config->{ldap_poll} * 60) { - $logger->debug('refreshing LDAP cache'); - - my $cache = {}; - - my $host = $config->{ldap_host}; - trick_taint($host); - my $scheme = lc($config->{ldap_scheme}); - my $ldap = Net::LDAP->new($host, scheme => $scheme, onerror => 'die') - or die $!; - $ldap->bind($config->{ldap_user}, password => $config->{ldap_pass}); - foreach my $ldap_base ('o=com,dc=mozilla', 'o=org,dc=mozilla') { - my $result = $ldap->search( - base => $ldap_base, - scope => 'sub', - filter => '(mail=*)', - attrs => ['mail', 'bugzillaEmail', 'emailAlias', 'cn', 'employeeType'], - ); - foreach my $entry ($result->entries) { - my ($name, $bugMail, $mail, $type) = - map { $entry->get_value($_) || '' } - qw(cn bugzillaEmail mail employeeType); - next if $type eq 'DISABLED'; - $mail = lc $mail; - $bugMail = '' if $bugMail !~ /\@/; - $bugMail = trim($bugMail); - if ($bugMail =~ / /) { - $bugMail = (grep { /\@/ } split / /, $bugMail)[0]; - } - $name =~ s/\s+/ /g; - $cache->{$mail}{name} = trim($name); - $cache->{$mail}{bugmail} = $bugMail; - $cache->{$mail}{bugmail_canon} = canon_email($bugMail); - $cache->{$mail}{aliases} = []; - foreach my $alias ( - @{$entry->get_value('emailAlias', asref => 1) || []} - ) { - push @{$cache->{$mail}{aliases}}, canon_email($alias); - } - } - } + my ($self) = @_; + my $logger = Bugzilla->push_ext->logger; + my $config = $self->config; + + # cache of all ldap entries; updated infrequently + if (!$self->{ldap_cache_time} + || (time) - $self->{ldap_cache_time} > $config->{ldap_poll} * 60) + { + $logger->debug('refreshing LDAP cache'); - $self->{ldap_cache} = $cache; - $self->{ldap_cache_time} = (time); + my $cache = {}; + + my $host = $config->{ldap_host}; + trick_taint($host); + my $scheme = lc($config->{ldap_scheme}); + my $ldap = Net::LDAP->new($host, scheme => $scheme, onerror => 'die') or die $!; + $ldap->bind($config->{ldap_user}, password => $config->{ldap_pass}); + foreach my $ldap_base ('o=com,dc=mozilla', 'o=org,dc=mozilla') { + my $result = $ldap->search( + base => $ldap_base, + scope => 'sub', + filter => '(mail=*)', + attrs => ['mail', 'bugzillaEmail', 'emailAlias', 'cn', 'employeeType'], + ); + foreach my $entry ($result->entries) { + my ($name, $bugMail, $mail, $type) + = map { $entry->get_value($_) || '' } qw(cn bugzillaEmail mail employeeType); + next if $type eq 'DISABLED'; + $mail = lc $mail; + $bugMail = '' if $bugMail !~ /\@/; + $bugMail = trim($bugMail); + if ($bugMail =~ / /) { + $bugMail = (grep {/\@/} split / /, $bugMail)[0]; + } + $name =~ s/\s+/ /g; + $cache->{$mail}{name} = trim($name); + $cache->{$mail}{bugmail} = $bugMail; + $cache->{$mail}{bugmail_canon} = canon_email($bugMail); + $cache->{$mail}{aliases} = []; + foreach my $alias (@{$entry->get_value('emailAlias', asref => 1) || []}) { + push @{$cache->{$mail}{aliases}}, canon_email($alias); + } + } } - return $self->{ldap_cache}; + $self->{ldap_cache} = $cache; + $self->{ldap_cache_time} = (time); + } + + return $self->{ldap_cache}; } 1; |