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