* Move httpauth support to a plugin.
* Add an openid plugin to support logging in using OpenID.
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',
$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 {
);
$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 {
},
);
}
- else {
+ elsif ($submittype ne 'OpenID') {
$form->field(
name => "name",
validate => sub {
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 {
{ 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.
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();
--- /dev/null
+#!/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
--- /dev/null
+#!/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
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);
} # }}}
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");
} #}}}
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;
+}
+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.
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
### 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
#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,
# 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}],
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.
--- /dev/null
+[[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.
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:
--- /dev/null
+[[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.
--- /dev/null
+These plugins add different authentication methods for logging in to the
+wiki.
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);