enable Bcrypt password storage
This commit is contained in:
		| @@ -34,6 +34,7 @@ requires: | ||||
|   Dancer: 1.3112 | ||||
|   Dancer::Plugin::Auth::Extensible: 0.3 | ||||
|   Dancer::Plugin::DBIC: 0.1803 | ||||
|   Dancer::Plugin::Passphrase: 2 | ||||
|   File::ShareDir: 1.03 | ||||
|   Guard: 1.022 | ||||
|   HTML::Parser: 3.7 | ||||
|   | ||||
| @@ -18,6 +18,7 @@ requires 'Daemon::Control' => 0.001000; | ||||
| requires 'Dancer' => 1.3112; | ||||
| requires 'Dancer::Plugin::DBIC' => 0.1803; | ||||
| requires 'Dancer::Plugin::Auth::Extensible' => 0.30; | ||||
| requires 'Dancer::Plugin::Passphrase' => 2.00; | ||||
| requires 'File::ShareDir' => 1.03; | ||||
| requires 'Guard' => 1.022; | ||||
| requires 'HTML::Parser' => 3.70; | ||||
|   | ||||
| @@ -99,9 +99,9 @@ $bool = $term->ask_yn( | ||||
| ); | ||||
| deploy_db() if $bool; | ||||
|  | ||||
| say ''; | ||||
| my $users = schema('netdisco')->resultset('User'); | ||||
| if ($users->count == 0) { | ||||
|     say ''; | ||||
|     $bool = $term->ask_yn( | ||||
|       prompt => 'Would you like a default web user with Admin rights (discover, etc)?', | ||||
|       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 ''; | ||||
| $bool = $term->ask_yn( | ||||
|   | ||||
| @@ -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 | ||||
| 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 | ||||
|  | ||||
| @@ -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. | ||||
|  | ||||
| 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 | ||||
| L<Configuration|App::Netdisco::Manual::Configuration> for further details. | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
| 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 | ||||
|  | ||||
| =head3 C<mibhome> | ||||
|   | ||||
| @@ -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 { | ||||
|     var('notfound' => true); | ||||
|     status 'not_found'; | ||||
|   | ||||
| @@ -10,7 +10,7 @@ use base 'Dancer::Plugin::Auth::Extensible::Provider::Base'; | ||||
|  | ||||
| use Dancer ':syntax'; | ||||
| use Dancer::Plugin::DBIC; | ||||
|  | ||||
| use Dancer::Plugin::Passphrase; | ||||
| use Digest::MD5; | ||||
|  | ||||
| sub authenticate_user { | ||||
| @@ -77,8 +77,25 @@ sub match_with_local_pass { | ||||
|  | ||||
|     return unless $password and $user->$password_column; | ||||
|  | ||||
|     my $sum = Digest::MD5::md5_hex($password); | ||||
|     return ($sum eq $user->$password_column ? 1 : 0); | ||||
|     if ($user->$password_column !~ m/^{[A-Z]+}/) { | ||||
|         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 { | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -4,6 +4,7 @@ use Dancer ':syntax'; | ||||
| use Dancer::Plugin::Ajax; | ||||
| use Dancer::Plugin::DBIC; | ||||
| use Dancer::Plugin::Auth::Extensible; | ||||
| use Dancer::Plugin::Passphrase; | ||||
|  | ||||
| use App::Netdisco::Web::Plugin; | ||||
| use Digest::MD5 (); | ||||
| @@ -21,6 +22,16 @@ sub _sanity_ok { | ||||
|     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 { | ||||
|     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') | ||||
|         ->create({ | ||||
|           username => param('username'), | ||||
|           password => Digest::MD5::md5_hex(param('password')), | ||||
|           password => _make_password(param('password')), | ||||
|           fullname => param('fullname'), | ||||
|           ldap => (param('ldap') ? \'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({ | ||||
|         ((param('password') ne '********') | ||||
|           ? (password => Digest::MD5::md5_hex(param('password'))) | ||||
|           ? (password => _make_password(param('password'))) | ||||
|           : ()), | ||||
|         fullname => param('fullname'), | ||||
|         ldap => (param('ldap') ? \'true' : \'false'), | ||||
|   | ||||
| @@ -72,6 +72,7 @@ web_plugins: | ||||
|   - Device::Neighbors | ||||
|   - Device::Addresses | ||||
| extra_web_plugins: [] | ||||
| safe_password_store: true | ||||
|  | ||||
| # ------------- | ||||
| # NETDISCO CORE | ||||
|   | ||||
| @@ -18,6 +18,11 @@ database: | ||||
| # 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 | ||||
| # also, do not forget the leading dot. | ||||
| # ``````````````````````````````````````````````````````` | ||||
| @@ -48,8 +53,8 @@ database: | ||||
| #  expiry: | ||||
| #    when: '20 23 * * *' | ||||
|  | ||||
| # increase the performance of parallel DNS resolution for node names | ||||
| # (the default is max_outstanding: 10) | ||||
| # increase the performance of parallel DNS resolution for node | ||||
| # names (the default is max_outstanding: 10) | ||||
| # ```````````````````````````````````````````````````````````` | ||||
| #dns: | ||||
| #  max_outstanding: 100 | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user