From 5b459737a50d83ff94490d86d1b9a4438d4b50a1 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 13 May 2015 18:37:24 -0400 Subject: Converted openid-selector into a more generic loginselector helper plugin. --- IkiWiki/Plugin/loginselector.pm | 121 ++++++++++++++++++++++++++++++++++++++++ IkiWiki/Plugin/openid.pm | 85 ++++++++++------------------ debian/changelog | 2 + doc/plugins/write.mdwn | 3 + templates/login-selector.tmpl | 28 +++++----- 5 files changed, 169 insertions(+), 70 deletions(-) create mode 100644 IkiWiki/Plugin/loginselector.pm diff --git a/IkiWiki/Plugin/loginselector.pm b/IkiWiki/Plugin/loginselector.pm new file mode 100644 index 000000000..b5e2056a4 --- /dev/null +++ b/IkiWiki/Plugin/loginselector.pm @@ -0,0 +1,121 @@ +#!/usr/bin/perl +package IkiWiki::Plugin::loginselector; + +use warnings; +use strict; +use IkiWiki 3.00; + +# Plugins that provide login methods can register themselves here. +# Note that the template and js file also have be be modifed to add a new +# login method. +our %login_plugins; + +sub register_login_plugin ($$$$) { + # Same as the name of the plugin that is registering itself as a + # login plugin. eg, "openid" + my $plugin_name=shift; + # This sub is passed a cgi object and a template object which it + # can manipulate. It should return true if the plugin can be used + # (it might load necessary modules for auth checking, for example). + my $plugin_setup=shift; + # 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. + my $plugin_auth=shift; + $login_plugins{$plugin_name}={ + setup => $plugin_setup, + check_input => $plugin_check_input, + auth => $plugin_auth, + }; +} + +sub login_selector { + my $real_cgi_signin=shift; + my $otherform_label=shift; + my $q=shift; + my $session=shift; + + my $template=IkiWiki::template("login-selector.tmpl"); + + foreach my $plugin (keys %login_plugins) { + if (! $login_plugins{$plugin}->{setup}->($template)) { + delete $login_plugins{$plugin}; + } + else { + $template->param("login_selector_$plugin", 1); + } + } + + foreach my $plugin (keys %login_plugins) { + if ($login_plugins{$plugin}->{check_input}->($q)) { + $login_plugins{$plugin}->{auth}->($q, $session, sub { + $template->param(login_error => shift()); + }); + last; + } + } + + $template->param( + cgiurl => IkiWiki::cgiurl(), + ($real_cgi_signin ? (otherform => $real_cgi_signin->($q, $session, 1)) : ()), + otherform_label => $otherform_label, + ); + + IkiWiki::printheader($session); + print IkiWiki::cgitemplate($q, "signin", $template->output); + exit; +} + +sub import { + add_underlay("login-selector"); + add_underlay("jquery"); + hook(type => "getsetup", id => "loginselector", call => \&getsetup); + hook(type => "checkconfig", id => "loginselector", call => \&checkconfig); +} + +sub checkconfig () { + if ($config{cgi}) { + # Intercept normal signin form, so the login selector + # can be displayed. + # + # When other auth hooks are registered, give the selector + # a reference to the normal signin form. + require IkiWiki::CGI; + my $real_cgi_signin; + my $otherform_label=gettext("Other"); + if (keys %{$IkiWiki::hooks{auth}} > 1) { + $real_cgi_signin=\&IkiWiki::cgi_signin; + my %h=%{$IkiWiki::hooks{auth}}; + foreach my $p (keys %login_plugins) { + delete $h{$p}; + } + # Special case to avoid labeling password auth as + # "Other" when it's the only auth plugin not + # integrated with the loginselector. + if (keys %h == 1 && exists $h{passwordauth}) { + $otherform_label=gettext("Password"); + } + } + inject(name => "IkiWiki::cgi_signin", call => sub ($$) { + login_selector($real_cgi_signin, $otherform_label, @_); + }); + } +} + +sub getsetup () { + return + plugin => { + # this plugin is safe but only makes sense as a + # dependency + safe => 0, + rebuild => 0, + }, +} + +1 diff --git a/IkiWiki/Plugin/openid.pm b/IkiWiki/Plugin/openid.pm index a12c83b8c..67b8cd387 100644 --- a/IkiWiki/Plugin/openid.pm +++ b/IkiWiki/Plugin/openid.pm @@ -7,38 +7,17 @@ use strict; use IkiWiki 3.00; sub import { - add_underlay("login-selector"); - add_underlay("jquery"); - hook(type => "checkconfig", id => "openid", call => \&checkconfig); hook(type => "getsetup", id => "openid", call => \&getsetup); hook(type => "auth", id => "openid", call => \&auth); hook(type => "formbuilder_setup", id => "openid", call => \&formbuilder_setup, last => 1); -} - -sub checkconfig () { - if ($config{cgi}) { - # Intercept normal signin form, so the openid selector - # can be displayed. - # - # When other auth hooks are registered, give the selector - # a reference to the normal signin form. - require IkiWiki::CGI; - my $real_cgi_signin; - my $otherform_label=gettext("Other"); - if (keys %{$IkiWiki::hooks{auth}} > 1) { - $real_cgi_signin=\&IkiWiki::cgi_signin; - my %h=%{$IkiWiki::hooks{auth}}; - delete $h{openid}; - delete $h{emailauth}; - if (keys %h == 1 && exists $h{passwordauth}) { - $otherform_label=gettext("Password"); - } - } - inject(name => "IkiWiki::cgi_signin", call => sub ($$) { - openid_selector($real_cgi_signin, $otherform_label, @_); - }); - } + IkiWiki::loadplugin("loginselector"); + IkiWiki::Plugin::loginselector::register_login_plugin( + "openid", + \&openid_setup, + \&openid_check_input, + \&openid_auth, + ); } sub getsetup () { @@ -62,40 +41,34 @@ sub getsetup () { }, } -sub openid_selector { - my $real_cgi_signin=shift; - my $otherform_label=shift; - my $q=shift; - my $session=shift; - - my $template=IkiWiki::template("login-selector.tmpl"); - my $openid_url=$q->param('openid_identifier'); +sub openid_setup ($$) { + my $q=shift; + my $template=shift; - if (! load_openid_module()) { - if ($real_cgi_signin) { - $real_cgi_signin->($q, $session); - exit; + if (load_openid_module()) { + my $openid_url=$q->param('openid_identifier'); + if (defined $openid_url) { + $template->param(openid_url => $openid_url); } - error(sprintf(gettext("failed to load openid module: "), @_)); + return 1; } - elsif (defined $q->param("action") && $q->param("action") eq "verify" && defined $openid_url && length $openid_url) { - validate($q, $session, $openid_url, sub { - $template->param(login_error => shift()) - }); + else { + return 0; } +} - $template->param( - cgiurl => IkiWiki::cgiurl(), - (defined $openid_url ? (openid_url => $openid_url) : ()), - ($real_cgi_signin ? (otherform => $real_cgi_signin->($q, $session, 1)) : ()), - otherform_label => $otherform_label, - login_selector_openid => 1, - login_selector_email => 1, - ); +sub openid_check_input ($) { + my $q=shift; + my $openid_url=$q->param('openid_identifier'); + defined $q->param("action") && $q->param("action") eq "verify" && defined $openid_url && length $openid_url; +} - IkiWiki::printheader($session); - print IkiWiki::cgitemplate($q, "signin", $template->output); - exit; +sub openid_auth ($$$) { + my $q=shift; + my $session=shift; + my $errordisplayer=shift; + my $openid_url=$q->param('openid_identifier'); + validate($q, $session, $openid_url, $errordisplayer); } sub formbuilder_setup (@) { diff --git a/debian/changelog b/debian/changelog index 4d8b589cf..30b358ca6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,8 @@ ikiwiki (3.20150330) UNRELEASED; urgency=medium * When openid and passwordauth are the only enabled auth plugins, make the openid selector display "Password" instead of "Other", so users are more likely to click on it when they don't have an openid. + * Converted openid-selector into a more generic loginselector helper + plugin. -- Joey Hess Tue, 28 Apr 2015 12:24:08 -0400 diff --git a/doc/plugins/write.mdwn b/doc/plugins/write.mdwn index c3f531b66..95f4a39df 100644 --- a/doc/plugins/write.mdwn +++ b/doc/plugins/write.mdwn @@ -458,6 +458,9 @@ object's "name" parameter to the authenticated user's name. Note that if the name is set to the name of a user who is not registered, a basic registration of the user will be automatically performed. +Auth plugins can use the loginselector helper plugin to let the user +select which authentication method to use. + ### sessioncgi hook(type => "sessioncgi", id => "foo", call => \&sessioncgi); diff --git a/templates/login-selector.tmpl b/templates/login-selector.tmpl index e3a91fef2..9b68838dc 100644 --- a/templates/login-selector.tmpl +++ b/templates/login-selector.tmpl @@ -6,7 +6,7 @@ $(document).ready(function() { 'openid_identifier', { 'openid': 1, - 'email': 1, + 'email': 1, }, 'otherform', '' @@ -27,23 +27,23 @@ $(document).ready(function() {
-
-

OpenId login:

- - - +
+

OpenId login:

+ + + +
-
-
-

Email login:

- - - - + +
+

Email login:

+ + + +
-
-- cgit v1.2.3