MT#59447 admin user LDAP authentication

Change-Id: I86005af30dee6285d80fe69f020f4a33cb23a608
mr13.1
Rene Krenn 7 months ago
parent a8476ad20d
commit 358139edee

1
debian/control vendored

@ -94,6 +94,7 @@ Depends:
libmoosex-emulate-class-accessor-fast-perl, libmoosex-emulate-class-accessor-fast-perl,
libnamespace-autoclean-perl, libnamespace-autoclean-perl,
libnet-http-perl, libnet-http-perl,
libnet-ldap-perl,
libnet-telnet-perl, libnet-telnet-perl,
libnetaddr-ip-perl, libnetaddr-ip-perl,
libngcp-schema-perl, libngcp-schema-perl,

@ -1298,9 +1298,9 @@ sub dev_field_config :Chained('/') :PathPart('device/autoprov/config') :Args() {
name => 'PBX Address Book', name => 'PBX Address Book',
}, },
ldap => { ldap => {
tls => $c->config->{ldap}->{tls}, tls => $c->config->{ldap_device}->{tls},
ip => $c->config->{ldap}->{ip}, ip => $c->config->{ldap_device}->{ip},
port => $c->config->{ldap}->{port}, port => $c->config->{ldap_device}->{port},
dn => ',dc=hpbx,dc=sipwise,dc=com', # uid=xxx,o=contract-id added below dn => ',dc=hpbx,dc=sipwise,dc=com', # uid=xxx,o=contract-id added below
password => '', # set below password => '', # set below
base => ',dc=hpbx,dc=sipwise,dc=com', # o=contract-id added below base => ',dc=hpbx,dc=sipwise,dc=com', # o=contract-id added below

@ -14,7 +14,7 @@ has_block 'fields' => (
tag => 'div', tag => 'div',
class => [qw(modal-body)], class => [qw(modal-body)],
render_list => [qw( render_list => [qw(
reseller login password email role_id is_master is_active read_only show_passwords call_data billing_data can_reset_password reseller login auth_mode password email role_id is_master is_active read_only show_passwords call_data billing_data can_reset_password
)], )],
); );

@ -10,6 +10,16 @@ sub build_render_list {[qw/submitid fields actions/]}
sub build_form_element_class {[qw(form-horizontal)]} sub build_form_element_class {[qw(form-horizontal)]}
has_field 'login' => (type => 'Text', required => 1, minlength => 5, maxlength => 31, default_method => \&_set_default); has_field 'login' => (type => 'Text', required => 1, minlength => 5, maxlength => 31, default_method => \&_set_default);
has_field 'auth_mode' => (
type => 'Select',
label => 'Authentication Mode',
required => 1,
options => [
{ value => 'local', label => 'local' },
{ value => 'ldap', label => 'LDAP' },
],
default_method => \&_set_default,
);
has_field 'password' => (type => 'Password', required => 1, label => 'Password'); has_field 'password' => (type => 'Password', required => 1, label => 'Password');
has_field 'email' => (type => 'Email', required => 0, label => 'Email', maxlength => 255); has_field 'email' => (type => 'Email', required => 0, label => 'Email', maxlength => 255);
for (qw(is_active show_passwords call_data billing_data for (qw(is_active show_passwords call_data billing_data
@ -21,7 +31,7 @@ has_block 'fields' => (
tag => 'div', tag => 'div',
class => [qw(modal-body)], class => [qw(modal-body)],
render_list => [qw( render_list => [qw(
login password email is_master is_active read_only show_passwords call_data billing_data can_reset_password login auth_mode password email is_master is_active read_only show_passwords call_data billing_data can_reset_password
)], )],
); );
has_block 'actions' => (tag => 'div', class => [qw(modal-footer)], render_list => [qw(save)],); has_block 'actions' => (tag => 'div', class => [qw(modal-footer)], render_list => [qw(save)],);
@ -37,6 +47,10 @@ sub _set_default {
$field->default(1); $field->default(1);
} }
if ($field->name eq 'auth_mode') {
$field->default('local');
}
if (_check_inactive($field, $field->name)) { if (_check_inactive($field, $field->name)) {
$field->inactive(1); $field->inactive(1);
} }

@ -12,7 +12,7 @@ has_block 'fields' => (
tag => 'div', tag => 'div',
class => [qw(modal-body)], class => [qw(modal-body)],
render_list => [qw( render_list => [qw(
reseller login password email role_id is_master is_active read_only show_passwords call_data billing_data can_reset_password reseller login auth_mode password email role_id is_master is_active read_only show_passwords call_data billing_data can_reset_password
)], )],
); );

@ -7,6 +7,23 @@ use IO::Compress::Zip qw/zip/;
use IPC::System::Simple qw/capturex/; use IPC::System::Simple qw/capturex/;
use UUID; use UUID;
use NGCP::Panel::Utils::Ldap qw(
auth_ldap_simple
get_user_dn
$ldapconnecterror
$ldapnouserdn
$ldapauthfailed
$ldapsearchfailed
$ldapnousersfound
$ldapmultipleusersfound
$ldapuserfound
$ldapauthsuccessful
);
our $local_auth_method = 'local';
our $ldap_auth_method = 'ldap';
our $SALT_LENGTH = 128; our $SALT_LENGTH = 128;
our $ENCRYPT_SUBSCRIBER_WEBPASSWORDS = 1; our $ENCRYPT_SUBSCRIBER_WEBPASSWORDS = 1;
@ -54,7 +71,9 @@ sub get_usr_salted_pass {
sub perform_auth { sub perform_auth {
my ($c, $user, $pass, $realm, $bcrypt_realm) = @_; my ($c, $user, $pass, $realm, $bcrypt_realm) = @_;
my $res; my $res;
my $log_failed_login_attempt = 1;
return $res if !check_password($pass); return $res if !check_password($pass);
return $res if user_is_banned($c, $user, 'admin'); return $res if user_is_banned($c, $user, 'admin');
@ -64,52 +83,65 @@ sub perform_auth {
login => $user, login => $user,
is_active => 1, is_active => 1,
}) if $user; }) if $user;
if(defined $dbadmin && defined $dbadmin->saltedpass) { return $res unless $dbadmin;
$c->log->debug("login via bcrypt");
my $saltedpass = $dbadmin->saltedpass; if ($dbadmin->auth_mode eq $local_auth_method) {
my $usr_salted_pass = get_usr_salted_pass($saltedpass, $pass); if (defined $dbadmin->saltedpass) {
# fetch again to load user into session etc (otherwise we could $c->log->debug("login via bcrypt");
# simply compare the two hashes here :( my $saltedpass = $dbadmin->saltedpass;
$res = $c->authenticate( my $usr_salted_pass = get_usr_salted_pass($saltedpass, $pass);
{ # fetch again to load user into session etc (otherwise we could
login => $user, # simply compare the two hashes here :(
saltedpass => $usr_salted_pass, $res = $c->authenticate(
'dbix_class' => { {
searchargs => [{ login => $user,
-and => [ saltedpass => $usr_salted_pass,
login => $user, 'dbix_class' => {
is_active => 1, searchargs => [{
], -and => [
}], login => $user,
} is_active => 1,
}, $bcrypt_realm ],
); }],
} elsif(defined $dbadmin) { # we already know if the username is wrong, no need to check again }
}, $bcrypt_realm
# check md5 and migrate over to bcrypt on success );
$c->log->debug("login via md5"); } elsif (defined $dbadmin) { # we already know if the username is wrong, no need to check again
$res = $c->authenticate( # check md5 and migrate over to bcrypt on success
{ $c->log->debug("login via md5");
login => $user, $res = $c->authenticate(
md5pass => $pass, {
'dbix_class' => { login => $user,
searchargs => [{ md5pass => $pass,
-and => [ 'dbix_class' => {
login => $user, searchargs => [{
is_active => 1, -and => [
], login => $user,
}], is_active => 1,
} ],
}, $realm); }],
}
if($res) { }, $realm);
# login ok, time to move user to bcrypt hashing if($res) {
$c->log->debug("migrating to bcrypt"); # login ok, time to move user to bcrypt hashing
my $saltedpass = generate_salted_hash($pass); $c->log->debug("migrating to bcrypt");
$dbadmin->update({ my $saltedpass = generate_salted_hash($pass);
md5pass => undef, $dbadmin->update({
saltedpass => $saltedpass, md5pass => undef,
}); saltedpass => $saltedpass,
});
}
}
} elsif ($dbadmin->auth_mode eq $ldap_auth_method) {
$c->log->debug("login via ldap");
my ($code,$message) = auth_ldap_simple($c,get_user_dn($user),$pass);
if ($code == $ldapauthfailed) {
$res = 0;
} elsif ($code != $ldapauthsuccessful) {
$res = 0;
$log_failed_login_attempt = 0; # do not log failed attempt if there was an ldap error
} else {
$res = 1;
} }
} }
@ -117,7 +149,7 @@ sub perform_auth {
clear_failed_login_attempts($c, $user, 'admin'); clear_failed_login_attempts($c, $user, 'admin');
reset_ban_increment_stage($c, $user, 'admin'); reset_ban_increment_stage($c, $user, 'admin');
} }
: log_failed_login_attempt($c, $user, 'admin'); : ($log_failed_login_attempt && log_failed_login_attempt($c, $user, 'admin'));
return $res; return $res;
} }

