Bug 893195 - Allow token based authentication for webservices
r=glob,a=sgreen git-svn-id: svn://10.0.0.236/trunk@264987 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
cceb62130f
commit
c83ee562ea
@ -1 +1 @@
|
|||||||
8717
|
8718
|
||||||
@ -109,6 +109,15 @@ sub can_logout {
|
|||||||
return $getter->can_logout;
|
return $getter->can_logout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub login_token {
|
||||||
|
my ($self) = @_;
|
||||||
|
my $getter = $self->{_info_getter}->{successful};
|
||||||
|
if ($getter && $getter->isa('Bugzilla::Auth::Login::Cookie')) {
|
||||||
|
return $getter->login_token;
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
sub user_can_create_account {
|
sub user_can_create_account {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
my $verifier = $self->{_verifier}->{successful};
|
my $verifier = $self->{_verifier}->{successful};
|
||||||
@ -410,6 +419,14 @@ Params: None
|
|||||||
Returns: C<true> if users can change their own email address,
|
Returns: C<true> if users can change their own email address,
|
||||||
C<false> otherwise.
|
C<false> otherwise.
|
||||||
|
|
||||||
|
=item C<login_token>
|
||||||
|
|
||||||
|
Description: If a login token was used instead of a cookie then this
|
||||||
|
will return the current login token data such as user id
|
||||||
|
and the token itself.
|
||||||
|
Params: None
|
||||||
|
Returns: A hash containing C<login_token> and C<user_id>.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=head1 STRUCTURE
|
=head1 STRUCTURE
|
||||||
|
|||||||
@ -9,7 +9,7 @@ package Bugzilla::Auth::Login;
|
|||||||
|
|
||||||
use 5.10.1;
|
use 5.10.1;
|
||||||
use strict;
|
use strict;
|
||||||
use fields qw();
|
use fields qw(_login_token);
|
||||||
|
|
||||||
# Determines whether or not a user can logout. It's really a subroutine,
|
# Determines whether or not a user can logout. It's really a subroutine,
|
||||||
# but we implement it here as a constant. Override it in subclasses if
|
# but we implement it here as a constant. Override it in subclasses if
|
||||||
|
|||||||
@ -20,7 +20,8 @@ use List::Util qw(first);
|
|||||||
use constant requires_persistence => 0;
|
use constant requires_persistence => 0;
|
||||||
use constant requires_verification => 0;
|
use constant requires_verification => 0;
|
||||||
use constant can_login => 0;
|
use constant can_login => 0;
|
||||||
use constant is_automatic => 1;
|
|
||||||
|
sub is_automatic { return $_[0]->login_token ? 0 : 1; }
|
||||||
|
|
||||||
# Note that Cookie never consults the Verifier, it always assumes
|
# Note that Cookie never consults the Verifier, it always assumes
|
||||||
# it has a valid DB account or it fails.
|
# it has a valid DB account or it fails.
|
||||||
@ -28,24 +29,35 @@ sub get_login_info {
|
|||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
my $cgi = Bugzilla->cgi;
|
my $cgi = Bugzilla->cgi;
|
||||||
my $dbh = Bugzilla->dbh;
|
my $dbh = Bugzilla->dbh;
|
||||||
|
my ($user_id, $login_cookie);
|
||||||
|
|
||||||
my $ip_addr = remote_ip();
|
if (!Bugzilla->request_cache->{auth_no_automatic_login}) {
|
||||||
my $login_cookie = $cgi->cookie("Bugzilla_logincookie");
|
$login_cookie = $cgi->cookie("Bugzilla_logincookie");
|
||||||
my $user_id = $cgi->cookie("Bugzilla_login");
|
$user_id = $cgi->cookie("Bugzilla_login");
|
||||||
|
|
||||||
# If cookies cannot be found, this could mean that they haven't
|
# If cookies cannot be found, this could mean that they haven't
|
||||||
# been made available yet. In this case, look at Bugzilla_cookie_list.
|
# been made available yet. In this case, look at Bugzilla_cookie_list.
|
||||||
unless ($login_cookie) {
|
unless ($login_cookie) {
|
||||||
my $cookie = first {$_->name eq 'Bugzilla_logincookie'}
|
my $cookie = first {$_->name eq 'Bugzilla_logincookie'}
|
||||||
@{$cgi->{'Bugzilla_cookie_list'}};
|
@{$cgi->{'Bugzilla_cookie_list'}};
|
||||||
$login_cookie = $cookie->value if $cookie;
|
$login_cookie = $cookie->value if $cookie;
|
||||||
|
}
|
||||||
|
unless ($user_id) {
|
||||||
|
my $cookie = first {$_->name eq 'Bugzilla_login'}
|
||||||
|
@{$cgi->{'Bugzilla_cookie_list'}};
|
||||||
|
$user_id = $cookie->value if $cookie;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
unless ($user_id) {
|
|
||||||
my $cookie = first {$_->name eq 'Bugzilla_login'}
|
# If no cookies were provided, we also look for a login token
|
||||||
@{$cgi->{'Bugzilla_cookie_list'}};
|
# passed in the parameters of a webservice
|
||||||
$user_id = $cookie->value if $cookie;
|
my $token = $self->login_token;
|
||||||
|
if ($token && (!$login_cookie || !$user_id)) {
|
||||||
|
($user_id, $login_cookie) = ($token->{'user_id'}, $token->{'login_token'});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $ip_addr = remote_ip();
|
||||||
|
|
||||||
if ($login_cookie && $user_id) {
|
if ($login_cookie && $user_id) {
|
||||||
# Anything goes for these params - they're just strings which
|
# Anything goes for these params - they're just strings which
|
||||||
# we're going to verify against the db
|
# we're going to verify against the db
|
||||||
@ -78,4 +90,32 @@ sub get_login_info {
|
|||||||
return { failure => AUTH_NODATA };
|
return { failure => AUTH_NODATA };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub login_token {
|
||||||
|
my ($self) = @_;
|
||||||
|
my $input = Bugzilla->input_params;
|
||||||
|
my $usage_mode = Bugzilla->usage_mode;
|
||||||
|
|
||||||
|
return $self->{'_login_token'} if exists $self->{'_login_token'};
|
||||||
|
|
||||||
|
if ($usage_mode ne USAGE_MODE_XMLRPC
|
||||||
|
&& $usage_mode ne USAGE_MODE_JSON
|
||||||
|
&& $usage_mode ne USAGE_MODE_REST) {
|
||||||
|
return $self->{'_login_token'} = undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if a token was passed in via requests for WebServices
|
||||||
|
my $token = trim(delete $input->{'Bugzilla_token'});
|
||||||
|
return $self->{'_login_token'} = undef if !$token;
|
||||||
|
|
||||||
|
my ($user_id, $login_token) = split('-', $token, 2);
|
||||||
|
if (!detaint_natural($user_id) || !$login_token) {
|
||||||
|
return $self->{'_login_token'} = undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $self->{'_login_token'} = {
|
||||||
|
user_id => $user_id,
|
||||||
|
login_token => $login_token
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|||||||
@ -15,6 +15,8 @@ use Bugzilla::Constants;
|
|||||||
use Bugzilla::Util;
|
use Bugzilla::Util;
|
||||||
use Bugzilla::Token;
|
use Bugzilla::Token;
|
||||||
|
|
||||||
|
use Bugzilla::Auth::Login::Cookie qw(login_token);
|
||||||
|
|
||||||
use List::Util qw(first);
|
use List::Util qw(first);
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
@ -86,6 +88,7 @@ sub logout {
|
|||||||
|
|
||||||
my $dbh = Bugzilla->dbh;
|
my $dbh = Bugzilla->dbh;
|
||||||
my $cgi = Bugzilla->cgi;
|
my $cgi = Bugzilla->cgi;
|
||||||
|
my $input = Bugzilla->input_params;
|
||||||
$param = {} unless $param;
|
$param = {} unless $param;
|
||||||
my $user = $param->{user} || Bugzilla->user;
|
my $user = $param->{user} || Bugzilla->user;
|
||||||
my $type = $param->{type} || LOGOUT_ALL;
|
my $type = $param->{type} || LOGOUT_ALL;
|
||||||
@ -99,16 +102,23 @@ sub logout {
|
|||||||
# The LOGOUT_*_CURRENT options require the current login cookie.
|
# The LOGOUT_*_CURRENT options require the current login cookie.
|
||||||
# If a new cookie has been issued during this run, that's the current one.
|
# If a new cookie has been issued during this run, that's the current one.
|
||||||
# If not, it's the one we've received.
|
# If not, it's the one we've received.
|
||||||
|
my @login_cookies;
|
||||||
my $cookie = first {$_->name eq 'Bugzilla_logincookie'}
|
my $cookie = first {$_->name eq 'Bugzilla_logincookie'}
|
||||||
@{$cgi->{'Bugzilla_cookie_list'}};
|
@{$cgi->{'Bugzilla_cookie_list'}};
|
||||||
my $login_cookie;
|
|
||||||
if ($cookie) {
|
if ($cookie) {
|
||||||
$login_cookie = $cookie->value;
|
push(@login_cookies, $cookie->value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$login_cookie = $cgi->cookie("Bugzilla_logincookie");
|
push(@login_cookies, $cgi->cookie("Bugzilla_logincookie"));
|
||||||
}
|
}
|
||||||
trick_taint($login_cookie);
|
|
||||||
|
# If we are a webservice using a token instead of cookie
|
||||||
|
# then add that as well to the login cookies to delete
|
||||||
|
if (my $login_token = $user->authorizer->login_token) {
|
||||||
|
push(@login_cookies, $login_token->{'login_token'});
|
||||||
|
}
|
||||||
|
|
||||||
|
return if !@login_cookies;
|
||||||
|
|
||||||
# These queries use both the cookie ID and the user ID as keys. Even
|
# These queries use both the cookie ID and the user ID as keys. Even
|
||||||
# though we know the userid must match, we still check it in the SQL
|
# though we know the userid must match, we still check it in the SQL
|
||||||
@ -117,12 +127,18 @@ sub logout {
|
|||||||
# logged in and got the same cookie, we could be logging the other
|
# logged in and got the same cookie, we could be logging the other
|
||||||
# user out here. Yes, this is very very very unlikely, but why take
|
# user out here. Yes, this is very very very unlikely, but why take
|
||||||
# chances? - bbaetz
|
# chances? - bbaetz
|
||||||
|
map { trick_taint($_) } @login_cookies;
|
||||||
|
@login_cookies = map { $dbh->quote($_) } @login_cookies;
|
||||||
if ($type == LOGOUT_KEEP_CURRENT) {
|
if ($type == LOGOUT_KEEP_CURRENT) {
|
||||||
$dbh->do("DELETE FROM logincookies WHERE cookie != ? AND userid = ?",
|
$dbh->do("DELETE FROM logincookies WHERE " .
|
||||||
undef, $login_cookie, $user->id);
|
$dbh->sql_in('cookie', \@login_cookies, 1) .
|
||||||
|
" AND userid = ?",
|
||||||
|
undef, $user->id);
|
||||||
} elsif ($type == LOGOUT_CURRENT) {
|
} elsif ($type == LOGOUT_CURRENT) {
|
||||||
$dbh->do("DELETE FROM logincookies WHERE cookie = ? AND userid = ?",
|
$dbh->do("DELETE FROM logincookies WHERE " .
|
||||||
undef, $login_cookie, $user->id);
|
$dbh->sql_in('cookie', \@login_cookies) .
|
||||||
|
" AND userid = ?",
|
||||||
|
undef, $user->id);
|
||||||
} else {
|
} else {
|
||||||
die("Invalid type $type supplied to logout()");
|
die("Invalid type $type supplied to logout()");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ BEGIN {
|
|||||||
|
|
||||||
use Bugzilla::Error;
|
use Bugzilla::Error;
|
||||||
use Bugzilla::WebService::Constants;
|
use Bugzilla::WebService::Constants;
|
||||||
use Bugzilla::WebService::Util qw(taint_data);
|
use Bugzilla::WebService::Util qw(taint_data fix_credentials);
|
||||||
use Bugzilla::Util;
|
use Bugzilla::Util;
|
||||||
|
|
||||||
use HTTP::Message;
|
use HTTP::Message;
|
||||||
@ -373,6 +373,10 @@ sub _argument_type_check {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Update the params to allow for several convenience key/values
|
||||||
|
# use for authentication
|
||||||
|
fix_credentials($params);
|
||||||
|
|
||||||
Bugzilla->input_params($params);
|
Bugzilla->input_params($params);
|
||||||
|
|
||||||
if ($self->request->method eq 'POST') {
|
if ($self->request->method eq 'POST') {
|
||||||
|
|||||||
@ -16,7 +16,7 @@ use Bugzilla;
|
|||||||
use Bugzilla::Constants;
|
use Bugzilla::Constants;
|
||||||
use Bugzilla::Error;
|
use Bugzilla::Error;
|
||||||
use Bugzilla::WebService::Constants;
|
use Bugzilla::WebService::Constants;
|
||||||
use Bugzilla::WebService::Util qw(taint_data);
|
use Bugzilla::WebService::Util qw(taint_data fix_credentials);
|
||||||
use Bugzilla::Util qw(correct_urlbase html_quote);
|
use Bugzilla::Util qw(correct_urlbase html_quote);
|
||||||
|
|
||||||
# Load resource modules
|
# Load resource modules
|
||||||
@ -69,7 +69,7 @@ sub handle {
|
|||||||
|
|
||||||
my $params = $self->_retrieve_json_params;
|
my $params = $self->_retrieve_json_params;
|
||||||
|
|
||||||
$self->_fix_credentials($params);
|
fix_credentials($params);
|
||||||
|
|
||||||
# Fix includes/excludes for each call
|
# Fix includes/excludes for each call
|
||||||
rest_include_exclude($params);
|
rest_include_exclude($params);
|
||||||
@ -131,7 +131,7 @@ sub response {
|
|||||||
|
|
||||||
# If accessing through web browser, then display in readable format
|
# If accessing through web browser, then display in readable format
|
||||||
if ($self->content_type eq 'text/html') {
|
if ($self->content_type eq 'text/html') {
|
||||||
$result = $self->json->pretty->canonical->encode($result);
|
$result = $self->json->pretty->canonical->allow_nonref->encode($result);
|
||||||
|
|
||||||
my $template = Bugzilla->template;
|
my $template = Bugzilla->template;
|
||||||
$content = "";
|
$content = "";
|
||||||
@ -162,8 +162,15 @@ sub handle_login {
|
|||||||
# explicitly gives that site their username and password. (This is
|
# explicitly gives that site their username and password. (This is
|
||||||
# particularly important for JSONP, which would allow a remote site
|
# particularly important for JSONP, which would allow a remote site
|
||||||
# to use private data without the user's knowledge, unless we had this
|
# to use private data without the user's knowledge, unless we had this
|
||||||
# protection in place.)
|
# protection in place.) We do allow this for GET /login as we need to
|
||||||
if (!grep($_ eq $self->request->method, ('POST', 'PUT'))) {
|
# for Bugzilla::Auth::Persist::Cookie to create a login cookie that we
|
||||||
|
# can also use for Bugzilla_token support. This is OK as it requires
|
||||||
|
# a login and password to be supplied and will fail if they are not
|
||||||
|
# valid for the user.
|
||||||
|
if (!grep($_ eq $self->request->method, ('POST', 'PUT'))
|
||||||
|
&& !($self->bz_class_name eq 'Bugzilla::WebService::User'
|
||||||
|
&& $self->bz_method_name eq 'login'))
|
||||||
|
{
|
||||||
# XXX There's no particularly good way for us to get a parameter
|
# XXX There's no particularly good way for us to get a parameter
|
||||||
# to Bugzilla->login at this point, so we pass this information
|
# to Bugzilla->login at this point, so we pass this information
|
||||||
# around using request_cache, which is a bit of a hack. The
|
# around using request_cache, which is a bit of a hack. The
|
||||||
@ -424,15 +431,6 @@ sub _find_resource {
|
|||||||
return $handler_found;
|
return $handler_found;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _fix_credentials {
|
|
||||||
my ($self, $params) = @_;
|
|
||||||
# Allow user to pass in &username=foo&password=bar
|
|
||||||
if (exists $params->{'username'} && exists $params->{'password'}) {
|
|
||||||
$params->{'Bugzilla_login'} = delete $params->{'username'};
|
|
||||||
$params->{'Bugzilla_password'} = delete $params->{'password'};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _best_content_type {
|
sub _best_content_type {
|
||||||
my ($self, @types) = @_;
|
my ($self, @types) = @_;
|
||||||
return ($self->_simple_content_negotiation(@types))[0] || '*/*';
|
return ($self->_simple_content_negotiation(@types))[0] || '*/*';
|
||||||
@ -545,15 +543,23 @@ if you have a Bugzilla account by providing your login credentials.
|
|||||||
|
|
||||||
=over
|
=over
|
||||||
|
|
||||||
=item Username and password
|
=item Login name and password
|
||||||
|
|
||||||
Pass in as query parameters of any request:
|
Pass in as query parameters of any request:
|
||||||
|
|
||||||
username=fred@bedrock.com&password=ilovewilma
|
login=fred@example.com&password=ilovecheese
|
||||||
|
|
||||||
Remember to URL encode any special characters, which are often seen in passwords and to
|
Remember to URL encode any special characters, which are often seen in passwords and to
|
||||||
also enable SSL support.
|
also enable SSL support.
|
||||||
|
|
||||||
|
=item Login token
|
||||||
|
|
||||||
|
By calling GET /login?login=fred@example.com&password=ilovecheese, you get back
|
||||||
|
a C<token> value which can then be passed to each subsequent call as
|
||||||
|
authentication. This is useful for third party clients that cannot use cookies
|
||||||
|
and do not want to store a user's login and password in the client. You can also
|
||||||
|
pass in "token" as a convenience.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=head1 ERRORS
|
=head1 ERRORS
|
||||||
|
|||||||
@ -19,6 +19,16 @@ BEGIN {
|
|||||||
|
|
||||||
sub _rest_resources {
|
sub _rest_resources {
|
||||||
my $rest_resources = [
|
my $rest_resources = [
|
||||||
|
qr{^/login$}, {
|
||||||
|
GET => {
|
||||||
|
method => 'login'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
qr{^/logout$}, {
|
||||||
|
GET => {
|
||||||
|
method => 'logout'
|
||||||
|
}
|
||||||
|
},
|
||||||
qr{^/valid_login$}, {
|
qr{^/valid_login$}, {
|
||||||
GET => {
|
GET => {
|
||||||
method => 'valid_login'
|
method => 'valid_login'
|
||||||
|
|||||||
@ -19,6 +19,8 @@ use Bugzilla::User;
|
|||||||
use Bugzilla::Util qw(trim);
|
use Bugzilla::Util qw(trim);
|
||||||
use Bugzilla::WebService::Util qw(filter validate translate params_to_objects);
|
use Bugzilla::WebService::Util qw(filter validate translate params_to_objects);
|
||||||
|
|
||||||
|
use List::Util qw(first);
|
||||||
|
|
||||||
# Don't need auth to login
|
# Don't need auth to login
|
||||||
use constant LOGIN_EXEMPT => {
|
use constant LOGIN_EXEMPT => {
|
||||||
login => 1,
|
login => 1,
|
||||||
@ -73,14 +75,25 @@ sub login {
|
|||||||
$input_params->{'Bugzilla_password'} = $params->{password};
|
$input_params->{'Bugzilla_password'} = $params->{password};
|
||||||
$input_params->{'Bugzilla_remember'} = $remember;
|
$input_params->{'Bugzilla_remember'} = $remember;
|
||||||
|
|
||||||
Bugzilla->login();
|
my $user = Bugzilla->login();
|
||||||
return { id => $self->type('int', Bugzilla->user->id) };
|
|
||||||
|
my $result = { id => $self->type('int', $user->id) };
|
||||||
|
|
||||||
|
# We will use the stored cookie value combined with the user id
|
||||||
|
# to create a token that can be used with future requests in the
|
||||||
|
# query parameters
|
||||||
|
my $login_cookie = first { $_->name eq 'Bugzilla_logincookie' }
|
||||||
|
@{ Bugzilla->cgi->{'Bugzilla_cookie_list'} };
|
||||||
|
if ($login_cookie) {
|
||||||
|
$result->{'token'} = $user->id . "-" . $login_cookie->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub logout {
|
sub logout {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Bugzilla->logout;
|
Bugzilla->logout;
|
||||||
return undef;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub valid_login {
|
sub valid_login {
|
||||||
@ -448,10 +461,12 @@ management of cookies across sessions.
|
|||||||
|
|
||||||
=item B<Returns>
|
=item B<Returns>
|
||||||
|
|
||||||
On success, a hash containing one item, C<id>, the numeric id of the
|
On success, a hash containing two items, C<id>, the numeric id of the
|
||||||
user that was logged in. A set of http cookies is also sent with the
|
user that was logged in, and a C<token> which can be passed in
|
||||||
response. These cookies must be sent along with any future requests
|
the parameters as authentication in other calls. A set of http cookies
|
||||||
to the webservice, for the duration of the session.
|
is also sent with the response. These cookies *or* the token can be sent
|
||||||
|
along with any future requests to the webservice, for the duration of the
|
||||||
|
session.
|
||||||
|
|
||||||
=item B<Errors>
|
=item B<Errors>
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,7 @@ our @EXPORT_OK = qw(
|
|||||||
validate
|
validate
|
||||||
translate
|
translate
|
||||||
params_to_objects
|
params_to_objects
|
||||||
|
fix_credentials
|
||||||
);
|
);
|
||||||
|
|
||||||
sub filter ($$;$) {
|
sub filter ($$;$) {
|
||||||
@ -146,6 +147,22 @@ sub params_to_objects {
|
|||||||
return \@objects;
|
return \@objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub fix_credentials {
|
||||||
|
my ($params) = @_;
|
||||||
|
# Allow user to pass in login=foo&password=bar as a convenience
|
||||||
|
# even if not calling GET /login. We also do not delete them as
|
||||||
|
# GET /login requires "login" and "password".
|
||||||
|
if (exists $params->{'login'} && exists $params->{'password'}) {
|
||||||
|
$params->{'Bugzilla_login'} = $params->{'login'};
|
||||||
|
$params->{'Bugzilla_password'} = $params->{'password'};
|
||||||
|
}
|
||||||
|
# Allow user to pass token=12345678 as a convenience which becomes
|
||||||
|
# "Bugzilla_token" which is what the auth code looks for.
|
||||||
|
if (exists $params->{'token'}) {
|
||||||
|
$params->{'Bugzilla_token'} = $params->{'token'};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
|
||||||
=head1 NAME
|
=head1 NAME
|
||||||
@ -209,6 +226,12 @@ Helps make life simpler for WebService methods that internally create objects
|
|||||||
via both "ids" and "names" fields. Also de-duplicates objects that were loaded
|
via both "ids" and "names" fields. Also de-duplicates objects that were loaded
|
||||||
by both "ids" and "names". Returns an arrayref of objects.
|
by both "ids" and "names". Returns an arrayref of objects.
|
||||||
|
|
||||||
|
=head2 fix_credentials
|
||||||
|
|
||||||
|
Allows for certain parameters related to authentication such as Bugzilla_login,
|
||||||
|
Bugzilla_password, and Bugzilla_token to have shorter named equivalents passed in.
|
||||||
|
This function converts the shorter versions to their respective internal names.
|
||||||
|
|
||||||
=head1 B<Methods in need of POD>
|
=head1 B<Methods in need of POD>
|
||||||
|
|
||||||
=over
|
=over
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user