From: Ivan Kohler Date: Thu, 8 Sep 2022 00:22:51 +0000 (-0700) Subject: google authenticator support, RT#86743 X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=26ddb940ad27ce8ac5e87084eeed857a390987bc google authenticator support, RT#86743 --- diff --git a/FS/FS/AuthCookieHandler.pm b/FS/FS/AuthCookieHandler.pm index 93d8ea6a5..b7d0dbf5b 100644 --- a/FS/FS/AuthCookieHandler.pm +++ b/FS/FS/AuthCookieHandler.pm @@ -13,13 +13,13 @@ sub useragent_ip { } sub authen_cred { - my( $self, $r, $username, $password ) = @_; + my( $self, $r, $username, $password, $totp_code ) = @_; preuser_setup(); my $info = {}; - unless ( FS::Auth->authenticate($username, $password, $info) ) { + unless ( FS::Auth->authenticate($username, $password, $totp_code, $info) ) { warn "failed auth $username from ". $self->useragent_ip($r). "\n"; return undef; } diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index d88403644..61b793bb4 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -5931,6 +5931,7 @@ sub tables_hashref { 'username', 'varchar', '', $char_d, '', '', '_password', 'varchar', 'NULL', $char_d, '', '', '_password_encoding', 'varchar', 'NULL', $char_d, '', '', + 'totp_secret32', 'char', 'NULL', 32, '', '', 'last', 'varchar', 'NULL', $char_d, '', '', 'first', 'varchar', 'NULL', $char_d, '', '', 'user_custnum', 'int', 'NULL', '', '', '', diff --git a/FS/FS/access_user.pm b/FS/FS/access_user.pm index f23aa77f9..270f8bb27 100644 --- a/FS/FS/access_user.pm +++ b/FS/FS/access_user.pm @@ -13,6 +13,7 @@ use FS::agent; use FS::cust_main; use FS::sales; use Carp qw( croak ); +use Auth::GoogleAuth; $DEBUG = 0; $me = '[FS::access_user]'; @@ -239,6 +240,7 @@ sub check { $self->ut_numbern('usernum') || $self->ut_alpha_lower('username') || $self->ut_textn('_password') + || $self->ut_alphan('totp_secret32') || $self->ut_textn('last') || $self->ut_textn('first') || $self->ut_foreign_keyn('user_custnum', 'cust_main', 'custnum') @@ -733,6 +735,44 @@ sub change_password_fields { FS::Auth->auth_class->change_password_fields( @_ ); } +=item google_auth + +=cut + +sub google_auth { + my( $self ) = @_; + my $issuer = FS::Conf->new->config('company_name'). ' Freeside'; + my $label = $issuer. ':'. $self->username; + + Auth::GoogleAuth->new({ + secret => $self->totp_secret32, + issuer => $issuer, + key_id => $label, + }); + +} + +=item set_totp_secret32 + +=cut + +sub set_totp_secret32 { + my( $self ) = @_; + + $self->totp_secret32( $self->google_auth->generate_secret32 ); + $self->replace; +} + +=item totp_qr_code_url + +=cut + +sub totp_qr_code_url { + my( $self ) = @_; + + $self->google_auth->qr_code; +} + =item locale =cut diff --git a/debian/control b/debian/control index 479a150d1..043294fc8 100644 --- a/debian/control +++ b/debian/control @@ -109,7 +109,7 @@ Depends: aspell-en,gnupg,ghostscript,gsfonts,gzip, libspreadsheet-xlsx-perl, libpod-simple-perl, libwebservice-northern911-perl, liblocale-codes-perl, liblocale-po-perl, libgeo-uscensus-geocoding-perl, libnet-sftp-foreign-perl, libpdf-webkit-perl, libgeo-shapelib-perl, - libgeo-json-perl + libgeo-json-perl, libauth-googleauth-perl Conflicts: libparams-classify-perl (>= 0.013-6) Replaces: freeside (<<4) Breaks: freeside (<<4) diff --git a/httemplate/browse/access_user.html b/httemplate/browse/access_user.html index 446bfe0be..658762763 100644 --- a/httemplate/browse/access_user.html +++ b/httemplate/browse/access_user.html @@ -49,6 +49,11 @@ my $groups_sub = sub { }; +my $goog_auth_sub = sub { + my $access_user = shift; + $access_user->totp_secret32 ? 'Enabled' : ''; +}; + my $installer_sub = sub { my $access_user = shift; my @sched_item = $access_user->sched_item or return ''; @@ -66,11 +71,23 @@ my $count_query = 'SELECT COUNT(*) FROM access_user'; my $link = [ $p.'edit/access_user.html?', 'usernum' ]; my @header = ( - 'Username', 'Full name', 'Groups', 'Installer', 'Customer' ); + 'Username', + 'Full name', + 'Groups', + 'Google Auth', + 'Installer', + 'Customer', +); my @fields = ( - 'username', 'name', $groups_sub, $installer_sub, $cust_sub, ); -my $align = 'lllcl'; -my @links = ( $link, $link, $link, '', '', $cust_link ); + 'username', + 'name', + $groups_sub, + $goog_auth_sub, + $installer_sub, + $cust_sub, +); +my $align = 'lllccl'; +my @links = ( $link, $link, $link, '', '', '', $cust_link ); #if ( FS::Conf->new->config('ticket_system') ) { # push @header, 'Ticketing'; diff --git a/httemplate/edit/process/access_user.html b/httemplate/edit/process/access_user.html index c27262017..8e264c1a9 100644 --- a/httemplate/edit/process/access_user.html +++ b/httemplate/edit/process/access_user.html @@ -5,7 +5,7 @@ <% include( 'elements/process.html', 'table' => 'access_user', 'viewall_dir' => 'browse', - 'copy_on_empty' => [ '_password', '_password_encoding' ], + 'copy_on_empty' => [ '_password', '_password_encoding', 'totp_secret32' ], 'clear_on_error' => [ '_password', '_password2' ], 'process_m2m' => { 'link_table' => 'access_usergroup', 'target_table' => 'access_group', diff --git a/httemplate/loginout/login.html b/httemplate/loginout/login.html index 72e9525c4..1785ea796 100644 --- a/httemplate/loginout/login.html +++ b/httemplate/loginout/login.html @@ -27,6 +27,10 @@ Password: + + One-time code: + +
@@ -42,7 +46,7 @@ my %error = ( 'no_cookie' => '', #First login, don't display an error 'bad_cookie' => 'Bad Cookie', #timed out? - 'bad_credentials' => 'Incorrect username / password', + 'bad_credentials' => 'Incorrect username / password / one-time code', #'logout' => 'You have been logged out.', ); diff --git a/httemplate/pref/pref.html b/httemplate/pref/pref.html index 56fde6d44..5f68d3e46 100644 --- a/httemplate/pref/pref.html +++ b/httemplate/pref/pref.html @@ -29,6 +29,18 @@
+ <% emt('Google Authenticator') %> + + +% if ( $curuser->totp_secret32 ) { + +% } else { + +% } + +
Enable
+
+ % } <% emt("Interface") %> diff --git a/httemplate/pref/set_totp_secret32.html b/httemplate/pref/set_totp_secret32.html new file mode 100644 index 000000000..f5676bc38 --- /dev/null +++ b/httemplate/pref/set_totp_secret32.html @@ -0,0 +1,19 @@ +<& /elements/header.html, mt('Google Authenticator for [_1]', $FS::CurrentUser::CurrentUser->username) &> + +Scan this code with the Google Authenticator application on your phone. +

+ + +

+ +Future logins will require a 6-digit code generated by the application. + +<& /elements/footer.html &> +<%init> + +my $access_user = $FS::CurrentUser::CurrentUser; + +my $error = $access_user->set_totp_secret32 unless length($access_user->totp_secret32); +die $error if $error; #better error handling for this "shouldn't happen" case? + +