diff options
-rw-r--r-- | extensions/BrowserID/Config.pm | 43 | ||||
-rw-r--r-- | extensions/BrowserID/Extension.pm | 52 | ||||
-rw-r--r-- | extensions/BrowserID/TODO | 19 | ||||
-rw-r--r-- | extensions/BrowserID/lib/Login.pm | 122 | ||||
-rw-r--r-- | extensions/BrowserID/template/en/default/hook/account/auth/login-additional_methods.html.tmpl | 23 | ||||
-rw-r--r-- | extensions/BrowserID/template/en/default/hook/account/auth/login-small-additional_methods.html.tmpl | 20 | ||||
-rw-r--r-- | extensions/ComponentWatching/Extension.pm.orig | 345 | ||||
-rw-r--r-- | template/en/default/account/auth/login-small.html.tmpl | 3 | ||||
-rw-r--r-- | template/en/default/account/auth/login.html.tmpl | 2 |
9 files changed, 284 insertions, 345 deletions
diff --git a/extensions/BrowserID/Config.pm b/extensions/BrowserID/Config.pm new file mode 100644 index 000000000..a55ea8ff0 --- /dev/null +++ b/extensions/BrowserID/Config.pm @@ -0,0 +1,43 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the BrowserID Bugzilla Extension. +# +# The Initial Developer of the Original Code is the Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2011 the +# Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Gervase Markham <gerv@gerv.net> + +package Bugzilla::Extension::BrowserID; +use strict; + +use constant NAME => 'BrowserID'; + +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/BrowserID/Extension.pm b/extensions/BrowserID/Extension.pm new file mode 100644 index 000000000..b132ea503 --- /dev/null +++ b/extensions/BrowserID/Extension.pm @@ -0,0 +1,52 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the BrowserID Bugzilla Extension. +# +# The Initial Developer of the Original Code is the Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2011 the +# Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Gervase Markham <gerv@gerv.net> + +package Bugzilla::Extension::BrowserID; +use strict; +use base qw(Bugzilla::Extension); + +our $VERSION = '0.01'; + +sub auth_login_methods { + my ($self, $args) = @_; + my $modules = $args->{'modules'}; + if (exists($modules->{'BrowserID'})) { + $modules->{'BrowserID'} = 'Bugzilla/Extension/BrowserID/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) { + # XXX Bugzilla::Auth::Login::Stack doesn't let a hard failure stop the + # login process :-(( We put it in both ways round for now, for testing. + push(@{ $user_info_class->{'choices'} }, "CGI,BrowserID"); + push(@{ $user_info_class->{'choices'} }, "BrowserID,CGI"); + } +} + +__PACKAGE__->NAME; diff --git a/extensions/BrowserID/TODO b/extensions/BrowserID/TODO new file mode 100644 index 000000000..ac94a3c42 --- /dev/null +++ b/extensions/BrowserID/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/BrowserID/lib/Login.pm b/extensions/BrowserID/lib/Login.pm new file mode 100644 index 000000000..eedc85d09 --- /dev/null +++ b/extensions/BrowserID/lib/Login.pm @@ -0,0 +1,122 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the BrowserID Bugzilla Extension. +# +# The Initial Developer of the Original Code is the Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2011 the +# Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Gervase Markham <gerv@gerv.net> + +package Bugzilla::Extension::BrowserID::Login; +use strict; +use base qw(Bugzilla::Auth::Login); + +use Bugzilla::Constants; +use Bugzilla::Util; + +use JSON; +use LWP::UserAgent; + +use constant requires_verification => 0; +use constant is_automatic => 1; + +sub get_login_info { + my ($self) = @_; + + my $cgi = Bugzilla->cgi; + my $assertion = $cgi->param("browserid_assertion"); + # Avoid the assertion being copied into any 'echoes' of the current URL + # in the page. + $cgi->delete('browserid_assertion'); + + if (!$assertion) { + return { failure => AUTH_NODATA }; + } + + my $urlbase = new URI(correct_urlbase()); + my $audience = $urlbase->scheme . "://" . $urlbase->host_port; + + my $ua = new LWP::UserAgent(); + + my $info = { 'status' => 'browserid-server-broken' }; + eval { + my $response = $ua->post("https://browserid.org/verify", + [assertion => $assertion, + audience => $audience]); + + $info = decode_json($response->content()); + }; + + # XXX Add 120 secs because 'expires' is currently broken in deployed + # BrowserID server - it returns exact current time, so is immediately + # expired! This should be fixed soon. + if ($info->{'status'} eq "okay" && + $info->{'audience'} eq $audience && + (($info->{'expires'} / 1000) + 120) > 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'}; + + # BrowserID logins are currently restricted to less powerful accounts - + # the most you can have is 'editbugs'. This is while the technology + # is maturing. So we need to check that the user doesn't have 'too + # many permissions' to log in this way. + # + # If a newly-created account has too many permissions, 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. + my @safe_groups = ('everyone', 'canconfirm', 'editbugs'); + foreach my $group (@{ $user->groups() }) { + if (!grep { $group->name eq $_ } @safe_groups) { + return { failure => AUTH_LOGINFAILED }; + } + } + + $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/BrowserID/template/en/default/hook/account/auth/login-additional_methods.html.tmpl b/extensions/BrowserID/template/en/default/hook/account/auth/login-additional_methods.html.tmpl new file mode 100644 index 000000000..4d8d8d3f0 --- /dev/null +++ b/extensions/BrowserID/template/en/default/hook/account/auth/login-additional_methods.html.tmpl @@ -0,0 +1,23 @@ +[% IF Param('user_info_class').split(',').contains('BrowserID') %] +<script src="https://browserid.org/include.js" type="text/javascript"></script> + +<script type="text/javascript"> +function browserid_sign_in() { + navigator.id.getVerifiedEmail(function(assertion) { + if (assertion) { + // This code will be invoked once the user has successfully + // selected an email address they control to sign in with. + window.location.href = "[% target FILTER none %]index.cgi?browserid_assertion=" + assertion; + } else { + // something went wrong! the user isn't logged in. + alert("Oh no! Something went wrong..."); + } + }); +} +</script> + +<p> +Or, you could login using BrowserID: +<img src="extensions/BrowserID/web/sign_in_orange.png" onclick="browserid_sign_in()"> +</p> +[% END %]
\ No newline at end of file diff --git a/extensions/BrowserID/template/en/default/hook/account/auth/login-small-additional_methods.html.tmpl b/extensions/BrowserID/template/en/default/hook/account/auth/login-small-additional_methods.html.tmpl new file mode 100644 index 000000000..12bff015b --- /dev/null +++ b/extensions/BrowserID/template/en/default/hook/account/auth/login-small-additional_methods.html.tmpl @@ -0,0 +1,20 @@ +[% IF Param('user_info_class').split(',').contains('BrowserID') %] +<script src="https://browserid.org/include.js" type="text/javascript"></script> + +<script type="text/javascript"> +function browserid_sign_in() { + navigator.id.getVerifiedEmail(function(assertion) { + if (assertion) { + // This code will be invoked once the user has successfully + // selected an email address they control to sign in with. + window.location.href = "[% login_target FILTER none %]?browserid_assertion=" + assertion; + } else { + // something went wrong! the user isn't logged in. + alert("Oh no! Something went wrong..."); + } + }); +} +</script> + +<img src="extensions/BrowserID/web/sign_in_orange.png" onclick="browserid_sign_in()" style="margin-bottom: -8px"> or +[% END %]
\ No newline at end of file diff --git a/extensions/ComponentWatching/Extension.pm.orig b/extensions/ComponentWatching/Extension.pm.orig deleted file mode 100644 index d39d9cd55..000000000 --- a/extensions/ComponentWatching/Extension.pm.orig +++ /dev/null @@ -1,345 +0,0 @@ -# -*- Mode: perl; indent-tabs-mode: nil -*- -# -# The contents of this file are subject to the Mozilla Public -# License Version 1.1 (the "License"); you may not use this file -# except in compliance with the License. You may obtain a copy of -# the License at http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS -# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -# implied. See the License for the specific language governing -# rights and limitations under the License. -# -# The Original Code is the Component Watching Extension -# -# The Initial Developer of the Original Code is the Mozilla Foundation -# Portions created by the Initial Developers are Copyright (C) 2011 the -# Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Byron Jones <bjones@mozilla.com> - -package Bugzilla::Extension::ComponentWatching; -use strict; -use base qw(Bugzilla::Extension); - -use Bugzilla::Constants; -use Bugzilla::Error; -use Bugzilla::Group; -use Bugzilla::User; -use Bugzilla::User::Setting; - -our $VERSION = '1.1'; - -use constant REL_COMPONENT_WATCHER => 15; - -# -# installation -# - -sub db_schema_abstract_schema { - my ($self, $args) = @_; - $args->{'schema'}->{'component_watch'} = { - FIELDS => [ - user_id => { - TYPE => 'INT3', - NOTNULL => 1, - REFERENCES => { - TABLE => 'profiles', - COLUMN => 'userid', - DELETE => 'CASCADE', - } - }, - component_id => { - TYPE => 'INT2', - NOTNULL => 0, - REFERENCES => { - TABLE => 'components', - COLUMN => 'id', - DELETE => 'CASCADE', - } - }, - product_id => { - TYPE => 'INT2', - NOTNULL => 0, - REFERENCES => { - TABLE => 'products', - COLUMN => 'id', - DELETE => 'CASCADE', - } - }, - ], - }; -} - -# -# templates -# - -sub template_before_create { - my ($self, $args) = @_; - my $config = $args->{config}; - my $constants = $config->{CONSTANTS}; - $constants->{REL_COMPONENT_WATCHER} = REL_COMPONENT_WATCHER; -} - -# -# preferences -# - -sub user_preferences { - my ($self, $args) = @_; - my $tab = $args->{'current_tab'}; - return unless $tab eq 'component_watch'; - - my $save = $args->{'save_changes'}; - my $handled = $args->{'handled'}; - my $user = Bugzilla->user; - - if ($save) { - my ($sth, $sthAdd, $sthDel); - - if (Bugzilla->input_params->{'add'}) { - # add watch - - my $productName = Bugzilla->input_params->{'add_product'}; - my $ra_componentNames = Bugzilla->input_params->{'add_component'}; - $ra_componentNames = [$ra_componentNames] unless ref($ra_componentNames); - - # load product and verify access - my $product = Bugzilla::Product->new({ name => $productName }); - unless ($product && $user->can_access_product($product)) { - ThrowUserError('product_access_denied', { product => $productName }); - } - - if (grep { $_ eq '' } @$ra_componentNames) { - # watching a product - _addProductWatch($user, $product); - - } else { - # watching specific components - foreach my $componentName (@$ra_componentNames) { - my $component = Bugzilla::Component->new({ name => $componentName, product => $product }); - unless ($component) { - ThrowUserError('product_access_denied', { product => $productName }); - } - _addComponentWatch($user, $component); - } - } - - _addDefaultSettings($user); - - } else { - # remove watch(s) - - foreach my $name (keys %{Bugzilla->input_params}) { - if ($name =~ /^del_(\d+)$/) { - _deleteProductWatch($user, $1); - } elsif ($name =~ /^del_(\d+)_(\d+)$/) { - _deleteComponentWatch($user, $1, $2); - } - } - } - } - - $args->{'vars'}->{'watches'} = _getWatches($user); - - $$handled = 1; -} - -# -# bugmail -# - -sub bugmail_recipients { - my ($self, $args) = @_; - my $bug = $args->{'bug'}; - my $recipients = $args->{'recipients'}; - my $diffs = $args->{'diffs'}; - - my ($oldProductId, $newProductId) = ($bug->product_id, $bug->product_id); - my ($oldComponentId, $newComponentId) = ($bug->component_id, $bug->component_id); - - # notify when the product/component is switch from one being watched - if (@$diffs) { - # we need the product to process the component, so scan for that first - my $product; - foreach my $ra (@$diffs) { - next if !(exists $ra->{'old'} - && exists $ra->{'field_name'}); - if ($ra->{'field_name'} eq 'product') { - $product = Bugzilla::Product->new({ name => $ra->{'old'} }); - $oldProductId = $product->id; - } - } - if (!$product) { - $product = Bugzilla::Product->new($oldProductId); - } - foreach my $ra (@$diffs) { - next if !(exists $ra->{'old'} - && exists $ra->{'field_name'}); - if ($ra->{'field_name'} eq 'component') { - my $component = Bugzilla::Component->new({ name => $ra->{'old'}, product => $product }); - $oldComponentId = $component->id; - } - } - } - - my $dbh = Bugzilla->dbh; - my $sth = $dbh->prepare(" - SELECT user_id - FROM component_watch - WHERE ((product_id = ? OR product_id = ?) AND component_id IS NULL) - OR (component_id = ? OR component_id = ?) - "); - $sth->execute($oldProductId, $newProductId, $oldComponentId, $newComponentId); - while (my ($uid) = $sth->fetchrow_array) { - if (!exists $recipients->{$uid}) { - $recipients->{$uid}->{+REL_COMPONENT_WATCHER} = Bugzilla::BugMail::BIT_WATCHING(); - } - } -} - -sub bugmail_relationships { - my ($self, $args) = @_; - my $relationships = $args->{relationships}; - $relationships->{+REL_COMPONENT_WATCHER} = 'Component-Watcher'; -} - -# -# db -# - -sub _getWatches { - my ($user) = @_; - my $dbh = Bugzilla->dbh; - - my $sth = $dbh->prepare(" - SELECT product_id, component_id - FROM component_watch - WHERE user_id = ? - "); - $sth->execute($user->id); - my @watches; - while (my ($productId, $componentId) = $sth->fetchrow_array) { - my $product = Bugzilla::Product->new($productId); - next unless $product && $user->can_access_product($product); - - my %watch = ( product => $product ); - if ($componentId) { - my $component = Bugzilla::Component->new($componentId); - next unless $component; - $watch{'component'} = $component; - } - - push @watches, \%watch; - } - - @watches = sort { - $a->{'product'}->name cmp $b->{'product'}->name - || $a->{'component'}->name cmp $b->{'component'}->name - } @watches; - - return \@watches; -} - -sub _addProductWatch { - my ($user, $product) = @_; - my $dbh = Bugzilla->dbh; - - my $sth = $dbh->prepare(" - SELECT 1 - FROM component_watch - WHERE user_id = ? AND product_id = ? AND component_id IS NULL - "); - $sth->execute($user->id, $product->id); - return if $sth->fetchrow_array; - - $sth = $dbh->prepare(" - DELETE FROM component_watch - WHERE user_id = ? AND product_id = ? - "); - $sth->execute($user->id, $product->id); - - $sth = $dbh->prepare(" - INSERT INTO component_watch(user_id, product_id) - VALUES (?, ?) - "); - $sth->execute($user->id, $product->id); -} - -sub _addComponentWatch { - my ($user, $component) = @_; - my $dbh = Bugzilla->dbh; - - my $sth = $dbh->prepare(" - SELECT 1 - FROM component_watch - WHERE user_id = ? - AND (component_id = ? OR (product_id = ? AND component_id IS NULL)) - "); - $sth->execute($user->id, $component->id, $component->product_id); - return if $sth->fetchrow_array; - - $sth = $dbh->prepare(" - INSERT INTO component_watch(user_id, product_id, component_id) - VALUES (?, ?, ?) - "); - $sth->execute($user->id, $component->product_id, $component->id); -} - -sub _deleteProductWatch { - my ($user, $productId) = @_; - my $dbh = Bugzilla->dbh; - - my $sth = $dbh->prepare(" - DELETE FROM component_watch - WHERE user_id = ? AND product_id = ? AND component_id IS NULL - "); - $sth->execute($user->id, $productId); -} - -sub _deleteComponentWatch { - my ($user, $productId, $componentId) = @_; - my $dbh = Bugzilla->dbh; - - my $sth = $dbh->prepare(" - DELETE FROM component_watch - WHERE user_id = ? AND product_id = ? AND component_id = ? - "); - $sth->execute($user->id, $productId, $componentId); -} - -sub _addDefaultSettings { - my ($user) = @_; - my $dbh = Bugzilla->dbh; - - my $sth = $dbh->prepare(" - SELECT 1 - FROM email_setting - WHERE user_id = ? AND relationship = ? - "); - $sth->execute($user->id, REL_COMPONENT_WATCHER); - return if $sth->fetchrow_array; - - my @defaultEvents = ( - EVT_OTHER, - EVT_COMMENT, - EVT_ATTACHMENT, - EVT_ATTACHMENT_DATA, - EVT_PROJ_MANAGEMENT, - EVT_OPENED_CLOSED, - EVT_KEYWORD, - EVT_DEPEND_BLOCK, - EVT_BUG_CREATED, - ); - foreach my $event (@defaultEvents) { - $dbh->do( - "INSERT INTO email_setting(user_id,relationship,event) VALUES (?,?,?)", - undef, - $user->id, REL_COMPONENT_WATCHER, $event - ); - } -} - -__PACKAGE__->NAME; diff --git a/template/en/default/account/auth/login-small.html.tmpl b/template/en/default/account/auth/login-small.html.tmpl index b073c79cc..21bd10117 100644 --- a/template/en/default/account/auth/login-small.html.tmpl +++ b/template/en/default/account/auth/login-small.html.tmpl @@ -47,6 +47,9 @@ id="mini_login[% qs_suffix FILTER html %]" onsubmit="return check_mini_login_fields( '[% qs_suffix FILTER html %]' );" > + + [% Hook.process('additional_methods') %] + <input id="Bugzilla_login[% qs_suffix FILTER html %]" class="bz_login" name="Bugzilla_login" diff --git a/template/en/default/account/auth/login.html.tmpl b/template/en/default/account/auth/login.html.tmpl index ec8c11e24..a3b075578 100644 --- a/template/en/default/account/auth/login.html.tmpl +++ b/template/en/default/account/auth/login.html.tmpl @@ -97,6 +97,8 @@ # their password, assuming that our auth method allows that. #%] + [% Hook.process('additional_methods') %] + [% IF Param("createemailregexp") && user.authorizer.user_can_create_account %] <hr> |