aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoey <joey@0fa5a96a-9a0e-0410-b3b2-a0fd24251071>2006-11-20 01:52:18 +0000
committerjoey <joey@0fa5a96a-9a0e-0410-b3b2-a0fd24251071>2006-11-20 01:52:18 +0000
commit54cf5a62cab254e923c8d73ae8bd043a1f33c3b1 (patch)
treebc30fa8f0ec7070eadb5dace8e9743272f742ba2
parentad01bcd8b49deed11f602c43d1da8235280d5d6b (diff)
downloadikiwiki-54cf5a62cab254e923c8d73ae8bd043a1f33c3b1.tar
ikiwiki-54cf5a62cab254e923c8d73ae8bd043a1f33c3b1.tar.gz
* Make auth methods pluggable.
* Move httpauth support to a plugin. * Add an openid plugin to support logging in using OpenID.
-rw-r--r--IkiWiki/CGI.pm77
-rw-r--r--IkiWiki/Plugin/httpauth.pm22
-rw-r--r--IkiWiki/Plugin/openid.pm101
-rw-r--r--IkiWiki/Plugin/skeleton.pm8
-rw-r--r--basewiki/style.css8
-rw-r--r--debian/changelog8
-rw-r--r--debian/control2
-rw-r--r--doc/features.mdwn6
-rw-r--r--doc/ikiwiki.setup4
-rw-r--r--doc/plugins.mdwn5
-rw-r--r--doc/plugins/httpauth.mdwn9
-rw-r--r--doc/plugins/map.mdwn1
-rw-r--r--doc/plugins/openid.mdwn12
-rw-r--r--doc/plugins/type/auth.mdwn2
-rw-r--r--doc/plugins/write.mdwn13
15 files changed, 245 insertions, 33 deletions
diff --git a/IkiWiki/CGI.pm b/IkiWiki/CGI.pm
index ce4b6ada1..def0549c5 100644
--- a/IkiWiki/CGI.pm
+++ b/IkiWiki/CGI.pm
@@ -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();
diff --git a/IkiWiki/Plugin/httpauth.pm b/IkiWiki/Plugin/httpauth.pm
new file mode 100644
index 000000000..336eb793a
--- /dev/null
+++ b/IkiWiki/Plugin/httpauth.pm
@@ -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
diff --git a/IkiWiki/Plugin/openid.pm b/IkiWiki/Plugin/openid.pm
new file mode 100644
index 000000000..55b1c4b17
--- /dev/null
+++ b/IkiWiki/Plugin/openid.pm
@@ -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
diff --git a/IkiWiki/Plugin/skeleton.pm b/IkiWiki/Plugin/skeleton.pm
index acac16c1a..f3244ae14 100644
--- a/IkiWiki/Plugin/skeleton.pm
+++ b/IkiWiki/Plugin/skeleton.pm
@@ -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");
} #}}}
diff --git a/basewiki/style.css b/basewiki/style.css
index 0334670e1..032665e70 100644
--- a/basewiki/style.css
+++ b/basewiki/style.css
@@ -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;
+}
diff --git a/debian/changelog b/debian/changelog
index afa0d9228..f261f92c9 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -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.
diff --git a/debian/control b/debian/control
index 074a2922e..710e24185 100644
--- a/debian/control
+++ b/debian/control
@@ -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
diff --git a/doc/features.mdwn b/doc/features.mdwn
index cb118f27d..05aeebfc9 100644
--- a/doc/features.mdwn
+++ b/doc/features.mdwn
@@ -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
diff --git a/doc/ikiwiki.setup b/doc/ikiwiki.setup
index 7cff65c5b..6eb6446e0 100644
--- a/doc/ikiwiki.setup
+++ b/doc/ikiwiki.setup
@@ -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}],
diff --git a/doc/plugins.mdwn b/doc/plugins.mdwn
index 45e380d58..6de17adc7 100644
--- a/doc/plugins.mdwn
+++ b/doc/plugins.mdwn
@@ -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.
diff --git a/doc/plugins/httpauth.mdwn b/doc/plugins/httpauth.mdwn
new file mode 100644
index 000000000..98233570c
--- /dev/null
+++ b/doc/plugins/httpauth.mdwn
@@ -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.
diff --git a/doc/plugins/map.mdwn b/doc/plugins/map.mdwn
index 01f61e5b3..592a20706 100644
--- a/doc/plugins/map.mdwn
+++ b/doc/plugins/map.mdwn
@@ -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:
diff --git a/doc/plugins/openid.mdwn b/doc/plugins/openid.mdwn
new file mode 100644
index 000000000..344be7de3
--- /dev/null
+++ b/doc/plugins/openid.mdwn
@@ -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.
diff --git a/doc/plugins/type/auth.mdwn b/doc/plugins/type/auth.mdwn
new file mode 100644
index 000000000..a6ae5e4ea
--- /dev/null
+++ b/doc/plugins/type/auth.mdwn
@@ -0,0 +1,2 @@
+These plugins add different authentication methods for logging in to the
+wiki.
diff --git a/doc/plugins/write.mdwn b/doc/plugins/write.mdwn
index 5cace0911..7c4da8d5f 100644
--- a/doc/plugins/write.mdwn
+++ b/doc/plugins/write.mdwn
@@ -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);