* Make auth methods pluggable.

* Move httpauth support to a plugin.
* Add an openid plugin to support logging in using OpenID.
master
joey 2006-11-20 01:52:18 +00:00
parent ad01bcd8b4
commit 54cf5a62ca
15 changed files with 245 additions and 33 deletions

View File

@ -120,7 +120,7 @@ sub cgi_signin ($$) { #{{{
error($@) if $@;
my $form = CGI::FormBuilder->new(
title => "signin",
fields => [qw(do title page subpage from name password)],
fields => [qw(do title page subpage from name password openid_url)],
header => 1,
charset => "utf-8",
method => 'POST',
@ -149,32 +149,56 @@ sub cgi_signin ($$) { #{{{
$form->field(name => "from", type => "hidden");
$form->field(name => "subpage", type => "hidden");
$form->field(name => "password", type => "password", required => 0);
if ($config{openid}) {
$form->field(name => "openid_url", label => "OpenID", comment => "to log in via OpenID");
}
else {
$form->field(name => "openid_url", type => "hidden");
}
if ($form->submitted eq "Register" || $form->submitted eq "Create Account") {
$form->title("register");
$form->text("");
$form->fields(qw(do title page subpage from name password confirm_password email));
$form->field(name => "confirm_password", type => "password");
$form->field(name => "email", type => "text");
$form->field(name => "openid_url", type => "hidden");
}
if ($q->param("do") ne "signin" && !$form->submitted) {
$form->text("You need to log in first.");
}
if ($form->submitted) {
my $submittype=$form->submitted;
# OpenID login uses the Login button, but validates
# differently.
if ($submittype eq "Login" && $config{openid} &&
length $form->field("openid_url")) {
$submittype="OpenID";
$form->field(
name => "openid_url",
validate => sub {
# FIXME: ugh
IkiWiki::Plugin::openid::validate($q, $session, $form, shift);
},
);
}
# Set required fields based on how form was submitted.
my %required=(
"Login" => [qw(name password)],
"Register" => [],
"Create Account" => [qw(name password confirm_password email)],
"Mail Password" => [qw(name)],
"OpenID" => [qw(openid_url)],
);
foreach my $opt (@{$required{$form->submitted}}) {
foreach my $opt (@{$required{$submittype}}) {
$form->field(name => $opt, required => 1);
}
# Validate password differently depending on how
# form was submitted.
if ($form->submitted eq 'Login') {
if ($submittype eq 'Login') {
$form->field(
name => "password",
validate => sub {
@ -184,13 +208,13 @@ sub cgi_signin ($$) { #{{{
);
$form->field(name => "name", validate => '/^\w+$/');
}
else {
elsif ($submittype ne 'OpenID') {
$form->field(name => "password", validate => 'VALUE');
}
# And make sure the entered name exists when logging
# in or sending email, and does not when registering.
if ($form->submitted eq 'Create Account' ||
$form->submitted eq 'Register') {
if ($submittype eq 'Create Account' ||
$submittype eq 'Register') {
$form->field(
name => "name",
validate => sub {
@ -201,7 +225,7 @@ sub cgi_signin ($$) { #{{{
},
);
}
else {
elsif ($submittype ne 'OpenID') {
$form->field(
name => "name",
validate => sub {
@ -229,8 +253,8 @@ sub cgi_signin ($$) { #{{{
do => $form->field("do"),
page => $form->field("page"),
title => $form->field("title"),
subpage => $form->field("subpage"),
from => $form->field("from"),
subpage => $form->field("subpage"),
));
}
else {
@ -680,11 +704,30 @@ sub cgi () { #{{{
{ FileName => "$config{wikistatedir}/sessions.db" });
umask($oldmask);
# Auth hooks can sign a user in.
if ($do ne 'signin' && ! defined $session->param("name")) {
run_hooks(auth => sub {
shift->($q, $session)
});
if (defined $session->param("name")) {
# Make sure whatever user was authed is in the
# userinfo db.
if (! userinfo_get($session->param("name"), "regdate")) {
userinfo_setall($session->param("name"), {
email => "",
password => "",
regdate => time,
});
}
}
}
# Everything below this point needs the user to be signed in.
if (((! $config{anonok} || $do eq 'prefs') &&
(! $config{httpauth}) &&
(! defined $session->param("name") ||
! userinfo_get($session->param("name"), "regdate"))) || $do eq 'signin') {
! userinfo_get($session->param("name"), "regdate")))
|| $do eq 'signin') {
cgi_signin($q, $session);
# Force session flush with safe umask.
@ -695,22 +738,6 @@ sub cgi () { #{{{
return;
}
if ($config{httpauth} && (! defined $session->param("name"))) {
if (! defined $q->remote_user()) {
error("Could not determine authenticated username.");
}
else {
$session->param("name", $q->remote_user());
if (! userinfo_get($session->param("name"), "regdate")) {
userinfo_setall($session->param("name"), {
email => "",
password => "",
regdate=>time,
});
}
}
}
if (defined $session->param("name") && userinfo_get($session->param("name"), "banned")) {
print $q->header(-status => "403 Forbidden");
$session->delete();

View File

@ -0,0 +1,22 @@
#!/usr/bin/perl
# HTTP basic auth plugin.
package IkiWiki::Plugin::httpauth;
use warnings;
use strict;
use IkiWiki;
sub import { #{{{
hook(type => "auth", id => "skeleton", call => \&auth);
} # }}}
sub auth ($$) { #{{{
my $cgi=shift;
my $session=shift;
if (defined $cgi->remote_user()) {
$session->param("name", $cgi->remote_user());
}
} #}}}
1

View File

@ -0,0 +1,101 @@
#!/usr/bin/perl
# OpenID support.
package IkiWiki::Plugin::openid;
use warnings;
use strict;
use IkiWiki;
sub import { #{{{
hook(type => "checkconfig", id => "smiley", call => \&checkconfig);
hook(type => "auth", id => "skeleton", call => \&auth);
} # }}}
sub checkconfig () { #{{{
# Currently part of the OpenID code is in CGI.pm, and is enabled by
# this setting.
# TODO: modularise it all out into this plugin..
$config{openid}=1;
} #}}}
sub auth ($$) { #{{{
my $q=shift;
my $session=shift;
if (defined $q->param('openid.mode')) {
my $csr=getobj($q, $session);
if (my $setup_url = $csr->user_setup_url) {
IkiWiki::redirect($q, $setup_url);
}
elsif ($csr->user_cancel) {
IkiWiki::redirect($q, $config{url});
}
elsif (my $vident = $csr->verified_identity) {
$session->param(name => $vident->url);
}
}
} #}}}
sub validate ($$$$) { #{{{
my $q=shift;
my $session=shift;
my $form=shift;
my $openid_url=shift;
my $csr=getobj($q, $session);
my $claimed_identity = $csr->claimed_identity($openid_url);
if (! $claimed_identity) {
# Put the error in the form and fail validation.
$form->field(name => "openid_url", comment => $csr->err);
return 0;
}
my $check_url = $claimed_identity->check_url(
return_to => IkiWiki::cgiurl(
do => $form->field("do"),
page => $form->field("page"),
title => $form->field("title"),
from => $form->field("from"),
subpage => $form->field("subpage")
),
trust_root => $config{cgiurl},
delayed_return => 1,
);
# Redirect the user to the OpenID server, which will
# eventually bounce them back to auth() above.
IkiWiki::redirect($q, $check_url);
exit 0;
} #}}}
sub getobj ($$) { #{{{
my $q=shift;
my $session=shift;
eval q{use Net::OpenID::Consumer};
error($@) if $@;
my $ua;
eval q{use LWPx::ParanoidAgent};
if (! $@) {
$ua=LWPx::ParanoidAgent->new;
}
else {
$ua=LWP::UserAgent->new;
}
# Store the secret in the session.
my $secret=$session->param("openid_secret");
if (! defined $secret) {
$secret=$session->param(openid_secret => time);
}
return Net::OpenID::Consumer->new(
ua => $ua,
args => $q,
consumer_secret => $secret,
required_root => $config{cgiurl},
);
} #}}}
1

View File

@ -20,6 +20,7 @@ sub import { #{{{
hook(type => "delete", id => "skeleton", call => \&delete);
hook(type => "change", id => "skeleton", call => \&change);
hook(type => "cgi", id => "skeleton", call => \&cgi);
hook(type => "auth", id => "skeleton", call => \&auth);
hook(type => "savestate", id => "savestate", call => \&savestate);
} # }}}
@ -95,6 +96,13 @@ sub cgi ($) { #{{{
debug("skeleton plugin running in cgi");
} #}}}
sub auth ($$) { #{{{
my $cgi=shift;
my $session=shift;
debug("skeleton plugin running in auth");
} #}}}
sub savestate () { #{{{
debug("skeleton plugin running in savestate");
} #}}}

View File

@ -205,3 +205,11 @@ li.L7 {
li.L8 {
list-style: upper-alpha;
}
input#openid_url {
background: url(http://openid.net/login-bg.gif) no-repeat;
background-color: #fff;
background-position: 0 50%;
color: #000;
padding-left: 18px;
}

8
debian/changelog vendored
View File

@ -1,3 +1,11 @@
ikiwiki (1.34) UNRELEASED; urgency=low
* Make auth methods pluggable.
* Move httpauth support to a plugin.
* Add an openid plugin to support logging in using OpenID.
-- Joey Hess <joeyh@debian.org> Sun, 19 Nov 2006 16:40:26 -0500
ikiwiki (1.33) unstable; urgency=low
* Fix issue with aggregate plugin updating expired pages.

2
debian/control vendored
View File

@ -11,7 +11,7 @@ Package: ikiwiki
Architecture: all
Depends: ${perl:Depends}, libxml-simple-perl, markdown, libtimedate-perl, libhtml-template-perl, libhtml-scrubber-perl, libcgi-formbuilder-perl (>= 3.02.02), libtime-duration-perl, libcgi-session-perl (>= 4.14-1), libmail-sendmail-perl, gcc | c-compiler, libc6-dev | libc-dev, libhtml-parser-perl, liburi-perl
Recommends: subversion | git-core | tla | mercurial, hyperestraier
Suggests: viewcvs, librpc-xml-perl, libtext-wikiformat-perl, python-docutils, polygen, tidy, libxml-feed-perl, libmailtools-perl, perlmagick, libfile-mimeinfo-perl
Suggests: viewcvs, librpc-xml-perl, libtext-wikiformat-perl, python-docutils, polygen, tidy, libxml-feed-perl, libmailtools-perl, perlmagick, libfile-mimeinfo-perl, libnet-openid-consumer-perl, libcrypt-ssleay-perl
Description: a wiki compiler
ikiwiki converts a directory full of wiki pages into html pages suitable
for publishing on a website. Unlike many wikis, ikiwiki does not have its

View File

@ -129,7 +129,11 @@ and can be enabled by enabling [[CGI]].
### User registration
Can optionally be configured to allow only registered users to post
pages; online user registration form, etc.
pages.
User registration can be done using a web form, or ikiwiki can be
configured to accept users authenticated with OpenID, or HTTP basic
authentication, or other methods implemented via plugins.
### Discussion pages

View File

@ -88,8 +88,6 @@ use IkiWiki::Setup::Standard {
#locale => 'en_US.UTF-8',
# Only send cookies over SSL connections.
#sslcookie => 1,
# Use HTTP Authentication instead of Ikiwiki's.
#httpauth => 1,
# Logging settings:
verbose => 0,
syslog => 0,
@ -97,7 +95,7 @@ use IkiWiki::Setup::Standard {
# To add plugins, list them here.
#add_plugins => [qw{meta tag pagecount brokenlinks search smiley
# wikitext camelcase pagestats htmltidy fortune
# sidebar map rst toc linkmap}],
# sidebar map rst toc linkmap openid}],
# If you want to disable any of the default plugins, list them here.
#disable_plugins => [qw{inline htmlscrubber}],

View File

@ -1,7 +1,8 @@
Most of ikiwiki's [[features]] are implemented as plugins. Beyond the
[[type/core]] features, there are plugins to [[type/format]] text,
use [[type/tags]], show [[type/meta]] information, do other [[type/useful]]
stuff, add [[type/chrome]] to the wiki, or just have [[type/fun]].
use [[type/tags]], show [[type/meta]] information, add [[type/auth]]
methods, do other [[type/useful]] stuff, add [[type/chrome]] to the
wiki, or just have [[type/fun]].
There's documentation if you want to [[write]] your own plugins, or you can
install and use plugins contributed by others.

View File

@ -0,0 +1,9 @@
[[template id=plugin name=httpauth included=1 author="Alec Berryman"]]
[[tag type/auth]]
This plugin allows HTTP basic authentication to be used to log into the
wiki. To use the plugin, your web server should be set up to perform HTTP
basic authentiation. The authenticated user will be automatically signed
into the wiki.
This plugin is included in ikiwiki, but is not enabled by default.

View File

@ -12,7 +12,6 @@ Hint: To limit the map to displaying pages less than a certian level deep,
use a [[PageSpec]] like this: `pages="* and !*/*/*"`
This plugin is included in ikiwiki, but is not enabled by default.
It was contributed by Alessandro Dotti Contra.
If this plugin is enabled, here is a page map for the plugins section
of this wiki:

View File

@ -0,0 +1,12 @@
[[template id=plugin name=openid included=1 author="[[Joey]]"]]
[[tag type/auth]]
This plugin allows users to use their [OpenID](http://openid.net/) to log
into the wiki.
The plugin needs the `Net::OpenID::Consumer` perl module. The
`LWPx::ParanoidAgent` perl module is used if available, for added
security. Finally, the `Crypt::SSLeay` perl module is needed to support
users entering "https" OpenID urls.
This plugin is included in ikiwiki, but is not enabled by default.

View File

@ -0,0 +1,2 @@
These plugins add different authentication methods for logging in to the
wiki.

View File

@ -175,6 +175,19 @@ called in turn, and passed a CGI object. The hook should examine the
parameters, and if it will handle this CGI request, output a page and
terminate the program.
### cgi
hook(type => "cgi", id => "foo", call => \&auth);
This hook can be used to implement a different authentication method than
the standard web form. When a user needs to be authenticated, each registered
auth hook is called in turn, and passed a CGI object and a session object.
If the hook is able to authenticate the user, it should set the session
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.
### savestate
hook(type => "savestate", id => "foo", call => \&savestate);