@ -0,0 +1,146 @@
package NGCP::Panel::Utils::Ldap;
use strict;
use warnings;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(search_dn
auth_ldap_simple
get_user_dn
$ldapconnecterror
$ldapnouserdn
$ldapauthfailed
$ldapsearchfailed
$ldapnousersfound
$ldapmultipleusersfound
$ldapuserfound
$ldapauthsuccessful);
use Net::LDAP qw(LDAP_SUCCESS);
our $ldapconnecterror = -1;
our $ldapnouserdn = -2;
our $ldapauthfailed = -3;
our $ldapsearchfailed = -4;
our $ldapnousersfound = -5;
our $ldapmultipleusersfound = -6;
our $ldapuserfound = 1;
our $ldapauthsuccessful = 2;
sub get_user_dn {
my $c = shift;
my $dn_format = $c->config->{ldap_admin}->{format};
$dn_format ||= '%s';
print sprintf($dn_format, @_);
return sprintf($dn_format, @_);
}
sub search_dn {
my ($c,$user_dn) = @_;
my $message;
my $label = 'LDAP search: ';
my $ldap_uri = $c->config->{ldap_admin}->{uri};
my $ldap_manager_dn = $c->config->{ldap_admin}->{dn};
my $ldap_manager_password = $c->config->{ldap_admin}->{password};
if (length($user_dn)) {
my $ldap = Net::LDAP->new($ldap_uri, verify => 'none');
if (defined $ldap) {
my $mesg;
if (length($ldap_manager_dn) > 0) {
$mesg = $ldap->bind($ldap_manager_dn, password => $ldap_manager_password);
} else {
$mesg = $ldap->bind();
}
if ($mesg->code() != LDAP_SUCCESS) {
$message = $mesg->error();
$c->log->debug($label . $message);
return ($ldapauthfailed, $message);
}
my $search = $ldap->search(base => $user_dn, scope => 'base', filter => '(objectClass=*)'); #attrs => ['dn'], );
if ($search->code() != LDAP_SUCCESS) {
$message = $search->error();
$c->log->debug($label . $message);
return ($ldapsearchfailed,$message);
}
if ($search->count() == 0) {
$message = 'no ldap entry found: ' . $user_dn;
$ldap->unbind();
$c->log->debug($label . $message);
return ($ldapnousersfound,$message);
} elsif ($search->count() > 1) {
$message = 'multiple ldap entries found: ' . $user_dn;
$ldap->unbind();
$c->log->debug($label . $message);
return ($ldapmultipleusersfound,$message);
} else {
my $entry = $search->shift_entry();
$message = 'ldap entry found: ' . $entry->dn();
$ldap->unbind();
$c->log->info($label . $message);
return ($ldapuserfound,$message);
}
} else {
$message = $@;
$c->log->debug($label . $message);
return ($ldapconnecterror,$message);
}
} else {
$message = 'no user dn specified';
$c->log->debug($label . $message);
return ($ldapnouserdn,$message);
}
}
sub auth_ldap_simple {
my ($c,$user_dn,$password) = @_;
my $label = 'LDAP auth: ';
my $ldap_uri = $c->config->{ldap}->{admins}->{uri};
my $message = undef;
if (length($user_dn) > 0) {
my $ldap = Net::LDAP->new($ldap_uri, verify => 'none');
if (defined $ldap) {
my $mesg = $ldap->bind($user_dn, password => $password);
if ($mesg->code() != LDAP_SUCCESS) {
$message = $mesg->error();
$c->log->debug($label . $message);
return ($ldapauthfailed,$message);
} else {
$message = 'successful ldap authentication: ' . $user_dn;
$c->log->info($label . $message);
}
$ldap->unbind();
return ($ldapauthsuccessful,$message);
} else {
$message = $@;
$c->log->debug($label . $message);
return ($ldapconnecterror,$message);
}
} else {
$message = 'no user dn specified';
$c->log->debug($label . $message);
return ($ldapnouserdn,$message);
}
}
1;
Loading…
Cancel
Save