Commit 47269232 authored by mkanat%bugzilla.org's avatar mkanat%bugzilla.org

Bug 525734: Allow WebService clients to authenticate using Bugzilla_login and Bugzilla_password

Patch by Max Kanat-Alexander <mkanat@bugzilla.org> r=dkl, a=mkanat
parent a90df29f
......@@ -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<cgi> object. Note that modules should B<not> 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<input_params>
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<input_params> currently represents).
This should be used instead of L</cgi> 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<Note:> When C<input_params> represents the CGI parameters, any
parameter specified more than once (like C<foo=bar&foo=baz>) 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<can> appear multiple times,
if it only I<does> appear once, then it will be a scalar in C<input_params>,
not an arrayref.
=item C<user>
C<undef> if there is no currently logged in user or if the login code has not
......
......@@ -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 };
......
......@@ -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';
......
......@@ -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__
......
......@@ -144,6 +144,9 @@ These params are accessible through L<Bugzilla/hook_args>.
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<Bugzilla/input_params> 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<info.pl>.
......
......@@ -142,11 +142,51 @@ how this is implemented for those frontends.
=head1 LOGGING IN
There are various ways to log in:
=over
=item C<User.login>
You can use L<Bugzilla::WebService::User/login> 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<Bugzilla_login> and C<Bugzilla_password>
B<Added in Bugzilla 3.6>
You can specify C<Bugzilla_login> and C<Bugzilla_password> 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<Bugzilla_login> (string) - A user's login name.
=item C<Bugzilla_password> (string) - That user's password.
=item C<Bugzilla_restrictlogin> (boolean) - Optional. If true,
then your login will only be valid for your IP address.
=item C<Bugzilla_rememberlogin> (boolean) - Optional. If true,
then the cookie sent back to you with the method response will
not expire.
=back
The C<Bugzilla_restrictlogin> and C<Bugzilla_rememberlogin> options
are only used when you have also specified C<Bugzilla_login> and
C<Bugzilla_password>.
Note that Bugzilla will return HTTP cookies along with the method
response when you use these arguments (just like the C<User.login> method
above).
=back
=head1 STABLE, EXPERIMENTAL, and UNSTABLE
Methods are marked B<STABLE> if you can expect their parameters and
......
......@@ -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();
}
......
......@@ -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.
......
......@@ -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;
......
......@@ -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) };
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment