X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/a2e78ebcf27680eedf30e00f1ee9b86ed1d9e127..2b31e0bed7cf4cef13d88af049b18c3731c32528:/IkiWiki/Plugin/passwordauth.pm?ds=sidebyside

diff --git a/IkiWiki/Plugin/passwordauth.pm b/IkiWiki/Plugin/passwordauth.pm
index b3a798055..cfa3ad418 100644
--- a/IkiWiki/Plugin/passwordauth.pm
+++ b/IkiWiki/Plugin/passwordauth.pm
@@ -19,6 +19,7 @@ sub getsetup () {
 		plugin => {
 			safe => 1,
 			rebuild => 0,
+			section => "auth",
 		},
 		account_creation_password => {
 			type => "string",
@@ -95,6 +96,72 @@ sub setpassword ($$;$) {
 	else {
 		IkiWiki::userinfo_set($user, $field, $password);
 	}
+
+	# Setting the password clears any passwordless login token.
+	if ($field ne 'passwordless') {
+		IkiWiki::userinfo_set($user, "passwordless", "");
+	}
+}
+
+# Generates a token that can be used to log the user in.
+# This needs to be hard to guess. Generating a cgi session id will
+# make it as hard to guess as any cgi session.
+sub gentoken ($$;$) {
+	my $user=shift;
+	my $tokenfield=shift;
+	my $reversable=shift;
+
+	eval q{use CGI::Session};
+	error($@) if $@;
+	my $token = CGI::Session->new("driver:DB_File", undef, {FileName => "/dev/null"})->id;
+	if (! $reversable) {
+		setpassword($user, $token, $tokenfield);
+	}
+	else {
+		IkiWiki::userinfo_set($user, $tokenfield, $token);
+	}
+	return $token;
+}
+
+# An anonymous user has no normal password, only a passwordless login
+# token. Given an email address, this sets up such a user for that email,
+# unless one already exists, and returns the username.
+sub anonuser ($) {
+	my $email=shift;
+
+	# Want a username for this email that won't overlap with any other.
+	my $user=$email;
+	$user=~s/@/_/g;
+
+	my $userinfo=IkiWiki::userinfo_retrieve();
+	if (! exists $userinfo->{$user} || ! ref $userinfo->{$user}) {
+		if (IkiWiki::userinfo_setall($user, {
+		    	'email' => $email,
+			'regdate' => time})) {
+			gentoken($user, "passwordless", 1);
+			return $user;
+		}
+		else {
+			error(gettext("Error creating account."));
+		}
+	}
+	elsif (defined anonusertoken($userinfo->{$user})) {
+		return $user;
+	}
+	else {
+		return undef;
+	}
+}
+
+sub anonusertoken ($) {
+	my $userhash=shift;
+	if (exists $userhash->{passwordless} &&
+	    length $userhash->{passwordless}) {
+		return $userhash->{passwordless};
+	}
+	else {
+		return undef;
+	}
 }
 
 sub formbuilder_setup (@) {
@@ -104,11 +171,13 @@ sub formbuilder_setup (@) {
 	my $session=$params{session};
 	my $cgi=$params{cgi};
 
-	if ($form->title eq "signin" || $form->title eq "register" || $cgi->param("do") eq "register") {
+	my $do_register=defined $cgi->param("do") && $cgi->param("do") eq "register";
+
+	if ($form->title eq "signin" || $form->title eq "register" || $do_register) {
 		$form->field(name => "name", required => 0);
 		$form->field(name => "password", type => "password", required => 0);
 		
-		if ($form->submitted eq "Register" || $form->submitted eq "Create Account" || $cgi->param("do") eq "register") {
+		if ($form->submitted eq "Register" || $form->submitted eq "Create Account" || $do_register) {
 			$form->field(name => "confirm_password", type => "password");
 			$form->field(name => "account_creation_password", type => "password")
 				 if (defined $config{account_creation_password} &&
@@ -162,7 +231,7 @@ sub formbuilder_setup (@) {
 				$form->field(
 					name => "password",
 					validate => sub {
-						checkpassword($form->field("name"), shift);
+						checkpassword(scalar $form->field("name"), shift);
 					},
 				);
 			}
@@ -182,6 +251,12 @@ sub formbuilder_setup (@) {
 						my $name=shift;
 						length $name &&
 						$name=~/$config{wiki_file_regexp}/ &&
+						# don't allow registering
+						# accounts that look like
+						# openids, or email
+						# addresses, even if the
+						# file regexp allows it
+						$name!~/[\/:\@]/ &&
 						! IkiWiki::userinfo_get($name, "regdate");
 					},
 				);
@@ -208,7 +283,7 @@ sub formbuilder_setup (@) {
 	}
 	elsif ($form->title eq "preferences") {
 		my $user=$session->param("name");
-		if (! IkiWiki::openiduser($user)) {
+		if (! IkiWiki::openiduser($user) && ! IkiWiki::emailuser($user)) {
 			$form->field(name => "name", disabled => 1, 
 				value => $user, force => 1,
 				fieldset => "login");
@@ -223,14 +298,14 @@ sub formbuilder_setup (@) {
 					shift eq $form->field("password");
 				});
 			
-			my $userpage=$config{userdir} ? $config{userdir}."/".$user : $user;
+			my $userpage=IkiWiki::userpage($user);
 			if (exists $pagesources{$userpage}) {
 				$form->text(gettext("Your user page: ").
 					htmllink("", "", $userpage,
 						noimageinline => 1));
 			}
 			else {
-				$form->text("<a href=\"".
+				$form->text("<a rel=\"nofollow\" href=\"".
 					IkiWiki::cgiurl(do => "edit", page => $userpage).
 					"\">".gettext("Create your user page")."</a>");
 			}
@@ -246,18 +321,24 @@ sub formbuilder (@) {
 	my $cgi=$params{cgi};
 	my $buttons=$params{buttons};
 
+	my $do_register=defined $cgi->param("do") && $cgi->param("do") eq "register";
+
 	if ($form->title eq "signin" || $form->title eq "register") {
-		if (($form->submitted && $form->validate) || $cgi->param("do") eq "register") {
+		if (($form->submitted && $form->validate) || $do_register) {
+			my $user_name = $form->field('name');
+
 			if ($form->submitted eq 'Login') {
-				$session->param("name", $form->field("name"));
+				$session->param("name", $user_name);
 				IkiWiki::cgi_postsignin($cgi, $session);
 			}
 			elsif ($form->submitted eq 'Create Account') {
-				my $user_name=$form->field('name');
+				my $email = $form->field('email');
+				my $password = $form->field('password');
+
 				if (IkiWiki::userinfo_setall($user_name, {
-				    	'email' => $form->field('email'),
+					'email' => $email,
 					'regdate' => time})) {
-					setpassword($user_name, $form->field('password'));
+					setpassword($user_name, $password);
 					$form->field(name => "confirm_password", type => "hidden");
 					$form->field(name => "email", type => "hidden");
 					$form->text(gettext("Account creation successful. Now you can Login."));
@@ -267,32 +348,24 @@ sub formbuilder (@) {
 				}
 			}
 			elsif ($form->submitted eq 'Reset Password') {
-				my $user_name=$form->field("name");
 				my $email=IkiWiki::userinfo_get($user_name, "email");
 				if (! length $email) {
 					error(gettext("No email address, so cannot email password reset instructions."));
 				}
-				
-				# Store a token that can be used once
-				# to log the user in. This needs to be hard
-				# to guess. Generating a cgi session id will
-				# make it as hard to guess as any cgi session.
-				eval q{use CGI::Session};
-				error($@) if $@;
-				my $token = CGI::Session->new->id;
-				setpassword($user_name, $token, "resettoken");
+
+				my $token=gentoken($user_name, "resettoken");
 				
 				my $template=template("passwordmail.tmpl");
 				$template->param(
 					user_name => $user_name,
-					passwordurl => IkiWiki::cgiurl(
+					passwordurl => IkiWiki::cgiurl_abs_samescheme(
 						'do' => "reset",
 						'name' => $user_name,
 						'token' => $token,
 					),
 					wikiurl => $config{url},
 					wikiname => $config{wikiname},
-					REMOTE_ADDR => $ENV{REMOTE_ADDR},
+					remote_addr => $session->remote_addr(),
 				);
 				
 				eval q{use Mail::Sendmail};
@@ -310,7 +383,7 @@ sub formbuilder (@) {
 				$form->field(name => "name", required => 0);
 				push @$buttons, "Reset Password";
 			}
-			elsif ($form->submitted eq "Register" || $cgi->param("do") eq "register") {
+			elsif ($form->submitted eq "Register" || $do_register) {
 				@$buttons="Create Account";
 			}
 		}
@@ -324,8 +397,9 @@ sub formbuilder (@) {
 	elsif ($form->title eq "preferences") {
 		if ($form->submitted eq "Save Preferences" && $form->validate) {
 			my $user_name=$form->field('name');
-			if ($form->field("password") && length $form->field("password")) {
-				setpassword($user_name, $form->field('password'));
+			my $password=$form->field('password');
+			if (defined $password && length $password) {
+				setpassword($user_name, $password);
 			}
 		}
 	}
@@ -351,6 +425,22 @@ sub sessioncgi ($$) {
 		IkiWiki::cgi_prefs($q, $session);
 		exit;
 	}
+	elsif ($q->param('do') eq 'tokenauth') {
+		my $name=$q->param("name");
+		my $token=$q->param("token");
+
+		if (! defined $name || ! defined $token ||
+		    ! length $name  || ! length $token) {
+			error(gettext("incorrect url"));
+	 	}
+		if (! checkpassword($name, $token, "passwordless")) {
+			error(gettext("access denied"));
+		}
+
+		$session->param("name", $name);
+		IkiWiki::cgi_prefs($q, $session);
+		exit;
+	}
 	elsif ($q->param("do") eq "register") {
 		# After registration, need to go somewhere, so show prefs page.
 		$session->param(postsignin => "do=prefs");