TT#3648 Add banned ips and users API interface

Change-Id: Ifbaafdfaf2194f84729bfb91c79453fd186dc5c6
changes/19/8719/16
Irina Peshinskaya 10 years ago
parent ad62a1a5a1
commit ae5e887dc2

@ -0,0 +1,28 @@
package NGCP::Panel::Controller::API::BannedIps;
use Sipwise::Base;
use parent qw/NGCP::Panel::Role::Entities NGCP::Panel::Role::API::BannedIps/;
use NGCP::Panel::Utils::Peering;
use HTTP::Status qw(:constants);
use NGCP::Panel::Utils::Security;
__PACKAGE__->set_config();
sub allowed_methods {
return [qw/GET OPTIONS HEAD/];
}
sub api_description {
return 'Defines banned ips.';
}
sub get_list{
my ($self, $c) = @_;
return NGCP::Panel::Utils::Security::list_banned_ips($c);
}
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,26 @@
package NGCP::Panel::Controller::API::BannedIpsItem;
use parent qw/NGCP::Panel::Role::EntitiesItem NGCP::Panel::Role::API::BannedIps/;
use Sipwise::Base;
use HTTP::Status qw(:constants);
use NGCP::Panel::Utils::Security;
__PACKAGE__->set_config();
sub allowed_methods {
return [qw/GET OPTIONS HEAD DELETE/];
}
sub delete_item {
my($self, $c, $item, $old_resource, $resource, $form) = @_;
my $ip = $item;
NGCP::Panel::Utils::Security::ip_unban($c, $ip);
}
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,28 @@
package NGCP::Panel::Controller::API::BannedUsers;
use Sipwise::Base;
use parent qw/NGCP::Panel::Role::Entities NGCP::Panel::Role::API::BannedUsers/;
use NGCP::Panel::Utils::Peering;
use HTTP::Status qw(:constants);
use NGCP::Panel::Utils::Security;
__PACKAGE__->set_config();
sub allowed_methods {
return [qw/GET OPTIONS HEAD/];
}
sub api_description {
return 'Defines banned users.';
}
sub get_list{
my ($self, $c) = @_;
return NGCP::Panel::Utils::Security::list_banned_users($c, data_for_json => 1);
}
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,26 @@
package NGCP::Panel::Controller::API::BannedUsersItem;
use parent qw/NGCP::Panel::Role::EntitiesItem NGCP::Panel::Role::API::BannedUsers/;
use Sipwise::Base;
use HTTP::Status qw(:constants);
use NGCP::Panel::Utils::Security;
__PACKAGE__->set_config();
sub allowed_methods {
return [qw/GET OPTIONS HEAD DELETE/];
}
sub delete_item {
my($self, $c, $item, $old_resource, $resource, $form) = @_;
my $user = $item;
NGCP::Panel::Utils::Security::user_unban($c, $user);
}
1;
# vim: set tabstop=4 expandtab:

