enable Bcrypt password storage

This commit is contained in:
Oliver Gorwits
2014-02-08 19:10:03 +00:00
parent f8cf6aad73
commit 545f878cb7
11 changed files with 114 additions and 66 deletions

View File

@@ -34,6 +34,7 @@ requires:
Dancer: 1.3112 Dancer: 1.3112
Dancer::Plugin::Auth::Extensible: 0.3 Dancer::Plugin::Auth::Extensible: 0.3
Dancer::Plugin::DBIC: 0.1803 Dancer::Plugin::DBIC: 0.1803
Dancer::Plugin::Passphrase: 2
File::ShareDir: 1.03 File::ShareDir: 1.03
Guard: 1.022 Guard: 1.022
HTML::Parser: 3.7 HTML::Parser: 3.7

View File

@@ -18,6 +18,7 @@ requires 'Daemon::Control' => 0.001000;
requires 'Dancer' => 1.3112; requires 'Dancer' => 1.3112;
requires 'Dancer::Plugin::DBIC' => 0.1803; requires 'Dancer::Plugin::DBIC' => 0.1803;
requires 'Dancer::Plugin::Auth::Extensible' => 0.30; requires 'Dancer::Plugin::Auth::Extensible' => 0.30;
requires 'Dancer::Plugin::Passphrase' => 2.00;
requires 'File::ShareDir' => 1.03; requires 'File::ShareDir' => 1.03;
requires 'Guard' => 1.022; requires 'Guard' => 1.022;
requires 'HTML::Parser' => 3.70; requires 'HTML::Parser' => 3.70;

View File

