diff options
author | Joey Hess <joeyh@joeyh.name> | 2015-05-13 22:27:03 -0400 |
---|---|---|
committer | Joey Hess <joeyh@joeyh.name> | 2015-05-13 22:27:03 -0400 |
commit | 95e1e51caafbb3e4b179936b6d191ca87f47d4ae (patch) | |
tree | 00b33106e2a4030c1f5a237592b9a2f55a7020b0 | |
parent | f1d77f81933b4e4c38780a1934ac6daaaf0a9b80 (diff) | |
download | ikiwiki-95e1e51caafbb3e4b179936b6d191ca87f47d4ae.tar ikiwiki-95e1e51caafbb3e4b179936b6d191ca87f47d4ae.tar.gz |
emailauth link sent and verified; user login works
Still some work to do since the user name is an email address and should
not be leaked.
-rw-r--r-- | IkiWiki/Plugin/emailauth.pm | 113 | ||||
-rw-r--r-- | IkiWiki/Plugin/loginselector.pm | 15 | ||||
-rw-r--r-- | IkiWiki/Plugin/openid.pm | 2 | ||||
-rw-r--r-- | doc/plugins/emailauth.pm | 3 | ||||
-rw-r--r-- | templates/login-selector.tmpl | 3 |
5 files changed, 124 insertions, 12 deletions
diff --git a/IkiWiki/Plugin/emailauth.pm b/IkiWiki/Plugin/emailauth.pm index 3946ace03..3266e21ab 100644 --- a/IkiWiki/Plugin/emailauth.pm +++ b/IkiWiki/Plugin/emailauth.pm @@ -8,6 +8,7 @@ use IkiWiki 3.00; sub import { hook(type => "getsetup", id => "emailauth", "call" => \&getsetup); + hook(type => "cgi", id => "cgi", "call" => \&cgi); IkiWiki::loadplugin("loginselector"); IkiWiki::Plugin::loginselector::register_login_plugin( "emailauth", @@ -41,17 +42,119 @@ sub email_check_input ($) { && length $cgi->param('Email_entry'); } -sub email_auth ($$$) { +# Send login link to email. +sub email_auth ($$$$) { my $cgi=shift; my $session=shift; my $errordisplayer=shift; - - unless ($cgi->param('Email_entry') =~ /.\@./) { - $errordisplayer->("Invalid email address."); + my $infodisplayer=shift; + + my $email=$cgi->param('Email_entry'); + unless ($email =~ /.\@./) { + $errordisplayer->(gettext("Invalid email address.")); return; } - error "EMAIL AUTH"; + # Implicit account creation. + my $userinfo=IkiWiki::userinfo_retrieve(); + if (! exists $userinfo->{$email} || ! ref $userinfo->{$email}) { + IkiWiki::userinfo_setall($email, { + 'email' => $email, + 'regdate' => time, + }); + } + + my $token=gentoken($email); + my $template=template("emailauth.tmpl"); + $template->param( + wikiname => $config{wikiname}, + # Intentionally using short field names to keep link short. + authurl => IkiWiki::cgiurl_abs( + 'e' => $email, + 'v' => $token, + ), + ); + + eval q{use Mail::Sendmail}; + error($@) if $@; + sendmail( + To => $email, + From => "$config{wikiname} admin <". + (defined $config{adminemail} ? $config{adminemail} : "") + .">", + Subject => "$config{wikiname} login", + Message => $template->output, + ) or error(gettext("Failed to send mail")); + + $infodisplayer->(gettext("You have been sent an email, with a link you can open to complete the login process.")); +} + +# Finish login process. +sub cgi ($$) { + my $cgi=shift; + + my $email=$cgi->param('e'); + my $v=$cgi->param('v'); + if (defined $email && defined $v && length $email && length $v) { + # Need to lock the wiki before getting a session. + IkiWiki::lockwiki(); + IkiWiki::loadindex(); + my $session=IkiWiki::cgi_getsession(); + + my $token=gettoken($email); + if ($token eq $v) { + print STDERR "SUCCESS $email!!\n"; + cleartoken($email); + $session->param(name => $email); + my $nickname=$email; + $nickname=~s/@.*//; + $session->param(nickname => Encode::decode_utf8($nickname)); + IkiWiki::cgi_postsignin($cgi, $session); + } + elsif (length $token ne length $cgi->param('v')) { + error(gettext("Wrong login token length. Please check that you pasted in the complete login link from the email!")); + } + else { + loginfailure(); + } + } +} + +# Generates the token that will be used in the authurl to log the user in. +# This needs to be hard to guess, and relatively short. Generating a cgi +# session id will make it as hard to guess as any cgi session. +sub gentoken ($) { + my $email=shift; + eval q{use CGI::Session}; + error($@) if $@; + my $token = CGI::Session->new->id; + # Store token in userinfo; this allows the user to log in + # using a different browser session, if it takes a while for the + # email to get to them. + IkiWiki::userinfo_set($email, "emailauthexpire", time+(60*60*24)); + IkiWiki::userinfo_set($email, "emailauth", $token); + return $token; +} + +# Gets the token, checking for expiry. +sub gettoken ($) { + my $email=shift; + my $val=IkiWiki::userinfo_get($email, "emailauth"); + my $expire=IkiWiki::userinfo_get($email, "emailauthexpire"); + if (! length $val || time > $expire) { + loginfailure(); + } + return $val; +} + +sub cleartoken ($) { + my $email=shift; + IkiWiki::userinfo_set($email, "emailauthexpire", 0); + IkiWiki::userinfo_set($email, "emailauth", ""); +} + +sub loginfailure () { + error "Bad email authentication token. Please retry login."; } 1 diff --git a/IkiWiki/Plugin/loginselector.pm b/IkiWiki/Plugin/loginselector.pm index 1a322a53a..26c80b4ce 100644 --- a/IkiWiki/Plugin/loginselector.pm +++ b/IkiWiki/Plugin/loginselector.pm @@ -21,12 +21,13 @@ sub register_login_plugin ($$$$) { # This sub is passed a cgi object, and should return true # if it looks like the user is logging in using the plugin. my $plugin_check_input=shift; - # This sub is passed a cgi object, a session object, and an error - # display callback, and should handle the actual authentication. - # It can either exit w/o returning, if it is able to handle - # auth, or it can pass an error message to the error display - # callback to make the openid selector form be re-disiplayed with - # an error message on it. + # This sub is passed a cgi object, a session object, an error + # display callback, and an info display callback, and should + # handle the actual authentication. It can either exit w/o + # returning, if it is able to handle auth, or it can pass an + # error message to the error display callback to make the + # openid selector form be re-disiplayed with an error message + # on it. my $plugin_auth=shift; $login_plugins{$plugin_name}={ setup => $plugin_setup, @@ -56,6 +57,8 @@ sub login_selector { if ($login_plugins{$plugin}->{check_input}->($q)) { $login_plugins{$plugin}->{auth}->($q, $session, sub { $template->param(login_error => shift()); + }, sub { + $template->param(login_info => shift()); }); last; } diff --git a/IkiWiki/Plugin/openid.pm b/IkiWiki/Plugin/openid.pm index 67b8cd387..cc4b4ba3d 100644 --- a/IkiWiki/Plugin/openid.pm +++ b/IkiWiki/Plugin/openid.pm @@ -63,7 +63,7 @@ sub openid_check_input ($) { defined $q->param("action") && $q->param("action") eq "verify" && defined $openid_url && length $openid_url; } -sub openid_auth ($$$) { +sub openid_auth ($$$$) { my $q=shift; my $session=shift; my $errordisplayer=shift; diff --git a/doc/plugins/emailauth.pm b/doc/plugins/emailauth.pm index 9ba26e40c..8cb060e93 100644 --- a/doc/plugins/emailauth.pm +++ b/doc/plugins/emailauth.pm @@ -12,3 +12,6 @@ Users who have logged in using emailauth will have their email address used as their username. In places where the username is displayed, like the RecentChanges page, the domain will be omitted, to avoid exposing the user's email address. + +This plugin needs the [[!cpan Mail::SendMail]] perl module installed, +and able to send outgoing email. diff --git a/templates/login-selector.tmpl b/templates/login-selector.tmpl index 9b68838dc..3e7045c63 100644 --- a/templates/login-selector.tmpl +++ b/templates/login-selector.tmpl @@ -48,6 +48,9 @@ $(document).ready(function() { <TMPL_IF LOGIN_ERROR> <div class="error"><TMPL_VAR LOGIN_ERROR></div> </TMPL_IF> + <TMPL_IF LOGIN_INFO> + <TMPL_VAR LOGIN_INFO> + </TMPL_IF> </div> </form> |