* Use fieldsets in the preferences form to group related options together.
[ikiwiki] / IkiWiki / Plugin / openid.pm
1 #!/usr/bin/perl
2 # OpenID support.
3 package IkiWiki::Plugin::openid;
4
5 use warnings;
6 use strict;
7 use IkiWiki 2.00;
8
9 sub import { #{{{
10         hook(type => "getopt", id => "openid", call => \&getopt);
11         hook(type => "auth", id => "openid", call => \&auth);
12         hook(type => "formbuilder_setup", id => "openid",
13                 call => \&formbuilder_setup, last => 1);
14 } # }}}
15
16 sub getopt () { #{{{
17         eval q{use Getopt::Long};
18         error($@) if $@;
19         Getopt::Long::Configure('pass_through');
20         GetOptions("openidsignup=s" => \$config{openidsignup});
21 } #}}}
22
23 sub formbuilder_setup (@) { #{{{
24         my %params=@_;
25
26         my $form=$params{form};
27         my $session=$params{session};
28         my $cgi=$params{cgi};
29
30         if ($form->title eq "signin") {
31                 $form->field(
32                         name => "openid_url",
33                         label => "OpenID",
34                         size => 30,
35                         comment => '('.
36                                 htmllink("", "", "OpenID", noimageinline => 1, linktext => gettext("What's this?"))
37                                 .($config{openidsignup} ? " | <a href=\"$config{openidsignup}\">".gettext("Get an OpenID")."</a>" : "")
38                                 .')'
39                 );
40
41                 # Handle submission of an OpenID as validation.
42                 if ($form->submitted && $form->submitted eq "Login" &&
43                     defined $form->field("openid_url") && 
44                     length $form->field("openid_url")) {
45                         $form->field(
46                                 name => "openid_url",
47                                 validate => sub {
48                                         validate($cgi, $session, shift, $form);
49                                 },
50                         );
51                         # Skip all other required fields in this case.
52                         foreach my $field ($form->field) {
53                                 next if $field eq "openid_url";
54                                 $form->field(name => $field, required => 0,
55                                         validate => '/.*/');
56                         }
57                 }
58         }
59         elsif ($form->title eq "preferences") {
60                 if (! defined $form->field(name => "name")) {
61                         $form->field(name => "OpenID", disabled => 1,
62                                 value => $session->param("name"), 
63                                 size => 50, force => 1,
64                                 fieldset => "login");
65                 }
66         }
67 }
68
69 sub validate ($$$;$) { #{{{
70         my $q=shift;
71         my $session=shift;
72         my $openid_url=shift;
73         my $form=shift;
74
75         my $csr=getobj($q, $session);
76
77         my $claimed_identity = $csr->claimed_identity($openid_url);
78         if (! $claimed_identity) {
79                 if ($form) {
80                         # Put the error in the form and fail validation.
81                         $form->field(name => "openid_url", comment => $csr->err);
82                         return 0;
83                 }
84                 else {
85                         error($csr->err);
86                 }
87         }
88
89         my $check_url = $claimed_identity->check_url(
90                 return_to => IkiWiki::cgiurl(do => "postsignin"),
91                 trust_root => $config{cgiurl},
92                 delayed_return => 1,
93         );
94         # Redirect the user to the OpenID server, which will
95         # eventually bounce them back to auth()
96         IkiWiki::redirect($q, $check_url);
97         exit 0;
98 } #}}}
99
100 sub auth ($$) { #{{{
101         my $q=shift;
102         my $session=shift;
103
104         if (defined $q->param('openid.mode')) {
105                 my $csr=getobj($q, $session);
106
107                 if (my $setup_url = $csr->user_setup_url) {
108                         IkiWiki::redirect($q, $setup_url);
109                 }
110                 elsif ($csr->user_cancel) {
111                         IkiWiki::redirect($q, $config{url});
112                 }
113                 elsif (my $vident = $csr->verified_identity) {
114                         $session->param(name => $vident->url);
115                 }
116                 else {
117                         error("OpenID failure: ".$csr->err);
118                 }
119         }
120         elsif (defined $q->param('openid_identifier')) {
121                 # myopenid.com affiliate support
122                 validate($q, $session, $q->param('openid_identifier'));
123         }
124 } #}}}
125
126 sub getobj ($$) { #{{{
127         my $q=shift;
128         my $session=shift;
129
130         eval q{use Net::OpenID::Consumer};
131         error($@) if $@;
132
133         my $ua;
134         eval q{use LWPx::ParanoidAgent};
135         if (! $@) {
136                 $ua=LWPx::ParanoidAgent->new;
137         }
138         else {
139                 $ua=LWP::UserAgent->new;
140         }
141
142         # Store the secret in the session.
143         my $secret=$session->param("openid_secret");
144         if (! defined $secret) {
145                 $secret=rand;
146                 $session->param(openid_secret => $secret);
147         }
148
149         return Net::OpenID::Consumer->new(
150                 ua => $ua,
151                 args => $q,
152                 consumer_secret => sub { return shift()+$secret },
153                 required_root => $config{cgiurl},
154         );
155 } #}}}
156
157 1