NG auth: pw changes, RT#21563
authorIvan Kohler <ivan@freeside.biz>
Thu, 9 May 2013 08:42:39 +0000 (01:42 -0700)
committerIvan Kohler <ivan@freeside.biz>
Thu, 9 May 2013 08:42:39 +0000 (01:42 -0700)
FS/FS/Auth/internal.pm
FS/FS/access_user.pm
httemplate/edit/access_user.html
httemplate/edit/process/access_user.html
httemplate/edit/process/elements/process.html
httemplate/pref/pref-process.html
httemplate/pref/pref.html

index 5d9170e..bb116ce 100644 (file)
@@ -2,29 +2,32 @@ package FS::Auth::internal;
 #use base qw( FS::Auth );
 
 use strict;
-use Crypt::Eksblowfish::Bcrypt qw(bcrypt_hash);
+use Crypt::Eksblowfish::Bcrypt qw(bcrypt_hash en_base64 de_base64);
 use FS::Record qw( qsearchs );
 use FS::access_user;
 
 sub authenticate {
   my($self, $username, $check_password ) = @_;
 
-  my $access_user = qsearchs('access_user', { 'username' => $username,
-                                              'disabled' => '',
-                                            }
-                            )
+  my $access_user =
+    ref($username) ? $username
+                   : qsearchs('access_user', { 'username' => $username,
+                                               'disabled' => '',
+                                             }
+                             )
     or return 0;
 
   if ( $access_user->_password_encoding eq 'bcrypt' ) {
 
     my( $cost, $salt, $hash ) = split(',', $access_user->_password);
 
-    my $check_hash = bcrypt_hash( { key_nul => 1,
-                                    cost    => $cost,
-                                    salt    => $salt,
-                                  },
-                                  $check_password
-                                );
+    my $check_hash = en_base64( bcrypt_hash( { key_nul => 1,
+                                               cost    => $cost,
+                                               salt    => de_base64($salt),
+                                             },
+                                             $check_password
+                                           )
+                              );
 
     $hash eq $check_hash;
 
@@ -39,7 +42,35 @@ sub authenticate {
 
 }
 
-#sub change_password {
-#}
+sub change_password {
+  my($self, $access_user, $new_password) = @_;
+
+  $self->change_password_fields( $access_user, $new_password );
+
+  $access_user->replace;
+
+}
+
+sub change_password_fields {
+  my($self, $access_user, $new_password) = @_;
+
+  $access_user->_password_encoding('bcrypt');
+
+  my $cost = 8;
+
+  my $salt = pack( 'C*', map int(rand(256)), 1..16 );
+
+  my $hash = bcrypt_hash( { key_nul => 1,
+                            cost    => $cost,
+                            salt    => $salt,
+                          },
+                          $new_password,
+                        );
+
+  $access_user->_password(
+    join(',', $cost, en_base64($salt), en_base64($hash) )
+  );
+
+}
 
 1;
index 509cc09..cdee377 100644 (file)
@@ -4,6 +4,7 @@ use strict;
 use base qw( FS::m2m_Common FS::option_Common ); 
 use vars qw( $DEBUG $me $conf $htpasswd_file );
 use FS::UID;
+use FS::Auth;
 use FS::Conf;
 use FS::Record qw( qsearch qsearchs dbh );
 use FS::access_user_pref;
@@ -563,7 +564,27 @@ sub is_system_user {
     fs_signup
     fs_bootstrap
     fs_selfserv
-) );
+  ) );
+}
+
+=item change_password NEW_PASSWORD
+
+=cut
+
+sub change_password {
+  #my( $self, $password ) = @_;
+  #FS::Auth->auth_class->change_password( $self, $password );
+  FS::Auth->auth_class->change_password( @_ );
+}
+
+=item change_password_fields NEW_PASSWORD
+
+=cut
+
+sub change_password_fields {
+  #my( $self, $password ) = @_;
+  #FS::Auth->auth_class->change_password_fields( $self, $password );
+  FS::Auth->auth_class->change_password_fields( @_ );
 }
 
 =back
