TT#78557 - Use bcrypt to hash webpassword

* Change the way webpassword is handled accross
	   NGCP Panel UI/API to comply with new password
	   encryption
	 * At login, if password is not encrypted with
	   high cost due to the ngcp-bcrypt-webpassword
	   script, encrypt it with proper cost
	 * Accept old password format as well until all
	   webpasswords are encrypted

Change-Id: Iefa9584a62ab4b7d2a224d10bdd415e9cbb8dfb5
changes/28/12728/8
Andreas Granig 8 years ago committed by Flaviu Mates
parent ac7c50332a
commit 8f8e1d5fc9

@ -4,7 +4,7 @@ use Sipwise::Base;
use parent qw/NGCP::Panel::Role::Entities NGCP::Panel::Role::API::AdminCerts/;
use HTTP::Status qw(:constants);
use NGCP::Panel::Utils::Admin;
use NGCP::Panel::Utils::Auth;
__PACKAGE__->set_config();
@ -56,7 +56,7 @@ sub create_item {
unless($c->user->login eq $login
|| $c->user->is_master
|| $c->user->is_superuser
|| NGCP::Panel::Utils::Admin::get_special_admin_login() ne $login
|| NGCP::Panel::Utils::Auth::get_special_admin_login() ne $login
) {
$c->log->warn("Admin " . $c->user->login . " trying to create certs for user $login, reject");
$self->error($c, HTTP_FORBIDDEN, "Insufficient privileges to create certificate for this administrator");
@ -64,7 +64,7 @@ sub create_item {
}
my $err;
my $res = NGCP::Panel::Utils::Admin::generate_client_cert($c, $admin, sub {
my $res = NGCP::Panel::Utils::Auth::generate_client_cert($c, $admin, sub {
my $e = shift;
$self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to generate client certificate");
$err = 1;

@ -10,7 +10,7 @@ use HTTP::Status qw(:constants);
__PACKAGE__->set_config();
sub api_description {
return'Defines admins to log into the system via panel or api.';
return 'Defines admins to log into the system via panel or api.';
}
sub allowed_methods{

@ -5,7 +5,7 @@ use Sipwise::Base;
use parent qw/NGCP::Panel::Role::EntitiesItem NGCP::Panel::Role::API::Admins/;
use NGCP::Panel::Utils::Admin;
use NGCP::Panel::Utils::Auth;
use HTTP::Status qw(:constants);
sub allowed_methods{
@ -26,7 +26,7 @@ __PACKAGE__->set_config();
sub delete_item {
my ($self, $c, $item) = @_;
my $special_user_login = NGCP::Panel::Utils::Admin::get_special_admin_login();
my $special_user_login = NGCP::Panel::Utils::Auth::get_special_admin_login();
if($item->login eq $special_user_login) {
$self->error($c, HTTP_FORBIDDEN, "Cannot delete special user '$special_user_login'");
@ -56,7 +56,7 @@ sub delete_item {
sub update_item_model {
my ($self, $c, $item, $old_resource, $resource, $form) = @_;
if($old_resource->{login} eq NGCP::Panel::Utils::Admin::get_special_admin_login()) {
if($old_resource->{login} eq NGCP::Panel::Utils::Auth::get_special_admin_login()) {
my $active = $resource->{is_active};
$resource = $old_resource;
$resource->{is_active} = $active;

@ -78,19 +78,6 @@ sub query_params {
},
},
},
{
param => 'webpassword',
description => 'Search for specific webuser login password (exact match)',
query => {
first => sub {
my $q = shift;
return { 'provisioning_voip_subscriber.webpassword' => $q };
},
second => sub {
return { join => 'provisioning_voip_subscriber' };
},
},
},
{
param => 'domain',
description => 'Filter for subscribers in specific domain',
@ -303,6 +290,7 @@ sub GET :Allow {
now => $now) if !exists $contract_map{$contract->id}; #apply underrun lock level
$contract_map{$contract->id} = 1;
my $resource = $self->resource_from_item($c, $subscriber, $form);
delete $resource->{webpassword}; # since it's encrypted, no point to return it
push @embedded, $self->hal_from_item($c, $subscriber, $resource, $form);
push @links, Data::HAL::Link->new(
relation => 'ngcp:'.$self->resource_name,
@ -440,6 +428,7 @@ sub POST :Allow {
my ($_form) = $self->get_form($c);
my $_subscriber = $self->item_by_id($c, $subscriber->id);
my $_resource = $self->resource_from_item($c, $_subscriber, $_form);
delete $_resource->{webpassword}; # since it's encrypted, no point writing it into journal as well
return $self->hal_from_item($c,$_subscriber,$_resource,$_form); });
$guard->commit;

@ -51,6 +51,7 @@ sub GET :Allow {
my ($form) = $self->get_form($c);
my $resource = $self->resource_from_item($c, $subscriber, $form);
delete $resource->{webpassword}; # since it's encrypted, no point to return it
my $hal = $self->hal_from_item($c, $subscriber, $resource, $form);
$guard->commit; #potential db write ops in hal_from
@ -100,6 +101,7 @@ sub PUT :Allow {
last unless $subscriber;
$resource = $self->resource_from_item($c, $subscriber, $form);
delete $resource->{webpassword}; # since it's encrypted, no point to return it
my $hal = $self->hal_from_item($c, $subscriber, $resource, $form);
last unless $self->add_update_journal_item_hal($c,$hal);
@ -149,6 +151,7 @@ sub PATCH :Allow {
last unless $subscriber;
$resource = $self->resource_from_item($c, $subscriber, $form);
delete $resource->{webpassword}; # since it's encrypted, no point to return it
my $hal = $self->hal_from_item($c, $subscriber, $resource, $form);
last unless $self->add_update_journal_item_hal($c,$hal);
@ -214,6 +217,7 @@ sub DELETE :Allow {
my ($_form) = $self->get_form($c);
#my $_subscriber = $self->item_by_id($c, $id);
my $_resource = $self->resource_from_item($c, $subscriber, $_form);
delete $_resource->{webpassword}; # since it's encrypted, no point writing it into journal as well
return $self->hal_from_item($c,$subscriber,$_resource,$_form); });
NGCP::Panel::Utils::Subscriber::terminate(c => $c, subscriber => $subscriber);

@ -7,7 +7,7 @@ use NGCP::Panel::Form;
use HTTP::Headers qw();
use NGCP::Panel::Utils::Message;
use NGCP::Panel::Utils::Navigation;
use NGCP::Panel::Utils::Admin;
use NGCP::Panel::Utils::Auth;
sub auto :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) :AllowedRole(reseller) {
my ($self, $c) = @_;
@ -44,7 +44,7 @@ sub list_admin :PathPart('administrator') :Chained('/') :CaptureArgs(0) {
@{ $cols } = (@{ $cols }, { name => "lawful_intercept", title => $c->loc("Lawful Intercept") });
}
$c->stash->{admin_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, $cols);
$c->stash->{special_admin_login} = NGCP::Panel::Utils::Admin::get_special_admin_login();
$c->stash->{special_admin_login} = NGCP::Panel::Utils::Auth::get_special_admin_login();
return;
}
@ -109,7 +109,7 @@ sub create :Chained('list_admin') :PathPart('create') :Args(0) {
$form->values->{reseller_id} = $c->user->reseller_id;
}
$form->values->{md5pass} = undef;
$form->values->{saltedpass} = NGCP::Panel::Utils::Admin::generate_salted_hash(delete $form->values->{password});
$form->values->{saltedpass} = NGCP::Panel::Utils::Auth::generate_salted_hash(delete $form->values->{password});
$c->stash->{admins}->create($form->values);
delete $c->session->{created_objects}->{reseller};
NGCP::Panel::Utils::Message::info(
@ -163,7 +163,7 @@ sub edit :Chained('base') :PathPart('edit') :Args(0) {
my $params = { $c->stash->{administrator}->get_inflated_columns };
$params->{reseller}{id} = delete $params->{reseller_id};
$params = merge($params, $c->session->{created_objects});
if($c->stash->{administrator}->login eq NGCP::Panel::Utils::Admin::get_special_admin_login()){
if($c->stash->{administrator}->login eq NGCP::Panel::Utils::Auth::get_special_admin_login()){
$form = NGCP::Panel::Form::get("NGCP::Panel::Form::Administrator::AdminSpecial", $c);
}elsif($c->user->is_superuser) {
$form = NGCP::Panel::Form::get("NGCP::Panel::Form::Administrator::Admin", $c);
@ -202,10 +202,10 @@ sub edit :Chained('base') :PathPart('edit') :Args(0) {
delete $form->values->{password} unless length $form->values->{password};
if(exists $form->values->{password}) {
$form->values->{md5pass} = undef;
$form->values->{saltedpass} = NGCP::Panel::Utils::Admin::generate_salted_hash(delete $form->values->{password});
$form->values->{saltedpass} = NGCP::Panel::Utils::Auth::generate_salted_hash(delete $form->values->{password});
}
#should be after other fields, to remove all added values, e.g. reseller_id
if($c->stash->{administrator}->login eq NGCP::Panel::Utils::Admin::get_special_admin_login()) {
if($c->stash->{administrator}->login eq NGCP::Panel::Utils::Auth::get_special_admin_login()) {
foreach my $field ($form->fields){
if($field ne 'is_active'){
delete $form->values->{$field};
@ -249,7 +249,7 @@ sub delete_admin :Chained('base') :PathPart('delete') :Args(0) {
);
NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/administrator'));
}
my $special_user_login = NGCP::Panel::Utils::Admin::get_special_admin_login();
my $special_user_login = NGCP::Panel::Utils::Auth::get_special_admin_login();
if($c->stash->{administrator}->login eq $special_user_login) {
NGCP::Panel::Utils::Message::error(
c => $c,
@ -288,7 +288,7 @@ sub delete_admin :Chained('base') :PathPart('delete') :Args(0) {
sub api_key :Chained('base') :PathPart('api_key') :Args(0) {
my ($self, $c) = @_;
my $special_user_login = NGCP::Panel::Utils::Admin::get_special_admin_login();
my $special_user_login = NGCP::Panel::Utils::Auth::get_special_admin_login();
if($c->stash->{administrator}->login eq $special_user_login) {
NGCP::Panel::Utils::Message::error(
c => $c,
@ -302,7 +302,7 @@ sub api_key :Chained('base') :PathPart('api_key') :Args(0) {
my ($pem, $p12);
if ($c->req->body_parameters->{'gen.generate'}) {
my $err;
my $res = NGCP::Panel::Utils::Admin::generate_client_cert($c, $c->stash->{administrator}, sub {
my $res = NGCP::Panel::Utils::Auth::generate_client_cert($c, $c->stash->{administrator}, sub {
my $e = shift;
NGCP::Panel::Utils::Message::error(
c => $c,
@ -374,7 +374,7 @@ sub toggle_openvpn :Chained('list_admin') :PathPart('openvpn/toggle') :Args(1) {
my ($self, $c, $set_active) = @_;
unless ($set_active eq 'confirm') {
my ($message, $error) = NGCP::Panel::Utils::Admin::toggle_openvpn($c, $set_active);
my ($message, $error) = NGCP::Panel::Utils::Auth::toggle_openvpn($c, $set_active);
if ( $message ) {
NGCP::Panel::Utils::Message::info(
c => $c,

@ -1675,13 +1675,30 @@ sub dev_static_jitsi_config :Chained('/') :PathPart('device/autoprov/static/jits
my $sub;
if($c->config->{deviceprovisioning}->{softphone_webauth}) {
$sub = $c->model('DB')->resultset('provisioning_voip_subscribers')->find({
webusername => $user,
'domain.domain' => $domain,
webpassword => $pass,
},{
join => 'domain',
my $authrs = $c->model('DB')->resultset('provisioning_voip_subscribers')->search({
webusername => $user,
'voip_subscriber.status' => 'active',
'domain.domain' => $domain,
'contract.status' => 'active',
}, {
join => ['domain', 'contract', 'voip_subscriber'],
});
$sub = $authrs->first;
if(defined $sub) {
my ($db_b64salt, $db_b64hash) = split /\$/, $sub->webpassword;
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,
}, $pass));
unless($usr_b64hash eq $db_b64hash) {
# wrong password
$sub = undef;
}
}
unless($sub) {
if($c->config->{features}->{debug}) {
$c->response->body("404 - webuser authentication failed");
@ -1694,13 +1711,15 @@ sub dev_static_jitsi_config :Chained('/') :PathPart('device/autoprov/static/jits
$user = $sub->username;
$pass = $sub->password;
} else {
$sub = $c->model('DB')->resultset('provisioning_voip_subscribers')->find({
username => $user,
'domain.domain' => $domain,
password => $pass,
},{
join => 'domain',
});
$sub = $c->model('DB')->resultset('provisioning_voip_subscribers')->search({
username => $user,
password => $pass,
'voip_subscriber.status' => 'active',
'domain.domain' => $domain,
'contract.status' => 'active',
}, {
join => ['domain', 'contract', 'voip_subscriber'],
})->first;
unless($sub) {
if($c->config->{features}->{debug}) {
$c->response->body("404 - sipuser authentication failed");

@ -7,7 +7,7 @@ use parent 'Catalyst::Controller';
use NGCP::Panel::Form;
use NGCP::Panel::Utils::Admin;
use NGCP::Panel::Utils::Auth;
sub login_index :Path Form {
my ( $self, $c, $realm ) = @_;
@ -30,7 +30,7 @@ sub login_index :Path Form {
$c->log->debug("*** Login::index user=$user, pass=****, realm=$realm");
my $res;
if($realm eq 'admin') {
$res = NGCP::Panel::Utils::Admin::perform_auth($c, $user, $pass, 'admin', 'admin_bcrypt');
$res = NGCP::Panel::Utils::Auth::perform_auth($c, $user, $pass, 'admin', 'admin_bcrypt');
} elsif($realm eq 'subscriber') {
my ($u, $d, $t) = split /\@/, $user;
if(defined $t) {
@ -41,24 +41,7 @@ sub login_index :Path Form {
unless(defined $d) {
$d = $c->req->uri->host;
}
my $authrs = $c->model('DB')->resultset('provisioning_voip_subscribers')->search({
webusername => $u,
webpassword => $pass,
'voip_subscriber.status' => 'active',
'domain.domain' => $d,
'contract.status' => 'active',
}, {
join => ['domain', 'contract', 'voip_subscriber'],
});
$res = $c->authenticate(
{
webusername => $u,
webpassword => $pass,
'dbix_class' => {
resultset => $authrs
}
},
$realm);
$res = NGCP::Panel::Utils::Auth::perform_subscriber_auth($c, $u, $d, $pass);
}
if($res) {

@ -16,7 +16,7 @@ use NGCP::Panel::Utils::BillingNetworks qw();
use NGCP::Panel::Utils::ProfilePackages qw();
use NGCP::Panel::Utils::BillingMappings qw();
use NGCP::Panel::Utils::Billing qw();
use NGCP::Panel::Utils::Admin;
use NGCP::Panel::Utils::Auth;
use NGCP::Panel::Utils::Phonebook;
use NGCP::Panel::Utils::TimeSet;
use NGCP::Panel::Utils::Preferences qw();
@ -463,8 +463,8 @@ sub create_defaults :Path('create_defaults') :Args(0) :Does(ACL) :ACLDetachTo('/
$c->detach('/denied_page')
if($c->user->read_only);
my $default_pass = 'defaultresellerpassword';
my $saltedpass = NGCP::Panel::Utils::Admin::generate_salted_hash($default_pass);
my $default_pass = 'defaultresellerpassword';
my $saltedpass = NGCP::Panel::Utils::Auth::generate_salted_hash($default_pass);
my $billing = $c->model('DB');
my $now = NGCP::Panel::Utils::DateTime::current_local;

@ -9,12 +9,14 @@ use parent 'Catalyst::Controller';
use Scalar::Util qw(blessed);
use NGCP::Panel::Utils::DateTime qw();
use NGCP::Panel::Utils::Statistics qw();
use NGCP::Panel::Utils::Admin;
use NGCP::Panel::Utils::Auth;
use NGCP::Panel::Form qw();
use DateTime qw();
use Time::HiRes qw();
use DateTime::Format::RFC3339 qw();
use HTTP::Status qw(:constants);
use Crypt::Eksblowfish::Bcrypt qw/bcrypt_hash en_base64 de_base64/;
use Data::Entropy::Algorithms qw/rand_bits/;
use NGCP::Schema qw//;
@ -217,9 +219,9 @@ sub auto :Private {
if ($d) {
$c->req->headers->authorization_basic($u,$password);
}
my $res = $c->authenticate({}, $realm);
my $res = NGCP::Panel::Utils::Auth::perform_subscriber_auth($c, $u, $d, $password);
if($c->user_exists) {
if($res && $c->user_exists) {
$d //= $c->req->uri->host;
$c->log->debug("++++++ checking '".$c->user->domain->domain."' against '$d'");
if ($c->user->domain->domain ne $d) {
@ -245,7 +247,7 @@ sub auto :Private {
$c->log->debug("++++++ Root::auto API admin request with http auth");
my ($user, $pass) = $c->req->headers->authorization_basic;
#$c->log->debug("user: " . $user . " pass: " . $pass);
my $res = NGCP::Panel::Utils::Admin::perform_auth($c, $user, $pass, "api_admin" , "api_admin_bcrypt");
my $res = NGCP::Panel::Utils::Auth::perform_auth($c, $user, $pass, "api_admin" , "api_admin_bcrypt");
if($res and $c->user_exists and $c->user->is_active) {
$c->log->debug("++++++ admin '".$c->user->login."' authenticated via api_admin_http");
} else {
@ -337,7 +339,7 @@ sub auto :Private {
$topmenu_templates = ['widgets/'.$c->user->roles.'_topmenu_settings.tt'];
if ($c->user->roles eq 'admin') {
if (!$c->stash->{openvpn_info}) {
my $openvpn_info = NGCP::Panel::Utils::Admin::check_openvpn_status($c);
my $openvpn_info = NGCP::Panel::Utils::Auth::check_openvpn_status($c);
$c->stash(openvpn_info => $openvpn_info);
}
}
@ -488,14 +490,56 @@ sub login_jwt :Chained('/') :PathPart('login_jwt') :Args(0) :Method('POST') {
}
my $authrs = $c->model('DB')->resultset('provisioning_voip_subscribers')->search({
webusername => $u,
webpassword => $pass,
'voip_subscriber.status' => 'active',
'domain.domain' => $d,
'contract.status' => 'active',
}, {
join => ['domain', 'contract', 'voip_subscriber'],
});
my $auth_user = $authrs->first;
my $auth_user;
if ($authrs->first) {
my $password = $authrs->first->webpassword;
if (length $password > 40) {
my @splitted_pass = split /\$/, $password;
if (scalar @splitted_pass == 3) {
#password is bcrypted with lower cost
my ($cost, $db_b64salt, $db_b64hash) = @splitted_pass;
my $salt = de_base64($db_b64salt);
my $usr_b64hash = en_base64(bcrypt_hash({
key_nul => 1,
cost => $cost,
salt => $salt,
}, $pass));
if ($db_b64hash eq $usr_b64hash) {
#upgrade password to bigger cost
$salt = rand_bits(128);
my $b64salt = en_base64($salt);
my $b64hash = en_base64(bcrypt_hash({
key_nul => 1,
cost => NGCP::Panel::Utils::Auth::get_bcrypt_cost(),
salt => $salt,
}, $pass));
$authrs->first->update({webpassword => $b64salt . '$' . $b64hash});
$auth_user = $authrs->first;
}
}
elsif (scalar @splitted_pass == 2) {
#password is bcrypted with proper cost
my ($db_b64salt, $db_b64hash) = @splitted_pass;
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,
}, $pass));
$auth_user = $authrs->search({webpassword => $db_b64salt . '$' . $usr_b64hash})->first;
}
}
else {
$auth_user = $authrs->search({webpassword => $pass})->first;
}
}
my $result = {};

@ -35,7 +35,7 @@ use NGCP::Panel::Utils::SOAP qw/typed/;
use NGCP::Panel::Utils::Interception qw();
use UUID;
use Moose;
use NGCP::Panel::Utils::Admin;
use NGCP::Panel::Utils::Auth;
use Crypt::Eksblowfish::Bcrypt qw/bcrypt_hash en_base64 de_base64/;
has 'c' => (is => 'rw', isa => 'Object');
@ -85,7 +85,7 @@ sub _auth {
my $salt = de_base64($db_b64salt);
my $usr_b64hash = en_base64(bcrypt_hash({
key_nul => 1,
cost => NGCP::Panel::Utils::Admin::get_bcrypt_cost(),
cost => NGCP::Panel::Utils::Auth::get_bcrypt_cost(),
salt => $salt,
}, $auth->{password}));
@ -102,7 +102,7 @@ sub _auth {
# migrate password to bcrypt
$admin->update({
md5pass => undef,
saltedpass => NGCP::Panel::Utils::Admin::generate_salted_hash($auth->{password}),
saltedpass => NGCP::Panel::Utils::Auth::generate_salted_hash($auth->{password}),
});
}

@ -594,7 +594,7 @@ sub recover_webpassword :Chained('/') :PathPart('recoverwebpassword') :Args(0) {
$c->detach('/denied_page');
}
$subscriber->provisioning_voip_subscriber->update({
webpassword => $form->params->{password},
webpassword => NGCP::Panel::Utils::Auth::generate_salted_hash($form->params->{password}),
});
$rs->delete;
});
@ -2788,7 +2788,6 @@ sub edit_master :Chained('master') :PathPart('edit') :Args(0) :Does(ACL) :ACLDet
$prov_subscriber->voip_subscriber_profile->id : undef;
$params->{webusername} = $prov_subscriber->webusername;
if (($c->user->roles eq 'admin' || $c->user->roles eq 'reseller') && $c->user->show_passwords) {
$params->{webpassword} = $prov_subscriber->webpassword;
$params->{password} = $prov_subscriber->password;
}
$params->{administrative} = $prov_subscriber->admin;
@ -2876,8 +2875,10 @@ sub edit_master :Chained('master') :PathPart('edit') :Args(0) :Does(ACL) :ACLDet
my $prov_params = {};
$prov_params->{pbx_extension} = $form->params->{pbx_extension};
$prov_params->{webusername} = $form->params->{webusername} || undef;
$prov_params->{webpassword} = $form->params->{webpassword}
if($form->params->{webpassword});
if($form->params->{webpassword}) {
$prov_params->{webpassword} =
NGCP::Panel::Utils::Auth::generate_salted_hash($form->params->{webpassword});
}
$prov_params->{password} = $form->params->{password}
if($form->params->{password});
if($is_admin) {
@ -3222,7 +3223,8 @@ sub webpass_edit :Chained('base') :PathPart('webpass/edit') :Args(0) {
my $prov_subscriber = $subscriber->provisioning_voip_subscriber;
$schema->txn_do(sub {
$prov_subscriber->update({
webpassword => $form->values->{webpassword} });
webpassword => NGCP::Panel::Utils::Auth::generate_salted_hash($form->values->{webpassword}),
});
});
NGCP::Panel::Utils::Message::info(
c => $c,

@ -3,7 +3,7 @@ use HTML::FormHandler::Moose;
use HTML::FormHandler::Widget::Block::Bootstrap;
extends 'NGCP::Panel::Form::Administrator::Reseller';
use NGCP::Panel::Utils::Admin;
use NGCP::Panel::Utils::Auth;
for (qw(is_superuser lawful_intercept)) {
has_field $_ => (type => 'Boolean',);

@ -84,7 +84,7 @@ has_field 'webusername' => (
);
has_field 'webpassword' => (
type => 'Text',
type => 'Password',
label => 'Web Password',
required => 0,
element_attr => {

@ -33,7 +33,7 @@ has_field 'webusername' => (
);
has_field 'webpassword' => (
type => 'Text',
type => 'Password',
label => 'Web Password',
required => 0,
element_attr => {

@ -43,7 +43,7 @@ has_field 'webusername' => (
);
has_field 'webpassword' => (
type => 'Text',
type => 'Password',
label => 'Web Password',
required => 0,
element_attr => {

@ -12,7 +12,7 @@ sub build_render_list {[qw/submitid fields actions/]}
sub build_form_element_class {[qw(form-horizontal)]}
has_field 'webpassword' => (
type => 'Text',
type => 'Password',
label => 'Web Password',
required => 1,
minlength => 6,

@ -39,7 +39,7 @@ has_field 'webusername' => (
);
has_field 'webpassword' => (
type => 'Text',
type => 'Password',
label => 'Web Password',
required => 0,
minlength => 6,

@ -10,7 +10,7 @@ use Data::HAL::Link qw();
use HTTP::Status qw(:constants);
use NGCP::Panel::Utils::DateTime;
use NGCP::Panel::Utils::Admin;
use NGCP::Panel::Utils::Auth;
sub item_name{
return 'admin';
@ -77,7 +77,7 @@ sub process_form_resource{
delete $resource->{password};
if(defined $pass) {
$resource->{md5pass} = undef;
$resource->{saltedpass} = NGCP::Panel::Utils::Admin::generate_salted_hash($pass);
$resource->{saltedpass} = NGCP::Panel::Utils::Auth::generate_salted_hash($pass);
}
foreach my $f(qw/billing_data call_data is_active is_master is_superuser is_ccare lawful_intercept read_only show_passwords/) {
$resource->{$f} = (ref $resource->{$f} eq 'JSON::true' || ( defined $resource->{$f} && ( $resource->{$f} eq 'true' || $resource->{$f} eq '1' ) ) ) ? 1 : 0;

@ -455,11 +455,16 @@ sub update_item {
status => $resource->{status},
contact_id => $resource->{contact_id},
};
if(defined $resource->{webpassword}) {
$resource->{webpassword} = NGCP::Panel::Utils::Auth::generate_salted_hash($resource->{webpassword});
}
my $provisioning_res = {
password => $resource->{password},
webusername => $resource->{webusername},
webpassword => $resource->{webpassword},
admin => $resource->{administrative} // $subscriber->provisioning_voip_subscriber->admin,
admin => $resource->{administrative} // 0,
is_pbx_pilot => $resource->{is_pbx_pilot} // 0,
is_pbx_group => $resource->{is_pbx_group} // 0,
modify_timestamp => NGCP::Panel::Utils::DateTime::current_local,

@ -1,4 +1,4 @@
package NGCP::Panel::Utils::Admin;
package NGCP::Panel::Utils::Auth;
use Sipwise::Base;
use Crypt::Eksblowfish::Bcrypt qw/bcrypt_hash en_base64 de_base64/;
@ -100,6 +100,92 @@ sub perform_auth {
return $res;
}
sub perform_subscriber_auth {
my ($c, $user, $domain, $pass) = @_;
my $res;
my $authrs = $c->model('DB')->resultset('provisioning_voip_subscribers')->search({
webusername => $user,
'voip_subscriber.status' => 'active',
'domain.domain' => $domain,
'contract.status' => 'active',
}, {
join => ['domain', 'contract', 'voip_subscriber'],
});
my $sub = $authrs->first;
if(defined $sub) {
my $sub_pass = $sub->webpassword;
if (length $sub_pass > 40) {
my @splitted_pass = split /\$/, $sub_pass;
if (scalar @splitted_pass == 3) {
#password is bcrypted with lower cost
my ($cost, $db_b64salt, $db_b64hash) = @splitted_pass;
my $salt = de_base64($db_b64salt);
my $usr_b64hash = en_base64(bcrypt_hash({
key_nul => 1,
cost => $cost,
salt => $salt,
}, $pass));
if ($db_b64hash eq $usr_b64hash) {
#upgrade password to bigger cost
$salt = rand_bits(128);
my $b64salt = en_base64($salt);
my $b64hash = en_base64(bcrypt_hash({
key_nul => 1,
cost => get_bcrypt_cost(),
salt => $salt,
}, $pass));
$sub->update({webpassword => $b64salt . '$' . $b64hash});
$res = $c->authenticate(
{
webusername => $user,
webpassword => $b64salt . '$' . $b64hash,
'dbix_class' => {
resultset => $authrs
}
},
'subscriber');
}
}
elsif (scalar @splitted_pass == 2) {
#password is bcrypted with proper cost
my ($db_b64salt, $db_b64hash) = @splitted_pass;
my $salt = de_base64($db_b64salt);
my $usr_b64hash = en_base64(bcrypt_hash({
key_nul => 1,
cost => get_bcrypt_cost(),
salt => $salt,
}, $pass));
# fetch again to load user into session etc (otherwise we could
# simply compare the two hashes here :(
$res = $c->authenticate(
{
webusername => $user,
webpassword => $db_b64salt . '$' . $usr_b64hash,
'dbix_class' => {
resultset => $authrs
}
},
'subscriber');
}
}
else {
$res = $c->authenticate(
{
webusername => $user,
webpassword => $pass,
'dbix_class' => {
resultset => $authrs
}
},
'subscriber');
}
}
return $res;
}
sub generate_client_cert {
my ($c, $admin, $error_cb) = @_;

@ -17,6 +17,7 @@ use NGCP::Panel::Utils::License;
use NGCP::Panel::Utils::Generic;
use NGCP::Panel::Utils::Contract;
use NGCP::Panel::Utils::RedisLocationResultSet;
use NGCP::Panel::Utils::Auth;
use UUID qw/generate unparse/;
use JSON qw/decode_json encode_json/;
use HTTP::Status qw(:constants);
@ -662,6 +663,9 @@ sub create_subscriber {
UUID::unparse($pass_bin, $pass_str);
$params->{password} = $pass_str;
}
if(defined $params->{webpassword}) {
$params->{webpassword} = NGCP::Panel::Utils::Auth::generate_salted_hash($params->{webpassword});
}
my $prov_subscriber = $schema->resultset('provisioning_voip_subscribers')->create({
uuid => $uuid_string,
username => $params->{username},

@ -6,11 +6,11 @@ use v5.14;
use lib '../lib';
use lib '../../sipwise-base/lib';
use NGCP::Panel::Utils::Admin;
use NGCP::Panel::Utils::Auth;
use Time::HiRes qw/gettimeofday tv_interval/;
my $t0 = [gettimeofday()];
NGCP::Panel::Utils::Admin::generate_salted_hash("testpass");
NGCP::Panel::Utils::Auth::generate_salted_hash("testpass");
my $t1 = [gettimeofday()];
my $diff = tv_interval($t0, $t1);

@ -41,7 +41,6 @@
elements.push({ value = subscriber.contact.email, desc = c.loc('Email Address') });
END;
elements.push({ value = subscriber.provisioning_voip_subscriber.webusername, desc = c.loc('Web Username') });
elements.push({ value = '******', desc = c.loc('Web Password') });
elements.push({ value = subscriber.username _ '@' _ subscriber.domain.domain, desc = c.loc('SIP URI') });
#elements.push({ value = , desc = c.loc('Primary Number') });

@ -83,9 +83,6 @@
elements.push({ value = subscriber.contact.email, desc = c.loc('Email Address') });
END;
elements.push({ value = subscriber.provisioning_voip_subscriber.webusername, desc = c.loc('Web Username') });
IF (c.user.roles == "admin" || c.user.roles == "reseller") && c.user.show_passwords;
elements.push({ value = subscriber.provisioning_voip_subscriber.webpassword, desc = c.loc('Web Password') });
END;
elements.push({ value = subscriber.username _ '@' _ subscriber.domain.domain, desc = c.loc('SIP URI') });
IF (c.user.roles == "admin" || c.user.roles == "reseller") && c.user.show_passwords;
elements.push({ value = subscriber.provisioning_voip_subscriber.password, desc = c.loc('SIP Password') });

Loading…
Cancel
Save