aboutsummaryrefslogtreecommitdiff
path: root/IkiWiki/Plugin/loginselector.pm
blob: b5e2056a409a4351c9a01119e5226079c997364c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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