index 86ce253..b087943 100644 (file)
@@ -3,8 +3,7 @@
                  'table'  => 'access_user',
                  'fields' => [
                                'username',
-                               { field=>'_password', type=>'password' },
-                               { field=>'_password2', type=>'password' },
+                               @pw_fields,
                                'last',
                                'first',
                                { field=>'user_custnum', type=>'search-cust_main', },
 die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
 
+my @pw_fields =
+  FS::Auth->auth_class->can('change_password')
+    ? ( { field=>'_password',  type=>'password' },
+        { field=>'_password2', type=>'password' },
+      )
+    : ();
+
 my $check_user_custnum_search = <<END;
   <SCRIPT TYPE="text/javascript">
     function check_user_custnum_search(what) {
index 8e7e70a..7fc7c25 100644 (file)
@@ -3,14 +3,15 @@
 %    print $cgi->redirect(popurl(2) . "access_user.html?" . $cgi->query_string);
 %  } else {
 <%   include( 'elements/process.html',
-                 'table'       => 'access_user',
-                 'viewall_dir' => 'browse',
-                 'copy_on_empty' => [ '_password' ],
+                 'table'          => 'access_user',
+                 'viewall_dir'    => 'browse',
+                 'copy_on_empty'  => [ '_password', '_password_encoding' ],
                  'clear_on_error' => [ '_password', '_password2' ],
-                 'process_m2m' => { 'link_table'   => 'access_usergroup',
-                                    'target_table' => 'access_group',
-                                  },
-                 'precheck_callback'=> \&precheck_callback,
+                 'process_m2m'    => { 'link_table'   => 'access_usergroup',
+                                       'target_table' => 'access_group',
+                                     },
+                 'precheck_callback'        => \&precheck_callback,
+                 'post_new_object_callback' => \&post_new_object_callback,
              )
 %>
 %   }
@@ -26,11 +27,24 @@ if ( FS::Conf->new->exists('disable_acl_changes') ) {
 
 sub precheck_callback {
   my $cgi = shift;
+
   my $o = FS::access_user->new({username => $cgi->param('username')});
   if( $o->is_system_user and !$cgi->param('usernum') ) {
     $cgi->param('username','');
     return "username '".$o->username."' reserved for system account."
   }
+
   return '';
 }
+
+sub post_new_object_callback {
+  my( $cgi, $access_user ) = @_;
+
+  if ( length($cgi->param('_password')) ) {
+    my $password = scalar($cgi->param('_password'));
+    $access_user->change_password_fields($password);
+  }
+
+}
+
 </%init>
index fb1ee7a..2afbdd0 100644 (file)
@@ -70,6 +70,9 @@ Example:
    #return an error string or empty for no error
    'precheck_callback' => sub { my( $cgi ) = @_; },
 
+   #after the new object is created
+   'post_new_object_callback' => sub { my( $cgi, $object ) = @_; },
+
    #after everything's inserted
    'noerror_callback' => sub { my( $cgi, $object ) = @_; },
 
@@ -226,6 +229,10 @@ foreach my $value ( @values ) {
       }
     }
 
+    if ( $opt{'post_new_object_callback'} ) {
+      &{ $opt{'post_new_object_callback'} }( $cgi, $new );
+    }
+
     if ( $opt{'agent_virt'} ) {
 
       if ( ! $new->agentnum
index 242e122..962ee51 100644 (file)
@@ -13,35 +13,35 @@ if ( FS::Conf->new->exists('disable_acl_changes') ) {
 }
 
 my $error = '';
-my $access_user = '';
 
-if ( grep { $cgi->param($_) !~ /^\s*$/ }
-          qw(_password new_password new_password2)
+if ( FS::Auth->auth_class->can('change_password')
+       && grep { $cgi->param($_) !~ /^\s*$/ }
+            qw(_password new_password new_password2)
    ) {
 
-  $access_user = qsearchs( 'access_user', {
-    'usernum'   => $FS::CurrentUser::CurrentUser->usernum,
-    'username'  => $FS::CurrentUser::CurrentUser->username,
-    '_password' => scalar($cgi->param('_password')),
-  } );
+  if ( $cgi->param('new_password') ne $cgi->param('new_password2') ) {
+    $error = "New passwords don't match";
 
-  $error = 'Current password incorrect; password not changed'
-    unless $access_user;
+  } elsif ( ! length($cgi->param('new_password')) ) {
+    $error = 'No new password entered';
 
-  $error ||= "New passwords don't match"
-    unless $cgi->param('new_password') eq $cgi->param('new_password2');
+  } elsif ( ! FS::Auth->authenticate( $FS::CurrentUser::CurrentUser,
+                                      scalar($cgi->param('_password')) )
+          ) {
+    $error = 'Current password incorrect; password not changed';
 
-  $error ||= "No new password entered"
-   unless length($cgi->param('new_password'));
+  } else {
 
-  $access_user->_password($cgi->param('new_password')) unless $error;
+    $error = $FS::CurrentUser::CurrentUser->change_password(
+      scalar($cgi->param('new_password'))
+    );
 
-} else {
-
-  $access_user = $FS::CurrentUser::CurrentUser;
+  }
 
 }
 
+my $access_user = $FS::CurrentUser::CurrentUser;
+
 #well, if you got your password change wrong, you don't get anything else
 #changed right now.  but it should be sticky on the form
 unless ( $error ) { # if ($access_user) {
index 9861c3f..dc44db0 100644 (file)
@@ -4,28 +4,30 @@
 
 <% include('/elements/error.html') %>
 
+% if ( FS::Auth->auth_class->can('change_password') ) {
 
-<% mt('Change password (leave blank for no change)') |h %>
-<% ntable("#cccccc",2) %>
+    <% mt('Change password (leave blank for no change)') |h %>
+    <% ntable("#cccccc",2) %>
 
-  <TR>
-    <TH ALIGN="right">Current password: </TH>
-    <TD><INPUT TYPE="password" NAME="_password"></TD>
-  </TR>
+      <TR>
+        <TH ALIGN="right">Current password: </TH>
+        <TD><INPUT TYPE="password" NAME="_password"></TD>
+      </TR>
 
-  <TR>
-    <TH ALIGN="right">New password: </TH>
-    <TD><INPUT TYPE="password" NAME="new_password"></TD>
-  </TR>
+      <TR>
+        <TH ALIGN="right">New password: </TH>
+        <TD><INPUT TYPE="password" NAME="new_password"></TD>
+      </TR>
 
-  <TR>
-   <TH ALIGN="right">Re-enter new password: </TH>
-   <TD><INPUT TYPE="password" NAME="new_password2"></TD>
-  </TR>
+      <TR>
+       <TH ALIGN="right">Re-enter new password: </TH>
+       <TD><INPUT TYPE="password" NAME="new_password2"></TD>
+      </TR>
 
-</TABLE>
-<BR>
+    </TABLE>
+    <BR>
 
+% }
 
 Interface
 <% ntable("#cccccc",2) %>