@ -4,10 +4,8 @@ use Sipwise::Base;
use parent 'Catalyst::Controller';
use XML::LibXML;
use URI::Encode;
use NGCP::Panel::Utils::Security;
use NGCP::Panel::Utils::Navigation;
use NGCP::Panel::Utils::XMLDispatcher;
use NGCP::Panel::Utils::DateTime;
sub auto :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) {
@ -23,74 +21,14 @@ sub root :PathPart('/') :CaptureArgs(0) {
sub index :Chained('/') :PathPart('security') :Args(0) {
my ( $self, $c ) = @_;
my $dispatcher = NGCP::Panel::Utils::XMLDispatcher->new;
my $xml_parser = XML::LibXML->new();
my $ip_xml = <<'EOF';
<?xml version="1.0" ?>
<methodCall>
<methodName>htable.dump</methodName>
<params>
<param><value><string>ipban</string></value></param>
</params>
</methodCall>
EOF
my $ip_res = $dispatcher->dispatch($c, "loadbalancer", 1, 1, $ip_xml);
my @ips = ();
for my $host (grep {$$_[1]} @$ip_res) {
my $xmlDoc = $xml_parser->parse_string($host->[2]);
@ips = map { { ip => $_->to_literal } }
$xmlDoc->findnodes('//member/value/string');
}
my $user_xml = <<'EOF';
<?xml version="1.0" ?>
<methodCall>
<methodName>htable.dump</methodName>
<params>
<param><value><string>auth</string></value></param>
</params>
</methodCall>
EOF
my $user_res = $dispatcher->dispatch($c, "loadbalancer", 1, 1, $user_xml);
my @users = ();
my $usr = {};
for my $host (grep {$$_[1]} @$user_res) {
my $xmlDoc = $xml_parser->parse_string($host->[2]);
my $username = '';
my $key = '';
foreach my $node ($xmlDoc->findnodes('//member')) {
my $name = $node->findvalue('./name');
my $value = $node->findvalue('./value/string') ||
$node->findvalue('./value/int');
if ($name eq 'name') {
$value =~ m/(?<user>.*)::(?<key>.*)/;
$username = $+{user};
$key = $+{key};
} elsif ($name eq 'value' && $username && $key) {
# there souldn't be any other keys
$key eq 'auth_count' and $usr->{$username}->{auth_count} = $value;
$key eq 'last_auth' and $usr->{$username}->{last_auth} = $value;
}
}
}
for my $key (keys %{ $usr }) {
push @users, {
username => $key,
auth_count => $usr->{$key}->{auth_count},
last_auth => NGCP::Panel::Utils::DateTime::epoch_local($usr->{$key}->{last_auth}),
} if($usr->{$key}->{auth_count} >= $c->config->{security}->{failed_auth_attempts});
}
my $ips = NGCP::Panel::Utils::Security::list_banned_ips($c);
my $users = NGCP::Panel::Utils::Security::list_banned_users($c);
$c->stash(
template => 'security/list.tt',
banned_ips => \@ips,
banned_users => \@users,
banned_ips => $ips,
banned_users => $users,
);
}
@ -102,21 +40,8 @@ sub ip_base :Chained('/') :PathPart('security/ip') :CaptureArgs(1) {
sub ip_unban :Chained('ip_base') :PathPart('unban') :Args(0) {
my ( $self, $c ) = @_;
my $dispatcher = NGCP::Panel::Utils::XMLDispatcher->new;
my $ip = $c->stash->{ip};
my $xml = <<"EOF";
<?xml version="1.0" ?>
<methodCall>
<methodName>htable.delete</methodName>
<params>
<param><value><string>ipban</string></value></param>
<param><value><string>$ip</string></value></param>
</params>
</methodCall>
EOF
$dispatcher->dispatch($c, "loadbalancer", 1, 1, $xml);
NGCP::Panel::Utils::Security::ip_unban($c, $ip);
NGCP::Panel::Utils::Message::info(
c => $c,
data => { ip => $ip },
@ -133,24 +58,8 @@ sub user_base :Chained('/') :PathPart('security/user') :CaptureArgs(1) {
sub user_unban :Chained('user_base') :PathPart('unban') :Args(0) {
my ( $self, $c ) = @_;
my $dispatcher = NGCP::Panel::Utils::XMLDispatcher->new;
my $user = $c->stash->{user};
my @keys = ($user.'::auth_count', $user.'::last_auth');
foreach my $key (@keys) {
my $xml = <<"EOF";
<?xml version="1.0" ?>
<methodCall>
<methodName>htable.delete</methodName>
<params>
<param><value><string>auth</string></value></param>
<param><value><string>$key</string></value></param>
</params>
</methodCall>
EOF
$dispatcher->dispatch($c, "loadbalancer", 1, 1, $xml);
}
NGCP::Panel::Utils::Security::ip_unban($c, $user);
NGCP::Panel::Utils::Message::info(
c => $c,
data => { user => $user },

@ -0,0 +1,38 @@
package NGCP::Panel::Role::API::BannedIps;
use Sipwise::Base;
use parent qw/NGCP::Panel::Role::API/;
use NGCP::Panel::Utils::Generic qw(:all);
use boolean qw(true);
use HTTP::Status qw(:constants);
use NGCP::Panel::Form::Peering::Group;
use NGCP::Panel::Utils::Peering;
sub item_name {
return 'bannedips';
}
sub resource_name{
return 'bannedips';
}
sub get_item_id{
my($self, $c, $item, $resource, $form) = @_;
return $item->{ip};
}
sub valid_id {
my ($self, $c, $id) = @_;
return 1 if $id=~/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?:\/\d{1,2})?$/;
$self->error($c, HTTP_BAD_REQUEST, "Invalid id in request URI. Should be an ip address.");
return;
}
sub item_by_id{
my ($self, $c, $id) = @_;
return $id;
}
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,41 @@
package NGCP::Panel::Role::API::BannedUsers;
use Sipwise::Base;
use parent qw/NGCP::Panel::Role::API/;
use NGCP::Panel::Utils::Generic qw(:all);
use boolean qw(true);
use HTTP::Status qw(:constants);
use NGCP::Panel::Form::Peering::Group;
use NGCP::Panel::Utils::Peering;
sub item_name {
return 'bannedusers';
}
sub resource_name{
return 'bannedusers';
}
sub get_item_id{
my($self, $c, $item, $resource, $form) = @_;
return $item->{username};
}
sub valid_id {
my ($self, $c, $id) = @_;
return 1 if $id=~/^[^@]+@[^@]+$/;
$self->error($c, HTTP_BAD_REQUEST, "Invalid id in request URI. Should be an ip address.");
return;
}
sub item_by_id{
my ($self, $c, $id) = @_;
return $id;
}
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,138 @@
package NGCP::Panel::Utils::Security;
use Sipwise::Base;
use XML::LibXML;
use URI::Encode;
use NGCP::Panel::Utils::XMLDispatcher;
use NGCP::Panel::Utils::DateTime;
sub list_banned_ips {
my ( $c ) = @_;
my $dispatcher = NGCP::Panel::Utils::XMLDispatcher->new;
my $xml_parser = XML::LibXML->new();
my $ip_xml = <<'EOF';
<?xml version="1.0" ?>
<methodCall>
<methodName>htable.dump</methodName>
<params>
<param><value><string>ipban</string></value></param>
</params>
</methodCall>
EOF
my $ip_res = $dispatcher->dispatch($c, "loadbalancer", 1, 1, $ip_xml);
my @ips = ();
for my $host (grep {$$_[1]} @$ip_res) {
my $xmlDoc = $xml_parser->parse_string($host->[2]);
foreach my $node ($xmlDoc->findnodes('//member')) {
my $name = $node->findvalue('./name');
my $value = $node->findvalue('./value/string');
if ($name eq 'name') {
push @ips, { ip => $value };
}
}
}
return \@ips;
}
sub list_banned_users {
my ( $c, %params ) = @_;
my $dispatcher = NGCP::Panel::Utils::XMLDispatcher->new;
my $xml_parser = XML::LibXML->new();
my $user_xml = <<'EOF';
<?xml version="1.0" ?>
<methodCall>
<methodName>htable.dump</methodName>
<params>
<param><value><string>auth</string></value></param>
</params>
</methodCall>
EOF
my $user_res = $dispatcher->dispatch($c, "loadbalancer", 1, 1, $user_xml);
my @users = ();
my $usr = {};
for my $host (grep {$$_[1]} @$user_res) {
my $xmlDoc = $xml_parser->parse_string($host->[2]);
my $username = '';
my $key = '';
foreach my $node ($xmlDoc->findnodes('//member')) {
my $name = $node->findvalue('./name');
my $value = $node->findvalue('./value/string') ||
$node->findvalue('./value/int');
if ($name eq 'name') {
$value =~ m/(?<user>.*)::(?<key>.*)/;
$username = $+{user};
$key = $+{key};
} elsif ($name eq 'value' && $username && $key) {
# there souldn't be any other keys
$key eq 'auth_count' and $usr->{$username}->{auth_count} = $value;
$key eq 'last_auth' and $usr->{$username}->{last_auth} = $value;
}
}
}
for my $key (keys %{ $usr }) {
my $last_auth = $usr->{$key}->{last_auth} ? NGCP::Panel::Utils::DateTime::epoch_local($usr->{$key}->{last_auth}) : undef;
if($last_auth && $params{data_for_json}){
$last_auth = $last_auth->ymd.' '. $last_auth->hms;
}
push @users, {
username => $key,
auth_count => $usr->{$key}->{auth_count},
last_auth => $last_auth,
} if($usr->{$key}->{auth_count} >= $c->config->{security}->{failed_auth_attempts});
}
return \@users;
}
sub ip_unban {
my ( $c, $ip ) = @_;
my $decoder = URI::Encode->new;
$ip = $decoder->decode($ip);
my $dispatcher = NGCP::Panel::Utils::XMLDispatcher->new;
my $xml = <<"EOF";
<?xml version="1.0" ?>
<methodCall>
<methodName>htable.delete</methodName>
<params>
<param><value><string>ipban</string></value></param>
<param><value><string>$ip</string></value></param>
</params>
</methodCall>
EOF
$dispatcher->dispatch($c, "loadbalancer", 1, 1, $xml);
}
sub user_unban {
my ( $c, $user ) = @_;
my $decoder = URI::Encode->new;
$user = $decoder->decode($user);
my $dispatcher = NGCP::Panel::Utils::XMLDispatcher->new;
my @keys = ($user.'::auth_count', $user.'::last_auth');
foreach my $key (@keys) {
my $xml = <<"EOF";
<?xml version="1.0" ?>
<methodCall>
<methodName>htable.delete</methodName>
<params>
<param><value><string>auth</string></value></param>
<param><value><string>$key</string></value></param>
</params>
</methodCall>
EOF
$dispatcher->dispatch($c, "loadbalancer", 1, 1, $xml);
}
}
1;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,51 @@
use strict;
use Test::Collection;
use Test::FakeData;
use Test::More;
use Data::Dumper;
#use NGCP::Panel::Utils::Subscriber;
my $test_machine = Test::Collection->new(
name => 'bannedips',
);
my $fake_data = Test::FakeData->new;
$test_machine->methods->{collection}->{allowed} = {map {$_ => 1} qw(GET HEAD OPTIONS)};
$test_machine->methods->{item}->{allowed} = {map {$_ => 1} qw(GET HEAD OPTIONS DELETE)};
$fake_data->set_data_from_script({
'bannedips' => {
'data' => {
},
},
});
$test_machine->DATA_ITEM_STORE($fake_data->process('bannedips'));
$test_machine->ALLOW_EMPTY_COLLECTION(1);
$test_machine->form_data_item();
my $time = time();
my $hal_before = $test_machine->get_item_hal(undef,undef,1);
my @ips = qw/127.0.0.1 127.0.0.2 127.0.0.3/;
foreach (@ips){
`ngcp-sercmd lb htable.sets ipban $_ 1`;
}
$test_machine->check_bundle();
if(!$test_machine->IS_EMPTY_COLLECTION){
$test_machine->clear_test_data_all([map {"/api/bannedips/$_"} @ips]);
}
if(!$hal_before || $hal_before->{content_collection}->{total_count} < 1){
my $hal_after = $test_machine->get_item_hal(undef,undef,1);
is($hal_after, undef, "Check that all added banned ips were deleted");
}
#fake data aren't registered in this test machine, so they will stay.
done_testing;
# vim: set tabstop=4 expandtab:

@ -0,0 +1,54 @@
use strict;
use Test::Collection;
use Test::FakeData;
use Test::More;
use Data::Dumper;
#use NGCP::Panel::Utils::Subscriber;
my $test_machine = Test::Collection->new(
name => 'bannedusers',
);
my $fake_data = Test::FakeData->new;
$test_machine->methods->{collection}->{allowed} = {map {$_ => 1} qw(GET HEAD OPTIONS)};
$test_machine->methods->{item}->{allowed} = {map {$_ => 1} qw(GET HEAD OPTIONS DELETE)};
$fake_data->set_data_from_script({
'bannedusers' => {
'data' => {
},
},
});
$test_machine->DATA_ITEM_STORE($fake_data->process('bannedusers'));
$test_machine->ALLOW_EMPTY_COLLECTION(1);
$test_machine->form_data_item();
my $hal_before = $test_machine->get_item_hal(undef,undef,1);
my $time = time();
my @users = qw/user1 user2 user3/;
foreach (@users){
my $cmd1 = "ngcp-sercmd lb htable.sets auth $_\@domain.com::auth_count 10";
my $cmd2 = "ngcp-sercmd lb htable.sets auth $_\@domain.com::last_auth $time";
print $cmd1."\n".$cmd2."\n";
`$cmd1`;
`$cmd2`;
}
$test_machine->check_bundle();
print "is_empty:".$test_machine->IS_EMPTY_COLLECTION.";";
if(!$test_machine->IS_EMPTY_COLLECTION){
$test_machine->clear_test_data_all([map {"/api/bannedusers/$_\@domain.com"} @users]);
}
if(!$hal_before || $hal_before->{content_collection}->{total_count} < 1){
my $hal_after = $test_machine->get_item_hal(undef,undef,1);
print Dumper $hal_after;
is($hal_after, undef, "Check that all added banned users were deleted");
}
done_testing;
# vim: set tabstop=4 expandtab:

@ -334,10 +334,10 @@ sub get_uri_item{
return $resuri;
}
sub get_item_hal{
my($self,$name,$uri) = @_;
my($self,$name,$uri, $reload) = @_;
$name ||= $self->name;
my $resitem ;
if(!$uri){
if(!$uri && !$reload){
if(( $name eq $self->name ) && $self->DATA_CREATED->{FIRST}){
$resitem = $self->get_created_first;
}
@ -346,14 +346,22 @@ sub get_item_hal{
}
}
if(!$resitem){
my ($reshal, $location,$total_count);
my ($reshal, $location,$total_count,$reshal_collection);
$uri //= $self->get_uri_collection($name)."?page=1&rows=1";
#print "uri=$uri;";
my($res,$list_collection,$req) = $self->check_item_get($self->normalize_uri($uri));
($reshal,$location,$total_count) = $self->get_hal_from_collection($list_collection,$name);
($reshal,$location,$total_count,$reshal_collection) = $self->get_hal_from_collection($list_collection,$name);
if($total_count || ('HASH' eq ref $reshal->{content} && $reshal->{content}->{total_count})){
$self->IS_EMPTY_COLLECTION(0);
$resitem = { num => 1, content => $reshal, res => $res, req => $req, location => $location, total_count => $total_count };
$resitem = {
num => 1,
content => $reshal,
res => $res,
req => $req,
location => $location,
total_count => $total_count,
content_collection => $reshal_collection,
};
$self->DATA_LOADED->{$name} ||= [];
push @{$self->DATA_LOADED->{$name}}, $resitem;
}else{
@ -366,7 +374,8 @@ sub get_hal_from_collection{
my($self,$list_collection,$name) = @_;
$name ||= $self->name;
my $hal_name = $self->get_hal_name($name);
my($reshal,$location,$total_count);
my($reshal,$reshal_collection,$location,$total_count);
$reshal_collection = $list_collection;
if( $list_collection->{_embedded} && ref $list_collection->{_embedded}->{$hal_name} eq 'ARRAY') {
$reshal = $list_collection->{_embedded}->{$hal_name}->[0];
$location = $reshal->{_links}->{self}->{href};
@ -379,14 +388,14 @@ sub get_hal_from_collection{
#found first subscriber
$reshal = $list_collection;
$location = $reshal->{_links}->{$hal_name}->{href};
$total_count = $reshal->{total_count};
$total_count = $reshal->{total_count};
} elsif( ref $list_collection eq 'HASH' && $list_collection->{_links}->{self}->{href}) {
#preferencedefs collection
$reshal = $list_collection;
$location = $reshal->{_links}->{self}->{href};
$total_count = $reshal->{total_count};
}
return ($reshal,$location,$total_count);
return ($reshal,$location,$total_count,$reshal_collection);
}
sub get_created_first{
my($self) = @_;

Loading…
Cancel
Save