From 4726923228d810ea0cd1800b17a9244c6f0d8eef Mon Sep 17 00:00:00 2001 From: "mkanat%bugzilla.org" <> Date: Mon, 9 Nov 2009 19:15:28 +0000 Subject: Bug 525734: Allow WebService clients to authenticate using Bugzilla_login and Bugzilla_password Patch by Max Kanat-Alexander r=dkl, a=mkanat --- Bugzilla.pm | 36 +++++++++++++++++++++++++++++++ Bugzilla/Auth/Login/CGI.pm | 8 +++---- Bugzilla/Auth/Persist/Cookie.pm | 7 +++--- Bugzilla/CGI.pm | 33 +++++++++++++++++++++++++++++ Bugzilla/Hook.pm | 3 +++ Bugzilla/WebService.pm | 40 +++++++++++++++++++++++++++++++++++ Bugzilla/WebService/Server.pm | 3 ++- Bugzilla/WebService/Server/JSONRPC.pm | 11 +++++----- Bugzilla/WebService/Server/XMLRPC.pm | 5 ++++- Bugzilla/WebService/User.pm | 10 ++++----- 10 files changed, 135 insertions(+), 21 deletions(-) mode change 100755 => 100644 Bugzilla/WebService.pm diff --git a/Bugzilla.pm b/Bugzilla.pm index a373aa801..666b1ec15 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -234,6 +234,22 @@ sub cgi { return $class->request_cache->{cgi}; } +sub input_params { + my ($class, $params) = @_; + my $cache = $class->request_cache; + # This is how the WebService and other places set input_params. + if (defined $params) { + $cache->{input_params} = $params; + } + return $cache->{input_params} if defined $cache->{input_params}; + + # Making this scalar makes it a tied hash to the internals of $cgi, + # so if a variable is changed, then it actually changes the $cgi object + # as well. + $cache->{input_params} = $class->cgi->Vars; + return $cache->{input_params}; +} + sub localconfig { my $class = shift; $class->request_cache->{localconfig} ||= read_localconfig(); @@ -647,6 +663,26 @@ The current C object. Note that modules should B be using this in general. Not all Bugzilla actions are cgi requests. Its useful as a convenience method for those scripts/templates which are only use via CGI, though. +=item C + +When running under the WebService, this is a hashref containing the arguments +passed to the WebService method that was called. When running in a normal +script, this is a hashref containing the contents of the CGI parameters. + +Modifying this hashref will modify the CGI parameters or the WebService +arguments (depending on what C currently represents). + +This should be used instead of L in situations where your code +could be being called by either a normal CGI script or a WebService method, +such as during a code hook. + +B When C represents the CGI parameters, any +parameter specified more than once (like C) will appear +as an arrayref in the hash, but any value specified only once will appear +as a scalar. This means that even if a value I appear multiple times, +if it only I appear once, then it will be a scalar in C, +not an arrayref. + =item C C if there is no currently logged in user or if the login code has not diff --git a/Bugzilla/Auth/Login/CGI.pm b/Bugzilla/Auth/Login/CGI.pm index a93bc3d3a..407582af4 100644 --- a/Bugzilla/Auth/Login/CGI.pm +++ b/Bugzilla/Auth/Login/CGI.pm @@ -40,12 +40,10 @@ use Bugzilla::Error; sub get_login_info { my ($self) = @_; - my $cgi = Bugzilla->cgi; - - my $username = trim($cgi->param("Bugzilla_login")); - my $password = $cgi->param("Bugzilla_password"); + my $params = Bugzilla->input_params; - $cgi->delete('Bugzilla_login', 'Bugzilla_password'); + my $username = trim(delete $params->{"Bugzilla_login"}); + my $password = delete $params->{"Bugzilla_password"}; if (!defined $username || !defined $password) { return { failure => AUTH_NODATA }; diff --git a/Bugzilla/Auth/Persist/Cookie.pm b/Bugzilla/Auth/Persist/Cookie.pm index 4458e31b5..1e1b3a871 100644 --- a/Bugzilla/Auth/Persist/Cookie.pm +++ b/Bugzilla/Auth/Persist/Cookie.pm @@ -48,9 +48,10 @@ sub persist_login { my ($self, $user) = @_; my $dbh = Bugzilla->dbh; my $cgi = Bugzilla->cgi; + my $input_params = Bugzilla->input_params; my $ip_addr; - if ($cgi->param('Bugzilla_restrictlogin')) { + if ($input_params->{'Bugzilla_restrictlogin'}) { $ip_addr = $cgi->remote_addr; # The IP address is valid, at least for comparing with itself in a # subsequent login @@ -80,8 +81,8 @@ sub persist_login { # or admin didn't forbid it and user told to remember. if ( Bugzilla->params->{'rememberlogin'} eq 'on' || (Bugzilla->params->{'rememberlogin'} ne 'off' && - $cgi->param('Bugzilla_remember') && - $cgi->param('Bugzilla_remember') eq 'on') ) + $input_params->{'Bugzilla_remember'} && + $input_params->{'Bugzilla_remember'} eq 'on') ) { # Not a session cookie, so set an infinite expiry $cookieargs{'-expires'} = 'Fri, 01-Jan-2038 00:00:00 GMT'; diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm index 8c68f996c..bebff2d63 100644 --- a/Bugzilla/CGI.pm +++ b/Bugzilla/CGI.pm @@ -416,6 +416,39 @@ sub url_is_attachment_base { return ($self->self_url =~ $regex) ? 1 : 0; } +########################## +# Vars TIEHASH Interface # +########################## + +# Fix the TIEHASH interface (scalar $cgi->Vars) to return and accept +# arrayrefs. +sub STORE { + my $self = shift; + my ($param, $value) = @_; + if (defined $value and ref $value eq 'ARRAY') { + return $self->param(-name => $param, -value => $value); + } + return $self->SUPER::STORE(@_); +} + +sub FETCH { + my ($self, $param) = @_; + return $self if $param eq 'CGI'; # CGI.pm did this, so we do too. + my @result = $self->param($param); + return undef if !scalar(@result); + return $result[0] if scalar(@result) == 1; + return \@result; +} + +# For the Vars TIEHASH interface: the normal CGI.pm DELETE doesn't return +# the value deleted, but Perl's "delete" expects that value. +sub DELETE { + my ($self, $param) = @_; + my $value = $self->FETCH($param); + $self->delete($param); + return $value; +} + 1; __END__ diff --git a/Bugzilla/Hook.pm b/Bugzilla/Hook.pm index 51bce7fbe..69fa2f654 100644 --- a/Bugzilla/Hook.pm +++ b/Bugzilla/Hook.pm @@ -144,6 +144,9 @@ These params are accessible through L. That returns a hashref. Very frequently, if you want your hook to do anything, you have to modify these variables. +You may also want to use L to get parameters +that were passed to the current CGI script or WebService method. + =head2 Versioning Extensions Every extension must have a file in its root called F. diff --git a/Bugzilla/WebService.pm b/Bugzilla/WebService.pm old mode 100755 new mode 100644 index 75fcf6bc9..222923e70 --- a/Bugzilla/WebService.pm +++ b/Bugzilla/WebService.pm @@ -142,11 +142,51 @@ how this is implemented for those frontends. =head1 LOGGING IN +There are various ways to log in: + +=over + +=item C + You can use L to log in as a Bugzilla user. This issues standard HTTP cookies that you must then use in future calls, so your client must be capable of receiving and transmitting cookies. +=item C and C + +B + +You can specify C and C as arguments +to any WebService method, and you will be logged in as that user if your +credentials are correct. Here are the arguments you can specify to any +WebService method to perform a login: + +=over + +=item C (string) - A user's login name. + +=item C (string) - That user's password. + +=item C (boolean) - Optional. If true, +then your login will only be valid for your IP address. + +=item C (boolean) - Optional. If true, +then the cookie sent back to you with the method response will +not expire. + +=back + +The C and C options +are only used when you have also specified C and +C. + +Note that Bugzilla will return HTTP cookies along with the method +response when you use these arguments (just like the C method +above). + +=back + =head1 STABLE, EXPERIMENTAL, and UNSTABLE Methods are marked B if you can expect their parameters and diff --git a/Bugzilla/WebService/Server.pm b/Bugzilla/WebService/Server.pm index 2db182fd4..115c7df89 100644 --- a/Bugzilla/WebService/Server.pm +++ b/Bugzilla/WebService/Server.pm @@ -21,7 +21,8 @@ use strict; sub handle_login { my ($self, $class, $method, $full_method) = @_; eval "require $class"; - return if $class->login_exempt($method); + return if ($class->login_exempt($method) + and !defined Bugzilla->input_params->{Bugzilla_login}); Bugzilla->login(); } diff --git a/Bugzilla/WebService/Server/JSONRPC.pm b/Bugzilla/WebService/Server/JSONRPC.pm index e54387a6d..919370a2a 100644 --- a/Bugzilla/WebService/Server/JSONRPC.pm +++ b/Bugzilla/WebService/Server/JSONRPC.pm @@ -112,12 +112,6 @@ sub _argument_type_check { my $self = shift; my $params = $self->SUPER::_argument_type_check(@_); - # This is the best time to do login checks. - $self->handle_login(); - - # If there are no parameters, we don't need to parse them. - return $params if !ref $params; - # JSON-RPC 1.0 requires all parameters to be passed as an array, so # we just pull out the first item and assume it's an object. if (ref $params eq 'ARRAY') { @@ -144,6 +138,11 @@ sub _argument_type_check { } } + Bugzilla->input_params($params); + + # This is the best time to do login checks. + $self->handle_login(); + # Bugzilla::WebService packages call internal methods like # $self->_some_private_method. So we have to inherit from # that class as well as this Server class. diff --git a/Bugzilla/WebService/Server/XMLRPC.pm b/Bugzilla/WebService/Server/XMLRPC.pm index b2a50712a..cbfb1b7f2 100644 --- a/Bugzilla/WebService/Server/XMLRPC.pm +++ b/Bugzilla/WebService/Server/XMLRPC.pm @@ -78,6 +78,7 @@ sub deserialize { $som->{_bz_do_taint} = 1; } bless $som, 'Bugzilla::XMLRPC::SOM'; + Bugzilla->input_params($som->paramsin); return $som; } @@ -146,11 +147,13 @@ use Bugzilla::WebService::Util qw(taint_data); sub paramsin { my $self = shift; + return $self->{bz_params_in} if $self->{bz_params_in}; my $params = $self->SUPER::paramsin(@_); if ($self->{_bz_do_taint}) { taint_data($params); } - return $params; + $self->{bz_params_in} = $params; + return $self->{bz_params_in}; } 1; diff --git a/Bugzilla/WebService/User.pm b/Bugzilla/WebService/User.pm index ba899cd4d..67a4720de 100644 --- a/Bugzilla/WebService/User.pm +++ b/Bugzilla/WebService/User.pm @@ -61,12 +61,12 @@ sub login { } # Make sure the CGI user info class works if necessary. - my $cgi = Bugzilla->cgi; - $cgi->param('Bugzilla_login', $params->{login}); - $cgi->param('Bugzilla_password', $params->{password}); - $cgi->param('Bugzilla_remember', $remember); + my $input_params = Bugzilla->input_params; + $input_params->{'Bugzilla_login'} = $params->{login}; + $input_params->{'Bugzilla_password'} = $params->{password}; + $input_params->{'Bugzilla_remember'} = $remember; - Bugzilla->login; + Bugzilla->login(); return { id => $self->type('int', Bugzilla->user->id) }; } -- cgit v1.2.3-24-g4f1b