package NGCP::Panel::Controller::SOAP::Intercept; use NGCP::Panel::Utils::Generic qw(:all); use Sipwise::Base; use parent 'Catalyst::Controller'; use File::Slurp; use SOAP::Transport::LOCAL; sub thewsdl : GET Path('/SOAP/Intercept.wsdl') :Local :Args() { my ($self, $c, $args) = @_; my $thewsdl = read_file('/etc/ngcp-panel/Intercept.wsdl'); $c->response->body($thewsdl); $c->response->content_type('text/xml'); } sub intercept_index : POST Path('/SOAP/Intercept') { my ($self, $c) = @_; my $h = Sipwise::SOAP::Intercept->new(c => $c); my $server = SOAP::Transport::LOCAL::Client->new; $server->serializer->register_ns('http://dev.sipwise.com/SOAP/Provisioning/Types', 'typens'); my $out = $server ->dispatch_with({ 'urn:/SOAP/Intercept' => $h }) ->handle($c->req->body); $c->response->content_type('text/xml'); $c->response->body($out); } package Sipwise::SOAP::Intercept; use Sipwise::Base; use NGCP::Panel::Form; use Data::Structure::Util qw/unbless/; use NGCP::Panel::Utils::SOAP qw/typed/; use NGCP::Panel::Utils::Interception qw(); use UUID; use Moose; use NGCP::Panel::Utils::Auth; use Crypt::Eksblowfish::Bcrypt qw/bcrypt_hash en_base64 de_base64/; has 'c' => (is => 'rw', isa => 'Object'); sub _validate { my ($self, $form, $data) = @_; unbless($data); $form->process(params => $data); unless($form->validated) { if($form->has_form_errors) { my @errs = $form->form_errors; die SOAP::Fault ->faultcode('Client.Syntax.MissingParameter') ->faultstring(shift @errs); } else { my @fields = $form->error_fields; my $field = shift @fields; my @errs = $field->errors; my $err = shift @errs; die SOAP::Fault ->faultcode('Client.Syntax.MalformedParameter') ->faultstring($field->label . ": " . join('; ', @{ $err })); } } } sub _auth { my ($self, $auth) = @_; my $c = $self->c; $self->_validate(NGCP::Panel::Form::get("NGCP::Panel::Form::Intercept::Authentication", $c), $auth); try { # check for general availability of user first, we need it in # both md5 and bcrypt cases my $admin = $c->model('DB')->resultset('admins')->search({ login => $auth->{username}, is_active => 1, })->first; die unless($admin && ($admin->is_superuser || $admin->lawful_intercept)); if(defined $admin->saltedpass) { my ($db_b64salt, $db_b64hash) = split /\$/, $admin->saltedpass; my $salt = de_base64($db_b64salt); my $usr_b64hash = en_base64(bcrypt_hash({ key_nul => 1, cost => NGCP::Panel::Utils::Auth::get_bcrypt_cost(), salt => $salt, }, $auth->{password})); die unless($usr_b64hash eq $db_b64hash); die unless($admin->is_superuser || $admin->lawful_intercept); } else { my $md5admin = $c->model('DB')->resultset('admins')->search({ login => $auth->{username}, is_active => 1, md5pass => { '=' => \['MD5("'.$auth->{password}.'")'] }, })->first; die unless($md5admin && ($md5admin->is_superuser || $md5admin->lawful_intercept)); # migrate password to bcrypt $admin->update({ md5pass => undef, saltedpass => NGCP::Panel::Utils::Auth::generate_salted_hash($auth->{password}), }); } } catch($e) { die SOAP::Fault ->faultcode('Client.Auth.Refused') ->faultstring("admin may not access LI data (wrong credentials or lawful_intercept flag not set)"); } } sub create_interception { my ($self, $auth, $params) = @_; my $c = $self->c; $self->_auth($auth); $self->_validate(NGCP::Panel::Form::get("NGCP::Panel::Form::Intercept::Create", $c), $params); my ($sub, $reseller, $voip_number, $sip_username, $sip_domain); try { ($sub, $reseller, $voip_number) = NGCP::Panel::Utils::Interception::subresnum_from_number($c, $params->{number}, sub { my ($msg,$field,$response) = @_; die SOAP::Fault ->faultcode('Client.Voip.NoSuchSubscriber') ->faultstring($msg); return 0; }); $sip_username = NGCP::Panel::Utils::Interception::username_to_regexp_pattern($c,$voip_number,$sub->username); $sip_domain = $sub->domain->domain; } catch($e) { die $e if 'SOAP::Fault' eq ref $e; die SOAP::Fault ->faultcode('Server.Internal') ->faultstring($e); } my ($uuid_bin, $uuid_string); UUID::generate($uuid_bin); UUID::unparse($uuid_bin, $uuid_string); my $guard = $c->model('DB')->txn_scope_guard; my $i; { try { $i = $self->c->model('InterceptDB')->resultset('voip_intercept')->create({ reseller_id => $reseller->id, LIID => $params->{LIID}, number => $params->{number}, cc_required => $params->{cc_required}, delivery_host => $params->{iri_delivery}->{host}, delivery_port => $params->{iri_delivery}->{port}, delivery_user => $params->{iri_delivery}->{user}, delivery_pass => $params->{iri_delivery}->{pass}, deleted => 0, uuid => $uuid_string, sip_username => $sip_username, sip_domain => $sip_domain, create_timestamp => \['NOW()'], $params->{cc_required} ? (cc_delivery_host => $params->{cc_delivery}->{host}) : (), $params->{cc_required} ? (cc_delivery_port => $params->{cc_delivery}->{port}) : (), }); $guard->commit; NGCP::Panel::Utils::Interception::request($c, 'POST', undef, { liid => $i->LIID, uuid => $i->uuid, number => $i->number, sip_username => $sip_username, sip_domain => $sip_domain, delivery_host => $i->delivery_host, delivery_port => $i->delivery_port, delivery_user => $i->delivery_user, delivery_password => $i->delivery_pass, cc_required => $i->cc_required, cc_delivery_host => $i->cc_delivery_host, cc_delivery_port => $i->cc_delivery_port, }); } catch($e) { die SOAP::Fault ->faultcode('Server.Internal') ->faultstring($e); } } return typed($c, $i->id); } sub update_interception { my ($self, $auth, $params) = @_; my $c = $self->c; $self->_auth($auth); $self->_validate(NGCP::Panel::Form::get("NGCP::Panel::Form::Intercept::Update", $c), $params); my $i; try { $i = $c->model('DB')->resultset('voip_intercept')->search({ id => $params->{id}, deleted => 0, })->first; } catch($e) { die SOAP::Fault ->faultcode('Server.Internal') ->faultstring($e); } unless($i) { die SOAP::Fault ->faultcode('Client.Intercept.NoSuchInterception') ->faultstring("interception ID '$$params{id}' does not exist"); } my $guard = $c->model('DB')->txn_scope_guard; { if($params->{data}->{iri_delivery}) { $i->delivery_host($params->{data}->{iri_delivery}->{host}); $i->delivery_port($params->{data}->{iri_delivery}->{port}); $i->delivery_user($params->{data}->{iri_delivery}->{username}); $i->delivery_pass($params->{data}->{iri_delivery}->{password}); } if($params->{data}->{cc_delivery}) { $i->cc_delivery_host($params->{data}->{cc_delivery}->{host}); $i->cc_delivery_port($params->{data}->{cc_delivery}->{port}); } $i->cc_required($params->{data}->{cc_required}); # not applying changed number/domain? try { $i->update(); $guard->commit; NGCP::Panel::Utils::Interception::request($c, 'PUT', $i->uuid, { liid => $i->LIID, uuid => $i->uuid, number => $i->number, sip_username => $i->sip_username, sip_domain => $i->sip_domain, delivery_host => $i->delivery_host, delivery_port => $i->delivery_port, delivery_user => $i->delivery_user, delivery_password => $i->delivery_pass, cc_required => $i->cc_required, cc_delivery_host => $i->cc_delivery_host, cc_delivery_port => $i->cc_delivery_port, }); } catch($e) { die SOAP::Fault ->faultcode('Server.Internal') ->faultstring($e); } } return; } sub delete_interception { my ($self, $auth, $params) = @_; my $c = $self->c; $self->_auth($auth); $self->_validate(NGCP::Panel::Form::get("NGCP::Panel::Form::Intercept::Delete", $c), $params); my $i; try { $i = $c->model('DB')->resultset('voip_intercept')->search({ id => $params->{id}, deleted => 0, })->first; } catch($e) { die SOAP::Fault ->faultcode('Server.Internal') ->faultstring($e); } unless($i) { die SOAP::Fault ->faultcode('Client.Intercept.NoSuchInterception') ->faultstring("interception ID '$$params{id}' does not exist"); } my $guard = $c->model('DB')->txn_scope_guard; { try { my $uuid = $i->uuid; $i->update({ deleted => 1, reseller_id => undef, LIID => undef, number => undef, cc_required => 0, delivery_host => undef, delivery_port => undef, delivery_user => undef, delivery_pass => undef, cc_delivery_host => undef, cc_delivery_port => undef, sip_username => undef, sip_domain => undef, uuid => undef, }); $guard->commit; NGCP::Panel::Utils::Interception::request($c, 'DELETE', $uuid); } catch($e) { die SOAP::Fault ->faultcode('Server.Internal') ->faultstring($e); } } return; } sub get_interception_by_id { my ($self, $auth, $params) = @_; my $c = $self->c; $self->_auth($auth); my $i; try { $i = $c->model('DB')->resultset('voip_intercept')->search({ id => $params->{id}, deleted => 0, })->first; } catch($e) { die SOAP::Fault ->faultcode('Server.Internal') ->faultstring($e); } unless($i) { die SOAP::Fault ->faultcode('Client.Intercept.NoSuchInterception') ->faultstring("interception ID '$$params{id}' does not exist"); } return typed($c, { id => $i->id, LIID => $i->LIID, number => $i->number, cc_required => $i->cc_required, iri_delivery => { host => $i->delivery_host, port => $i->delivery_port, username => $i->delivery_user, password => $i->delivery_pass, }, cc_delivery => { host => $i->cc_delivery_host, port => $i->cc_delivery_port, } }); } sub get_interceptions_by_liid { my ($self, $auth, $params) = @_; my $c = $self->c; $self->_auth($auth); my @interceptions = (); try { my $rs = $c->model('DB')->resultset('voip_intercept')->search({ LIID => $params->{LIID}, deleted => 0, }); while(my $i = $rs->next) { push @interceptions, { id => $i->id, LIID => $i->LIID, number => $i->number, cc_required => $i->cc_required, iri_delivery => { host => $i->delivery_host, port => $i->delivery_port, username => $i->delivery_user, password => $i->delivery_pass, }, cc_delivery => { host => $i->cc_delivery_host, port => $i->cc_delivery_port, } }; } } catch($e) { die SOAP::Fault ->faultcode('Server.Internal') ->faultstring($e); } return typed($c, \@interceptions); } sub get_interceptions_by_number { my ($self, $auth, $params) = @_; my $c = $self->c; $self->_auth($auth); my @interceptions = (); try { my $rs = $c->model('DB')->resultset('voip_intercept')->search({ number => $params->{number}, deleted => 0, }); while(my $i = $rs->next) { push @interceptions, { id => $i->id, LIID => $i->LIID, number => $i->number, cc_required => $i->cc_required, iri_delivery => { host => $i->delivery_host, port => $i->delivery_port, username => $i->delivery_user, password => $i->delivery_pass, }, cc_delivery => { host => $i->cc_delivery_host, port => $i->cc_delivery_port, } }; } } catch($e) { die SOAP::Fault ->faultcode('Server.Internal') ->faultstring($e); } return typed($c, \@interceptions); } sub get_interceptions { my ($self, $auth, $params) = @_; my $c = $self->c; $self->_auth($auth); my @interceptions = (); try { my $rs = $c->model('DB')->resultset('voip_intercept')->search({ deleted => 0, }); while(my $i = $rs->next) { push @interceptions, { id => $i->id, LIID => $i->LIID, number => $i->number, cc_required => $i->cc_required, iri_delivery => { host => $i->delivery_host, port => $i->delivery_port, username => $i->delivery_user, password => $i->delivery_pass, }, cc_delivery => { host => $i->cc_delivery_host, port => $i->cc_delivery_port, } }; } } catch($e) { die SOAP::Fault ->faultcode('Server.Internal') ->faultstring($e); } return typed($c, \@interceptions); } 1; # vim: set tabstop=4 expandtab: