diff options
author | Matthew Somerville <matthew@mysociety.org> | 2020-06-24 22:44:37 +0100 |
---|---|---|
committer | M Somerville <matthew-github@dracos.co.uk> | 2020-11-11 10:29:20 +0000 |
commit | 26ca5c069d168eded92b95abd3be0d7b7349b430 (patch) | |
tree | 538f93cc1e6d52343a07def933afc9f638b8a7ed /perllib | |
parent | cabc4f91d55b952ab2521ec85ec745de4c354d8c (diff) |
[Bromley] Push notification from Echo.
Make sure a 200 response is always sent for a valid notification.
Diffstat (limited to 'perllib')
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Open311/Updates.pm | 18 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Waste.pm | 107 |
2 files changed, 122 insertions, 3 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Open311/Updates.pm b/perllib/FixMyStreet/App/Controller/Open311/Updates.pm index d5754bab7..8881a1b87 100644 --- a/perllib/FixMyStreet/App/Controller/Open311/Updates.pm +++ b/perllib/FixMyStreet/App/Controller/Open311/Updates.pm @@ -43,6 +43,12 @@ sub receive : Regex('^open311/v2/servicerequestupdates.(xml|json)$') : Args(0) { $request->{$_} = $c->get_param($_) || $c->detach('bad_request', [ $_ ]); } + $c->forward('process_update', [ $body, $request ]); +} + +sub process_update : Private { + my ($self, $c, $body, $request) = @_; + my $updates = Open311::GetServiceRequestUpdates->new( system_user => $body->comment_user, current_body => $body, @@ -51,16 +57,22 @@ sub receive : Regex('^open311/v2/servicerequestupdates.(xml|json)$') : Args(0) { my $p = $updates->find_problem($request); $c->detach('bad_request', [ 'not found' ]) unless $p; - my $comment = $p->comments->search( { external_id => $request->{update_id} } )->first; - $c->detach('bad_request', [ 'already exists' ]) if $comment; + $c->forward('check_existing', [ $p, $request, $updates ]); - $comment = $updates->process_update($request, $p); + my $comment = $updates->process_update($request, $p); my $data = { service_request_updates => { update_id => $comment->id } }; $c->forward('/open311/format_output', [ $data ]); } +sub check_existing : Private { + my ($self, $c, $p, $request, $updates) = @_; + + my $comment = $p->comments->search( { external_id => $request->{update_id} } )->first; + $c->detach('bad_request', [ 'already exists' ]) if $comment; +} + sub bad_request : Private { my ($self, $c, $comment) = @_; $c->response->status(400); diff --git a/perllib/FixMyStreet/App/Controller/Waste.pm b/perllib/FixMyStreet/App/Controller/Waste.pm index 7587795c8..152c7c28e 100644 --- a/perllib/FixMyStreet/App/Controller/Waste.pm +++ b/perllib/FixMyStreet/App/Controller/Waste.pm @@ -11,6 +11,7 @@ use FixMyStreet::App::Form::Waste::AboutYou; use FixMyStreet::App::Form::Waste::Request; use FixMyStreet::App::Form::Waste::Report; use FixMyStreet::App::Form::Waste::Enquiry; +use Open311::GetServiceRequestUpdates; sub auto : Private { my ( $self, $c ) = @_; @@ -457,6 +458,112 @@ sub setup_categories_and_bodies : Private { @$contacts = grep { grep { $_ eq 'Waste' } @{$_->groups} } @$contacts; } +sub receive_echo_event_notification : Path('/waste/echo') : Args(0) { + my ($self, $c) = @_; + $c->stash->{format} = 'xml'; + $c->response->header(Content_Type => 'application/soap+xml'); + + require SOAP::Lite; + + $c->detach('soap_error', [ 'Invalid method', 405 ]) unless $c->req->method eq 'POST'; + + my $echo = $c->cobrand->feature('echo'); + $c->detach('soap_error', [ 'Missing config', 500 ]) unless $echo; + + # Make sure we log entire request for debugging + $c->detach('soap_error', [ 'Missing body' ]) unless $c->req->body; + my $soap = join('', $c->req->body->getlines); + $c->log->info($soap); + + my $body = $c->cobrand->body; + $c->detach('soap_error', [ 'Bad jurisdiction' ]) unless $body; + + my $env = SOAP::Deserializer->deserialize($soap); + + my $header = $env->header; + $c->detach('soap_error', [ 'Missing SOAP header' ]) unless $header; + my $action = $header->{Action}; + $c->detach('soap_error', [ 'Incorrect Action' ]) unless $action && $action eq $echo->{receive_action}; + $header = $header->{Security}; + $c->detach('soap_error', [ 'Missing Security header' ]) unless $header; + my $token = $header->{UsernameToken}; + $c->detach('soap_error', [ 'Authentication failed' ]) + unless $token && $token->{Username} eq $echo->{receive_username} && $token->{Password} eq $echo->{receive_password}; + + my $event = $env->result; + + my $cfg = { echo => Integrations::Echo->new(%$echo) }; + my $request = $c->cobrand->construct_waste_open311_update($cfg, $event); + $request->{updated_datetime} = DateTime::Format::W3CDTF->format_datetime(DateTime->now); + $request->{service_request_id} = $event->{Guid}; + + my $updates = Open311::GetServiceRequestUpdates->new( + system_user => $body->comment_user, + current_body => $body, + ); + + my $p = $updates->find_problem($request); + if ($p) { + $c->forward('check_existing_update', [ $p, $request, $updates ]); + my $comment = $updates->process_update($request, $p); + } + # Still want to say it is okay, even if we did nothing with it + $c->forward('soap_ok'); +} + +sub soap_error : Private { + my ($self, $c, $comment, $code) = @_; + $code ||= 400; + $c->response->status($code); + my $type = $code == 500 ? 'Server' : 'Client'; + $c->response->body(SOAP::Serializer->fault($type, "Bad request: $comment", soap_header())); +} + +sub soap_ok : Private { + my ($self, $c) = @_; + $c->response->status(200); + my $method = SOAP::Data->name("NotifyEventUpdatedResponse")->attr({ + xmlns => "http://www.twistedfish.com/xmlns/echo/api/v1" + }); + $c->response->body(SOAP::Serializer->envelope(method => $method, soap_header())); +} + +sub soap_header { + my $attr = "http://www.twistedfish.com/xmlns/echo/api/v1"; + my $action = "NotifyEventUpdatedResponse"; + my $header = SOAP::Header->name("Action")->attr({ + xmlns => 'http://www.w3.org/2005/08/addressing', + 'soap:mustUnderstand' => 1, + })->value("$attr/ReceiverService/$action"); + + my $dt = DateTime->now(); + my $dt2 = $dt->clone->add(minutes => 5); + my $w3c = DateTime::Format::W3CDTF->new; + my $header2 = SOAP::Header->name("Security")->attr({ + 'soap:mustUnderstand' => 'true', + 'xmlns' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' + })->value( + \SOAP::Header->name( + "Timestamp" => \SOAP::Header->value( + SOAP::Header->name('Created', $w3c->format_datetime($dt)), + SOAP::Header->name('Expires', $w3c->format_datetime($dt2)), + ) + )->attr({ + xmlns => "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", + }) + ); + return ($header, $header2); +} + +sub check_existing_update : Private { + my ($self, $c, $p, $request, $updates) = @_; + + my $cfg = { updates => $updates }; + $c->detach('soap_ok') + unless $c->cobrand->waste_check_last_update( + $cfg, $p, $request->{status}, $request->{external_status_code}); +} + __PACKAGE__->meta->make_immutable; 1; |