summaryrefslogtreecommitdiffstats
path: root/extensions/Persona
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/Persona')
-rw-r--r--extensions/Persona/Config.pm29
-rw-r--r--extensions/Persona/Extension.pm73
-rw-r--r--extensions/Persona/TODO19
-rw-r--r--extensions/Persona/lib/Config.pm41
-rw-r--r--extensions/Persona/lib/Login.pm127
-rw-r--r--extensions/Persona/template/en/default/admin/params/browserid.html.tmpl22
-rw-r--r--extensions/Persona/template/en/default/admin/params/persona.html.tmpl24
-rw-r--r--extensions/Persona/template/en/default/hook/account/auth/login-additional_methods.html.tmpl6
-rw-r--r--extensions/Persona/template/en/default/hook/account/auth/login-small-additional_methods.html.tmpl17
-rw-r--r--extensions/Persona/template/en/default/hook/account/create-additional_methods.html.tmpl13
-rw-r--r--extensions/Persona/template/en/default/hook/global/header-additional_header.html.tmpl87
-rw-r--r--extensions/Persona/template/en/default/hook/global/user-error-errors.html.tmpl12
-rw-r--r--extensions/Persona/web/images/persona_sign_in.pngbin0 -> 3684 bytes
-rw-r--r--extensions/Persona/web/images/sign_in.pngbin0 -> 1993 bytes
14 files changed, 470 insertions, 0 deletions
diff --git a/extensions/Persona/Config.pm b/extensions/Persona/Config.pm
new file mode 100644
index 000000000..8709655d1
--- /dev/null
+++ b/extensions/Persona/Config.pm
@@ -0,0 +1,29 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::Persona;
+use strict;
+
+use constant NAME => 'Persona';
+
+use constant REQUIRED_MODULES => [
+ {
+ package => 'JSON',
+ module => 'JSON',
+ version => 0,
+ },
+ {
+ package => 'libwww-perl',
+ module => 'LWP::UserAgent',
+ version => 0,
+ },
+];
+
+use constant OPTIONAL_MODULES => [
+];
+
+__PACKAGE__->NAME;
diff --git a/extensions/Persona/Extension.pm b/extensions/Persona/Extension.pm
new file mode 100644
index 000000000..f288702e8
--- /dev/null
+++ b/extensions/Persona/Extension.pm
@@ -0,0 +1,73 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::Persona;
+use strict;
+use base qw(Bugzilla::Extension);
+
+use Bugzilla::Config qw(SetParam write_params);
+
+our $VERSION = '0.01';
+
+sub install_update_db {
+ # The extension changed from BrowserID to Persona
+ # so we need to update user_info_class if this system
+ # was using BrowserID for verification.
+ my $params = Bugzilla->params || Bugzilla::Config::read_param_file();
+ my $user_info_class = $params->{'user_info_class'};
+ if ($user_info_class =~ /BrowserID/) {
+ $user_info_class =~ s/BrowserID/Persona/;
+ SetParam('user_info_class', $user_info_class);
+ write_params();
+ }
+}
+
+sub auth_login_methods {
+ my ($self, $args) = @_;
+ my $modules = $args->{'modules'};
+ if (exists($modules->{'Persona'})) {
+ $modules->{'Persona'} = 'Bugzilla/Extension/Persona/Login.pm';
+ }
+}
+
+sub config_modify_panels {
+ my ($self, $args) = @_;
+ my $panels = $args->{'panels'};
+ my $auth_panel_params = $panels->{'auth'}->{'params'};
+
+ my ($user_info_class) =
+ grep { $_->{'name'} eq 'user_info_class' } @$auth_panel_params;
+
+ if ($user_info_class) {
+ push(@{ $user_info_class->{'choices'} }, "Persona,CGI");
+ }
+
+ # The extension changed from BrowserID to Persona
+ # so we need to retain the current values for the new
+ # params that will be created.
+ my $params = Bugzilla->params || Bugzilla::Config::read_param_file();
+ my $verify_url = $params->{'browserid_verify_url'};
+ my $includejs_url = $params->{'browserid_includejs_url'};
+ if ($verify_url && $includejs_url) {
+ foreach my $param (@{ $panels->{'persona'}->{'params'} }) {
+ if ($param->{'name'} eq 'persona_verify_url') {
+ $param->{'default'} = $verify_url;
+ }
+ if ($param->{'name'} eq 'persona_includejs_url') {
+ $param->{'default'} = $includejs_url;
+ }
+ }
+ }
+}
+
+sub config_add_panels {
+ my ($self, $args) = @_;
+ my $modules = $args->{panel_modules};
+ $modules->{Persona} = "Bugzilla::Extension::Persona::Config";
+}
+
+__PACKAGE__->NAME;
diff --git a/extensions/Persona/TODO b/extensions/Persona/TODO
new file mode 100644
index 000000000..ac94a3c42
--- /dev/null
+++ b/extensions/Persona/TODO
@@ -0,0 +1,19 @@
+ToDo:
+
+* Cache the LWP::UserAgent in Login.pm?
+
+* Fix Bugzilla::Auth::Login::Stack to allow failure part way down the chain
+ (currently, it seems that both CGI and BrowserID have to be last in order
+ to report login failures correctly.)
+
+* JS inclusions noticeably slow page load. Do we want a local copy of
+ browserid.js? Do the browserid folks object to that? How can we get good
+ performance? How can we avoid including it in every logged-in page? Can we
+ do demand loading onclick, and/or load-on-reveal?
+
+* Fix -8px margin-bottom hack in login-small-additional_methods.html.tmpl
+
+
+
+
+
diff --git a/extensions/Persona/lib/Config.pm b/extensions/Persona/lib/Config.pm
new file mode 100644
index 000000000..9c483cb51
--- /dev/null
+++ b/extensions/Persona/lib/Config.pm
@@ -0,0 +1,41 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::Persona::Config;
+
+use strict;
+use warnings;
+
+use Bugzilla::Config::Common;
+
+our $sortkey = 1350;
+
+sub get_param_list {
+ my ($class) = @_;
+
+ my @param_list = (
+ {
+ name => 'persona_verify_url',
+ type => 't',
+ default => 'https://verifier.login.persona.org/verify',
+ },
+ {
+ name => 'persona_includejs_url',
+ type => 't',
+ default => 'https://login.persona.org/include.js',
+ },
+ {
+ name => 'persona_proxy_url',
+ type => 't',
+ default => '',
+ },
+ );
+
+ return @param_list;
+}
+
+1;
diff --git a/extensions/Persona/lib/Login.pm b/extensions/Persona/lib/Login.pm
new file mode 100644
index 000000000..ece92a3c0
--- /dev/null
+++ b/extensions/Persona/lib/Login.pm
@@ -0,0 +1,127 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::Persona::Login;
+use strict;
+use base qw(Bugzilla::Auth::Login);
+
+use Bugzilla::Constants;
+use Bugzilla::Util;
+use Bugzilla::Error;
+use Bugzilla::Token;
+
+use JSON;
+use LWP::UserAgent;
+
+use constant requires_verification => 0;
+use constant is_automatic => 1;
+use constant user_can_create_account => 1;
+
+sub get_login_info {
+ my ($self) = @_;
+
+ my $cgi = Bugzilla->cgi;
+
+ my $assertion = $cgi->param("persona_assertion");
+ # Avoid the assertion being copied into any 'echoes' of the current URL
+ # in the page.
+ $cgi->delete('persona_assertion');
+
+ if (!$assertion || !Bugzilla->params->{persona_verify_url}) {
+ return { failure => AUTH_NODATA };
+ }
+
+ my $token = $cgi->param("token");
+ $cgi->delete('token');
+ check_hash_token($token, ['login']);
+
+ my $urlbase = new URI(correct_urlbase());
+ my $audience = $urlbase->scheme . "://" . $urlbase->host_port;
+
+ my $ua = new LWP::UserAgent( timeout => 10 );
+ if (Bugzilla->params->{persona_proxy_url}) {
+ $ua->proxy('https', Bugzilla->params->{persona_proxy_url});
+ }
+
+ my $response = $ua->post(Bugzilla->params->{persona_verify_url},
+ [ assertion => $assertion,
+ audience => $audience ]);
+ if ($response->is_error) {
+ return { failure => AUTH_ERROR,
+ user_error => 'persona_server_fail',
+ details => { reason => $response->message }};
+ }
+
+ my $info;
+ eval {
+ $info = decode_json($response->decoded_content());
+ };
+ if ($@) {
+ return { failure => AUTH_ERROR,
+ user_error => 'persona_server_fail',
+ details => { reason => 'Received a malformed response.' }};
+ }
+ if ($info->{'status'} eq 'failure') {
+ return { failure => AUTH_ERROR,
+ user_error => 'persona_server_fail',
+ details => { reason => $info->{reason} }};
+ }
+
+ if ($info->{'status'} eq "okay" &&
+ $info->{'audience'} eq $audience &&
+ ($info->{'expires'} / 1000) > time())
+ {
+ my $login_data = {
+ 'username' => $info->{'email'}
+ };
+
+ my $result = Bugzilla::Auth::Verify->create_or_update_user($login_data);
+ return $result if $result->{'failure'};
+
+ my $user = $result->{'user'};
+
+ # You can restrict people in a particular group from logging in using
+ # Persona by making that group a member of a group called
+ # "no-browser-id".
+ #
+ # If you have your "createemailregexp" set up in such a way that a
+ # newly-created account is a member of "no-browser-id", this code will
+ # create an account for them and then fail their login. Which isn't
+ # great, but they can still use normal-Bugzilla-login password
+ # recovery.
+ if ($user->in_group('no-browser-id')) {
+ return { failure => AUTH_ERROR,
+ user_error => 'persona_account_too_powerful' };
+ }
+
+ $login_data->{'user'} = $user;
+ $login_data->{'user_id'} = $user->id;
+
+ return $login_data;
+ }
+ else {
+ return { failure => AUTH_LOGINFAILED };
+ }
+}
+
+# Pinched from Bugzilla::Auth::Login::CGI
+sub fail_nodata {
+ my ($self) = @_;
+ my $cgi = Bugzilla->cgi;
+ my $template = Bugzilla->template;
+
+ if (Bugzilla->usage_mode != USAGE_MODE_BROWSER) {
+ ThrowUserError('login_required');
+ }
+
+ print $cgi->header();
+ $template->process("account/auth/login.html.tmpl", { 'target' => $cgi->url(-relative=>1) })
+ || ThrowTemplateError($template->error());
+ exit;
+}
+
+1;
diff --git a/extensions/Persona/template/en/default/admin/params/browserid.html.tmpl b/extensions/Persona/template/en/default/admin/params/browserid.html.tmpl
new file mode 100644
index 000000000..379d12058
--- /dev/null
+++ b/extensions/Persona/template/en/default/admin/params/browserid.html.tmpl
@@ -0,0 +1,22 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%
+ title = "Persona"
+ desc = "Configure Persona Authentication"
+%]
+
+[% param_descs = {
+ persona_verify_url => "This is the URL for the Persona authority that the " _
+ "user will be verified against. " _
+ "Example: <kbd>https://verifier.login.persona.org/verify</kbd>.",
+ persona_includejs_url => "This is the URL needed by Persona to load the necessary " _
+ "javascript library for authentication. " _
+ "Example: <kbd>https://persona.org/include.js</kbd>."
+ }
+%]
diff --git a/extensions/Persona/template/en/default/admin/params/persona.html.tmpl b/extensions/Persona/template/en/default/admin/params/persona.html.tmpl
new file mode 100644
index 000000000..ef3cf32d2
--- /dev/null
+++ b/extensions/Persona/template/en/default/admin/params/persona.html.tmpl
@@ -0,0 +1,24 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[%
+ title = "Persona"
+ desc = "Configure Persona Authentication"
+%]
+
+[% param_descs = {
+ persona_verify_url => "This is the URL for the Persona authority that the " _
+ "user will be verified against. " _
+ "Example: <kbd>https://verifier.login.persona.org/verify</kbd>.",
+ persona_includejs_url => "This is the URL needed by Persona to load the necessary " _
+ "javascript library for authentication. " _
+ "Example: <kbd>https://login.persona.org/include.js</kbd>."
+ persona_proxy_url => "The URL of a HTTPS proxy server (optional). " _
+ "Example: <kbd>http://proxy.example.com:3128</kbd>."
+ }
+%]
diff --git a/extensions/Persona/template/en/default/hook/account/auth/login-additional_methods.html.tmpl b/extensions/Persona/template/en/default/hook/account/auth/login-additional_methods.html.tmpl
new file mode 100644
index 000000000..5be7910ad
--- /dev/null
+++ b/extensions/Persona/template/en/default/hook/account/auth/login-additional_methods.html.tmpl
@@ -0,0 +1,6 @@
+[% IF Param('user_info_class').split(',').contains('Persona')
+ && Param('persona_includejs_url') %]
+<p>
+ <img src="extensions/Persona/web/images/persona_sign_in.png" width="185" height="25" onclick="persona_sign_in()">
+</p>
+[% END %]
diff --git a/extensions/Persona/template/en/default/hook/account/auth/login-small-additional_methods.html.tmpl b/extensions/Persona/template/en/default/hook/account/auth/login-small-additional_methods.html.tmpl
new file mode 100644
index 000000000..5d8503d73
--- /dev/null
+++ b/extensions/Persona/template/en/default/hook/account/auth/login-small-additional_methods.html.tmpl
@@ -0,0 +1,17 @@
+[% IF Param('user_info_class').split(',').contains('Persona')
+ && Param('persona_includejs_url') %]
+<script type="text/javascript">
+ YAHOO.util.Event.addListener('login_link[% qs_suffix FILTER js %]','click', function () {
+ var login_link = YAHOO.util.Dom.get('persona_mini_login[% qs_suffix FILTER js %]');
+ YAHOO.util.Dom.removeClass(login_link, 'bz_default_hidden');
+ });
+ YAHOO.util.Event.addListener('hide_mini_login[% qs_suffix FILTER js %]','click', function () {
+ var login_link = YAHOO.util.Dom.get('persona_mini_login[% qs_suffix FILTER js %]');
+ YAHOO.util.Dom.addClass(login_link, 'bz_default_hidden');
+ });
+</script>
+<span id="persona_mini_login[% qs_suffix FILTER html %]" class="bz_default_hidden">
+ <img src="extensions/Persona/web/images/sign_in.png" height="22" width="75" align="absmiddle"
+ title="Sign in with Persona" onclick="persona_sign_in()"> or
+</span>
+[% END %]
diff --git a/extensions/Persona/template/en/default/hook/account/create-additional_methods.html.tmpl b/extensions/Persona/template/en/default/hook/account/create-additional_methods.html.tmpl
new file mode 100644
index 000000000..355ce3629
--- /dev/null
+++ b/extensions/Persona/template/en/default/hook/account/create-additional_methods.html.tmpl
@@ -0,0 +1,13 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[% RETURN UNLESS Param('user_info_class').split(',').contains('Persona') %]
+
+Or, use your Persona account:
+<img src="extensions/Persona/web/images/sign_in.png" onclick="persona_sign_in()"
+ width="95" height="25" align="absmiddle">
diff --git a/extensions/Persona/template/en/default/hook/global/header-additional_header.html.tmpl b/extensions/Persona/template/en/default/hook/global/header-additional_header.html.tmpl
new file mode 100644
index 000000000..786010a34
--- /dev/null
+++ b/extensions/Persona/template/en/default/hook/global/header-additional_header.html.tmpl
@@ -0,0 +1,87 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[% RETURN UNLESS Param('persona_includejs_url')
+ && Param('user_info_class').split(',').contains('Persona') %]
+
+[%# for now don't inject persona javascript on authenticated users.
+ # we've seen sessions being logged out unexpectedly
+ # we should only inject this code for users who used persona to authenicate %]
+[% RETURN IF user.id %]
+
+[% USE Bugzilla %]
+[% cgi = Bugzilla.cgi %]
+
+<script defer src="[% Param('persona_includejs_url') %]" type="text/javascript"></script>
+<script type="text/javascript">
+
+function createHidden(name, value, form) {
+ var field = document.createElement('input');
+ field.type = 'hidden';
+ field.name = name;
+ field.value = value;;
+ form.appendChild(field);
+}
+
+[% login_target = cgi.url("-relative" => 1, "-query" => 1) %]
+[% IF !login_target
+ OR login_target.match("^token\.cgi")
+ OR login_target.match("^createaccount\.cgi") %]
+ [% login_target = "index.cgi" %]
+[% END %]
+[% login_target = urlbase _ login_target %]
+
+[%# we only want to honour explicit login requests %]
+var persona_ignore_login = true;
+
+function persona_onlogin(assertion) {
+ if (persona_ignore_login)
+ return;
+ [% IF !user.id %]
+ var form = document.createElement('form');
+ form.action = '[% login_target FILTER js %]';
+ form.method = 'POST';
+ form.style.display = 'none';
+
+ createHidden('token', '[% issue_hash_token(['login']) FILTER js %]', form);
+ createHidden('Bugzilla_remember', 'on', form);
+ createHidden('persona_assertion', assertion, form);
+
+ [% FOREACH field = cgi.param() %]
+ [% NEXT IF field.search("^(Bugzilla_(login|password|restrictlogin)|token|persona_assertion)$") %]
+ [% NEXT UNLESS cgi.param(field).can('slice') %]
+ [% FOREACH mvalue = cgi.param(field).slice(0) %]
+ createHidden('[% field FILTER js %]', '[% mvalue FILTER html_linebreak FILTER js %]', form);
+ [% END %]
+ [% END %]
+
+ document.body.appendChild(form);
+ form.submit();
+ [% END %]
+}
+
+YAHOO.util.Event.on(window, 'load', persona_init);
+function persona_init() {
+ navigator.id.watch({
+ [%# we can't set loggedInUser to user.login as this causes cgi authenticated
+ sessions to be logged out by persona %]
+ loggedInUser: null,
+ onlogin: persona_onlogin,
+ onlogout: function () {
+ [%# this should be redirecting to index.cgi?logout=1 however there's a
+ persona bug which causes this to break chrome and safari logins.
+ https://github.com/mozilla/browserid/issues/2423 %]
+ }
+ });
+}
+
+function persona_sign_in() {
+ persona_ignore_login = false;
+ navigator.id.request({ siteName: '[% terms.BugzillaTitle FILTER js %]' });
+}
+</script>
diff --git a/extensions/Persona/template/en/default/hook/global/user-error-errors.html.tmpl b/extensions/Persona/template/en/default/hook/global/user-error-errors.html.tmpl
new file mode 100644
index 000000000..f2e5bda24
--- /dev/null
+++ b/extensions/Persona/template/en/default/hook/global/user-error-errors.html.tmpl
@@ -0,0 +1,12 @@
+[% IF error == "persona_account_too_powerful" %]
+ [% title = "Account Too Powerful" %]
+ Your account is a member of a group which is not permitted to use Persona to
+ log in. Please log in with your [% terms.Bugzilla %] username and password.
+ <br><br>
+ (Persona logins are disabled for accounts which are members of certain
+ particularly sensitive groups, while we gain experience with the technology.)
+[% ELSIF error == "persona_server_fail" %]
+ An error occurred during communication with the Persona servers:
+ <br>
+ [% reason FILTER html %]
+[% END %]
diff --git a/extensions/Persona/web/images/persona_sign_in.png b/extensions/Persona/web/images/persona_sign_in.png
new file mode 100644
index 000000000..ab88a7154
--- /dev/null
+++ b/extensions/Persona/web/images/persona_sign_in.png
Binary files differ
diff --git a/extensions/Persona/web/images/sign_in.png b/extensions/Persona/web/images/sign_in.png
new file mode 100644
index 000000000..82594ba82
--- /dev/null
+++ b/extensions/Persona/web/images/sign_in.png
Binary files differ