@@ -99,9 +99,9 @@ $bool = $term->ask_yn(
); );
deploy_db() if $bool; deploy_db() if $bool;
say '';
my $users = schema('netdisco')->resultset('User'); my $users = schema('netdisco')->resultset('User');
if ($users->count == 0) { if ($users->count == 0) {
say '';
$bool = $term->ask_yn( $bool = $term->ask_yn(
prompt => 'Would you like a default web user with Admin rights (discover, etc)?', prompt => 'Would you like a default web user with Admin rights (discover, etc)?',
default => 'n', default => 'n',
@@ -136,6 +136,10 @@ if ($users->count == 0) {
} }
} }
} }
elsif (!setting('safe_password_store')) {
say '*** WARNING: Weak password hashes are being stored in the database! ***';
say '*** WARNING: Please add "safe_password_store: true" to your ~/environments/deployment.yml file. ***';
}
say ''; say '';
$bool = $term->ask_yn( $bool = $term->ask_yn(

View File

@@ -210,7 +210,8 @@ C<name>, C<host>, C<user> and C<pass>).
In the same file uncomment and edit the C<domain_suffix> setting to be In the same file uncomment and edit the C<domain_suffix> setting to be
appropriate for your local site. If this is a fresh install, uncomment and set appropriate for your local site. If this is a fresh install, uncomment and set
the C<no_auth> value to true (temporarily disables user authentication). the C<no_auth> value to true (temporarily disables user authentication). Have
a quick read of the other settings to make sure you're happy, then move on.
=head1 Bootstrap =head1 Bootstrap
@@ -243,7 +244,7 @@ daemon at the same time. Similarly, if you use the device discovery with
Netdisco 2, disable your system's cron jobs for the Netdisco 1.x poller. Netdisco 2, disable your system's cron jobs for the Netdisco 1.x poller.
At this point you can revisit the C<~/environments/deployment.yml> file to At this point you can revisit the C<~/environments/deployment.yml> file to
uncomment more configuration. Check out the community string settings, and uncomment more configuration. Enable the C<community> string settings, and
C<housekeeping> which enables the automatic periodic device discovery. See C<housekeeping> which enables the automatic periodic device discovery. See
L<Configuration|App::Netdisco::Manual::Configuration> for further details. L<Configuration|App::Netdisco::Manual::Configuration> for further details.

View File

@@ -223,6 +223,13 @@ Value: List of Modules. Default: Empty List.
List of additional L<App::Netdisco::Web::Plugin> names to load. See also the List of additional L<App::Netdisco::Web::Plugin> names to load. See also the
C<web_plugins> setting. C<web_plugins> setting.
=head3 C<safe_password_store>
Value: Boolean. Default: true.
Set to "C<false>" if you MUST maintain backwards compatibility with the Netdisco
1.x web interface. Strongly recommended that you leave this set to "C<true>".
=head2 Netdisco Core =head2 Netdisco Core
=head3 C<mibhome> =head3 C<mibhome>

View File

@@ -90,61 +90,6 @@ hook 'after' => sub {
} }
}; };
get qr{^/(?:login(?:/denied)?)?} => sub {
template 'index';
};
# Override default login_handler so that we can log access in the
# database
post '/login' => sub {
my ($success, $realm) = authenticate_user(
params->{username}, params->{password}
);
if ($success) {
session logged_in_user => params->{username};
session logged_in_user_realm => $realm;
schema('netdisco')->resultset('UserLog')->create({
username => session('logged_in_user'),
userip => request->remote_address,
event => "Login",
details => params->{return_url},
});
redirect params->{return_url} || uri_for('/');
} else {
schema('netdisco')->resultset('UserLog')->create({
username => params->{username},
userip => request->remote_address,
event => "Login Failure",
details => params->{return_url},
});
vars->{login_failed}++;
forward uri_for('/login'), { login_failed => 1 }, { method => 'GET' };
}
};
# Since we override the default login_handler, logout has to be handled as
# well
any ['get','post'] => '/logout' => sub {
schema('netdisco')->resultset('UserLog')->create({
username => session('logged_in_user'),
userip => request->remote_address,
event => "Logout",
details => '',
});
session->destroy;
if (params->{return_url}) {
redirect params->{return_url};
} else {
return "OK, logged out successfully.";
}
};
any qr{.*} => sub { any qr{.*} => sub {
var('notfound' => true); var('notfound' => true);
status 'not_found'; status 'not_found';

View File

@@ -10,7 +10,7 @@ use base 'Dancer::Plugin::Auth::Extensible::Provider::Base';
use Dancer ':syntax'; use Dancer ':syntax';
use Dancer::Plugin::DBIC; use Dancer::Plugin::DBIC;
use Dancer::Plugin::Passphrase;
use Digest::MD5; use Digest::MD5;
sub authenticate_user { sub authenticate_user {
@@ -77,8 +77,25 @@ sub match_with_local_pass {
return unless $password and $user->$password_column; return unless $password and $user->$password_column;
my $sum = Digest::MD5::md5_hex($password); if ($user->$password_column !~ m/^{[A-Z]+}/) {
return ($sum eq $user->$password_column ? 1 : 0); debug 'authN: using legacy MD5';
my $sum = Digest::MD5::md5_hex($password);
if ($sum eq $user->$password_column) {
if (setting('safe_password_store')) {
# upgrade password if successful, and permitted
$user->update({password => passphrase($password)->generate});
}
return 1;
}
else {
return 0;
}
}
else {
debug 'authN: using Passphrase';
return passphrase($password)->matches($user->$password_column);
}
} }
sub match_with_ldap { sub match_with_ldap {

View File

@@ -27,4 +27,59 @@ hook 'before' => sub {
} }
}; };
get qr{^/(?:login(?:/denied)?)?} => sub {
template 'index';
};
# override default login_handler so we can log access in the database
post '/login' => sub {
my ($success, $realm) = authenticate_user(
params->{username}, params->{password}
);
if ($success) {
session logged_in_user => params->{username};
session logged_in_user_realm => $realm;
schema('netdisco')->resultset('UserLog')->create({
username => session('logged_in_user'),
userip => request->remote_address,
event => "Login",
details => params->{return_url},
});
redirect params->{return_url} || uri_for('/');
}
else {
schema('netdisco')->resultset('UserLog')->create({
username => params->{username},
userip => request->remote_address,
event => "Login Failure",
details => params->{return_url},
});
vars->{login_failed}++;
forward uri_for('/login'), { login_failed => 1 }, { method => 'GET' };
}
};
# we override the default login_handler, so logout has to be handled as well
any ['get','post'] => '/logout' => sub {
schema('netdisco')->resultset('UserLog')->create({
username => session('logged_in_user'),
userip => request->remote_address,
event => "Logout",
details => '',
});
session->destroy;
if (params->{return_url}) {
redirect params->{return_url};
}
else {
return "OK, logged out successfully.";
}
};
true; true;

View File

@@ -4,6 +4,7 @@ use Dancer ':syntax';
use Dancer::Plugin::Ajax; use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC; use Dancer::Plugin::DBIC;
use Dancer::Plugin::Auth::Extensible; use Dancer::Plugin::Auth::Extensible;
use Dancer::Plugin::Passphrase;
use App::Netdisco::Web::Plugin; use App::Netdisco::Web::Plugin;
use Digest::MD5 (); use Digest::MD5 ();
@@ -21,6 +22,16 @@ sub _sanity_ok {
return 1; return 1;
} }
sub _make_password {
my $pass = (shift || passphrase->generate_random);
if (setting('safe_password_store')) {
return passphrase($pass)->generate;
}
else {
return Digest::MD5::md5_hex($pass),
}
}
ajax '/ajax/control/admin/users/add' => require_role admin => sub { ajax '/ajax/control/admin/users/add' => require_role admin => sub {
send_error('Bad Request', 400) unless _sanity_ok(); send_error('Bad Request', 400) unless _sanity_ok();
@@ -28,7 +39,7 @@ ajax '/ajax/control/admin/users/add' => require_role admin => sub {
my $user = schema('netdisco')->resultset('User') my $user = schema('netdisco')->resultset('User')
->create({ ->create({
username => param('username'), username => param('username'),
password => Digest::MD5::md5_hex(param('password')), password => _make_password(param('password')),
fullname => param('fullname'), fullname => param('fullname'),
ldap => (param('ldap') ? \'true' : \'false'), ldap => (param('ldap') ? \'true' : \'false'),
port_control => (param('port_control') ? \'true' : \'false'), port_control => (param('port_control') ? \'true' : \'false'),
@@ -56,7 +67,7 @@ ajax '/ajax/control/admin/users/update' => require_role admin => sub {
$user->update({ $user->update({
((param('password') ne '********') ((param('password') ne '********')
? (password => Digest::MD5::md5_hex(param('password'))) ? (password => _make_password(param('password')))
: ()), : ()),
fullname => param('fullname'), fullname => param('fullname'),
ldap => (param('ldap') ? \'true' : \'false'), ldap => (param('ldap') ? \'true' : \'false'),

View File

@@ -72,6 +72,7 @@ web_plugins:
- Device::Neighbors - Device::Neighbors
- Device::Addresses - Device::Addresses
extra_web_plugins: [] extra_web_plugins: []
safe_password_store: true
# ------------- # -------------
# NETDISCO CORE # NETDISCO CORE

View File

@@ -18,6 +18,11 @@ database:
# RECOMMENDED SETTINGS # RECOMMENDED SETTINGS
# -------------------- # --------------------
# set to "false" if you MUST maintain backwards compatibility
# with Netdisco 1.x web frontend.
# ```````````````````````````````````````````````````````````
safe_password_store: true
# will be stripped from fqdn when displayed in the web UI # will be stripped from fqdn when displayed in the web UI
# also, do not forget the leading dot. # also, do not forget the leading dot.
# ``````````````````````````````````````````````````````` # ```````````````````````````````````````````````````````
@@ -48,8 +53,8 @@ database:
# expiry: # expiry:
# when: '20 23 * * *' # when: '20 23 * * *'
# increase the performance of parallel DNS resolution for node names # increase the performance of parallel DNS resolution for node
# (the default is max_outstanding: 10) # names (the default is max_outstanding: 10)
# ````````````````````````````````````````````````````````````
#dns: #dns:
# max_outstanding: 100 # max_outstanding: 100