diff --git a/lib/NGCP/Panel/Controller/API/BannedIps.pm b/lib/NGCP/Panel/Controller/API/BannedIps.pm
new file mode 100644
index 0000000000..a82924c3de
--- /dev/null
+++ b/lib/NGCP/Panel/Controller/API/BannedIps.pm
@@ -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:
diff --git a/lib/NGCP/Panel/Controller/API/BannedIpsItem.pm b/lib/NGCP/Panel/Controller/API/BannedIpsItem.pm
new file mode 100644
index 0000000000..ea09b64fe6
--- /dev/null
+++ b/lib/NGCP/Panel/Controller/API/BannedIpsItem.pm
@@ -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:
diff --git a/lib/NGCP/Panel/Controller/API/BannedUsers.pm b/lib/NGCP/Panel/Controller/API/BannedUsers.pm
new file mode 100644
index 0000000000..f416083bc6
--- /dev/null
+++ b/lib/NGCP/Panel/Controller/API/BannedUsers.pm
@@ -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:
diff --git a/lib/NGCP/Panel/Controller/API/BannedUsersItem.pm b/lib/NGCP/Panel/Controller/API/BannedUsersItem.pm
new file mode 100644
index 0000000000..c8174802ef
--- /dev/null
+++ b/lib/NGCP/Panel/Controller/API/BannedUsersItem.pm
@@ -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:
diff --git a/lib/NGCP/Panel/Controller/Security.pm b/lib/NGCP/Panel/Controller/Security.pm
index d7b3c79eb0..980ad2eb57 100644
--- a/lib/NGCP/Panel/Controller/Security.pm
+++ b/lib/NGCP/Panel/Controller/Security.pm
@@ -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';
-
-
- htable.dump
-
- ipban
-
-
-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';
-
-
- htable.dump
-
- auth
-
-
-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/(?.*)::(?.*)/;
- $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";
-
-
- htable.delete
-
- ipban
- $ip
-
-
-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";
-
-
- htable.delete
-
- auth
- $key
-
-
-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 },
diff --git a/lib/NGCP/Panel/Role/API/BannedIps.pm b/lib/NGCP/Panel/Role/API/BannedIps.pm
new file mode 100644
index 0000000000..8011c4bd07
--- /dev/null
+++ b/lib/NGCP/Panel/Role/API/BannedIps.pm
@@ -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:
diff --git a/lib/NGCP/Panel/Role/API/BannedUsers.pm b/lib/NGCP/Panel/Role/API/BannedUsers.pm
new file mode 100644
index 0000000000..5c4079ca7f
--- /dev/null
+++ b/lib/NGCP/Panel/Role/API/BannedUsers.pm
@@ -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:
diff --git a/lib/NGCP/Panel/Utils/Security.pm b/lib/NGCP/Panel/Utils/Security.pm
new file mode 100644
index 0000000000..b2645a5c7e
--- /dev/null
+++ b/lib/NGCP/Panel/Utils/Security.pm
@@ -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';
+
+
+ htable.dump
+
+ ipban
+
+
+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';
+
+
+ htable.dump
+
+ auth
+
+
+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/(?.*)::(?.*)/;
+ $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";
+
+
+ htable.delete
+
+ ipban
+ $ip
+
+
+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";
+
+
+ htable.delete
+
+ auth
+ $key
+
+
+EOF
+
+ $dispatcher->dispatch($c, "loadbalancer", 1, 1, $xml);
+ }
+}
+
+1;
+
+# vim: set tabstop=4 expandtab:
diff --git a/t/api-rest/api-bannedips.t b/t/api-rest/api-bannedips.t
new file mode 100644
index 0000000000..88191ddb3e
--- /dev/null
+++ b/t/api-rest/api-bannedips.t
@@ -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:
diff --git a/t/api-rest/api-bannedusers.t b/t/api-rest/api-bannedusers.t
new file mode 100644
index 0000000000..6cedb5eb19
--- /dev/null
+++ b/t/api-rest/api-bannedusers.t
@@ -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:
diff --git a/t/lib/Test/Collection.pm b/t/lib/Test/Collection.pm
index 7d9ae63f28..a938943d4f 100644
--- a/t/lib/Test/Collection.pm
+++ b/t/lib/Test/Collection.pm
@@ -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) = @_;