Fix "email" type for merged XML-RPC webservices, update Bugzilla WS and XMLRPC subclass

classes
Vitaliy Filippov 2015-11-05 13:32:53 +03:00
parent cb5b2f06e0
commit 58346deb00
6 changed files with 611 additions and 112 deletions

View File

@ -45,6 +45,12 @@ sub type {
if ($type eq 'dateTime') {
$value = $self->datetime_format_outbound($value);
}
elsif ($type eq 'email') {
$type = 'string';
if (Bugzilla->params->{'webservice_email_filter'}) {
$value = email_filter($value);
}
}
return XMLRPC::Data->type($type)->value($value);
}

View File

@ -1,43 +1,90 @@
# -*- Mode: perl; indent-tabs-mode: nil -*-
# 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/.
#
# 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 Bugzilla Bug Tracking System.
#
# Contributor(s): Marc Schumann <wurblzap@gmail.com>
# Max Kanat-Alexander <mkanat@bugzilla.org>
# Mads Bondo Dydensborg <mbd@dbc.dk>
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
package Bugzilla::WebService::Bugzilla;
use 5.10.1;
use strict;
use warnings;
use base qw(Bugzilla::WebService);
use Bugzilla::Constants;
use Bugzilla::Util qw(datetime_from);
use Bugzilla::WebService::Util qw(validate filter_wants);
use Bugzilla::Util qw(trick_taint);
use DateTime;
# Basic info that is needed before logins
use constant LOGIN_EXEMPT => {
parameters => 1,
timezone => 1,
version => 1,
};
use constant READ_ONLY => qw(
extensions
parameters
timezone
time
version
);
use constant PUBLIC_METHODS => qw(
extensions
last_audit_time
parameters
time
timezone
version
);
# Logged-out users do not need to know more than that.
use constant PARAMETERS_LOGGED_OUT => qw(
maintainer
requirelogin
);
# These parameters are guessable from the web UI when the user
# is logged in. So it's safe to access them.
use constant PARAMETERS_LOGGED_IN => qw(
allowemailchange
attachment_base
commentonchange_resolution
commentonduplicate
cookiepath
defaultopsys
defaultplatform
defaultpriority
defaultseverity
duplicate_or_move_bug_status
emailregexpdesc
emailsuffix
letsubmitterchoosemilestone
letsubmitterchoosepriority
mailfrom
maintainer
maxattachmentsize
maxlocalattachment
musthavemilestoneonaccept
noresolveonopenblockers
password_complexity
rememberlogin
requirelogin
search_allow_no_criteria
urlbase
use_see_also
useclassification
usemenuforusers
useqacontact
usestatuswhiteboard
usetargetmilestone
);
sub version {
my $self = shift;
return { version => $self->type('string', BUGZILLA_VERSION) };
@ -74,6 +121,32 @@ sub time {
};
}
sub last_audit_time {
my ($self, $params) = validate(@_, 'class');
return {
last_audit_time => undef
};
}
sub parameters {
my ($self, $args) = @_;
my $user = Bugzilla->login(LOGIN_OPTIONAL);
my $params = Bugzilla->params;
$args ||= {};
my @params_list = $user->in_group('tweakparams')
? keys(%$params)
: $user->id ? PARAMETERS_LOGGED_IN : PARAMETERS_LOGGED_OUT;
my %parameters;
foreach my $param (@params_list) {
next unless filter_wants($args, $param);
$parameters{$param} = $self->type('string', $params->{$param});
}
return { parameters => \%parameters };
}
1;
__END__
@ -91,9 +164,11 @@ This provides functions that tell you about Bugzilla in general.
See L<Bugzilla::WebService> for a description of how parameters are passed,
and what B<STABLE>, B<UNSTABLE>, and B<EXPERIMENTAL> mean.
=over
Although the data input and output is the same for JSONRPC, XMLRPC and REST,
the directions for how to access the data via REST is noted in each method
where applicable.
=item C<version>
=head2 version
B<STABLE>
@ -103,6 +178,12 @@ B<STABLE>
Returns the current version of Bugzilla.
=item B<REST>
GET /rest/version
The returned data format is the same as below.
=item B<Params> (none)
=item B<Returns>
@ -112,9 +193,17 @@ string.
=item B<Errors> (none)
=item B<History>
=over
=item REST API call added in Bugzilla B<5.0>.
=back
=item C<extensions>
=back
=head2 extensions
B<EXPERIMENTAL>
@ -125,6 +214,12 @@ B<EXPERIMENTAL>
Gets information about the extensions that are currently installed and enabled
in this Bugzilla.
=item B<REST>
GET /rest/extensions
The returned data format is the same as below.
=item B<Params> (none)
=item B<Returns>
@ -155,11 +250,13 @@ The return value looks something like this:
that the extensions define themselves. Before 3.6, the names of the
extensions depended on the directory they were in on the Bugzilla server.
=item REST API call added in Bugzilla B<5.0>.
=back
=back
=item C<timezone>
=head2 timezone
B<DEPRECATED> This method may be removed in a future version of Bugzilla.
Use L</time> instead.
@ -170,6 +267,12 @@ Use L</time> instead.
Returns the timezone that Bugzilla expects dates and times in.
=item B<REST>
GET /rest/timezone
The returned data format is the same as below.
=item B<Params> (none)
=item B<Returns>
@ -184,12 +287,14 @@ string in (+/-)XXXX (RFC 2822) format.
=item As of Bugzilla B<3.6>, the timezone returned is always C<+0000>
(the UTC timezone).
=item REST API call added in Bugzilla B<5.0>.
=back
=back
=item C<time>
=head2 time
B<STABLE>
@ -200,6 +305,12 @@ B<STABLE>
Gets information about what time the Bugzilla server thinks it is, and
what timezone it's running in.
=item B<REST>
GET /rest/time
The returned data format is the same as below.
=item B<Params> (none)
=item B<Returns>
@ -210,7 +321,7 @@ A struct with the following items:
=item C<db_time>
C<dateTime> The current time in UTC, according to the Bugzilla
C<dateTime> The current time in UTC, according to the Bugzilla
I<database server>.
Note that Bugzilla assumes that the database and the webserver are running
@ -220,7 +331,7 @@ rely on for doing searches and other input to the WebService.
=item C<web_time>
C<dateTime> This is the current time in UTC, according to Bugzilla's
C<dateTime> This is the current time in UTC, according to Bugzilla's
I<web server>.
This might be different by a second from C<db_time> since this comes from
@ -236,7 +347,7 @@ versions of Bugzilla before 3.6.)
=item C<tz_name>
C<string> The literal string C<UTC>. (Exists only for backwards-compatibility
with versions of Bugzilla before 3.6.)
with versions of Bugzilla before 3.6.)
=item C<tz_short_name>
@ -260,9 +371,136 @@ with versions of Bugzilla before 3.6.)
were in the UTC timezone, instead of returning information in the server's
local timezone.
=item REST API call added in Bugzilla B<5.0>.
=back
=back
=head2 parameters
B<UNSTABLE>
=over
=item B<Description>
Returns parameter values currently used in this Bugzilla.
=item B<REST>
GET /rest/parameters
The returned data format is the same as below.
=item B<Params> (none)
=item B<Returns>
A hash with a single item C<parameters> which contains a hash with
the name of the parameters as keys and their value as values. All
values are returned as strings.
The list of parameters returned by this method depends on the user
credentials:
A logged-out user can only access the C<maintainer> and C<requirelogin> parameters.
A logged-in user can access the following parameters (listed alphabetically):
C<allowemailchange>,
C<attachment_base>,
C<commentonchange_resolution>,
C<commentonduplicate>,
C<cookiepath>,
C<defaultopsys>,
C<defaultplatform>,
C<defaultpriority>,
C<defaultseverity>,
C<duplicate_or_move_bug_status>,
C<emailregexpdesc>,
C<emailsuffix>,
C<letsubmitterchoosemilestone>,
C<letsubmitterchoosepriority>,
C<mailfrom>,
C<maintainer>,
C<maxattachmentsize>,
C<maxlocalattachment>,
C<musthavemilestoneonaccept>,
C<noresolveonopenblockers>,
C<password_complexity>,
C<rememberlogin>,
C<requirelogin>,
C<search_allow_no_criteria>,
C<urlbase>,
C<use_see_also>,
C<useclassification>,
C<usemenuforusers>,
C<useqacontact>,
C<usestatuswhiteboard>,
C<usetargetmilestone>.
A user in the tweakparams group can access all existing parameters.
New parameters can appear or obsolete parameters can disappear depending
on the version of Bugzilla and on extensions being installed.
The list of parameters returned by this method is not stable and will
never be stable.
=item B<History>
=over
=item Added in Bugzilla B<4.4>.
=item REST API call added in Bugzilla B<5.0>.
=back
=back
=head2 last_audit_time
B<EXPERIMENTAL>
=over
=item B<Description>
Gets the latest time of the audit_log table.
=item B<REST>
GET /rest/last_audit_time
The returned data format is the same as below.
=item B<Params>
You can pass the optional parameter C<class> to get the maximum for only
the listed classes.
=over
=item C<class> (array) - An array of strings representing the class names.
B<Note:> The class names are defined as "Bugzilla::<class_name>". For the product
use Bugzilla:Product.
=back
=item B<Returns>
A hash with a single item, C<last_audit_time>, that is the maximum of the
at_time from the audit_log.
=item B<Errors> (none)
=item B<History>
=over
=item Added in Bugzilla B<4.4>.
=item REST API call added in Bugzilla B<5.0>.
=back
=back

View File

@ -1,30 +1,37 @@
# -*- Mode: perl; indent-tabs-mode: nil -*-
# 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/.
#
# 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 Bugzilla Bug Tracking System.
#
# Contributor(s): Marc Schumann <wurblzap@gmail.com>
# Max Kanat-Alexander <mkanat@bugzilla.org>
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
package Bugzilla::WebService::Constants;
use 5.10.1;
use strict;
use warnings;
use base qw(Exporter);
our @EXPORT = qw(
WS_ERROR_CODE
STATUS_OK
STATUS_CREATED
STATUS_ACCEPTED
STATUS_NO_CONTENT
STATUS_MULTIPLE_CHOICES
STATUS_BAD_REQUEST
STATUS_NOT_FOUND
STATUS_GONE
REST_STATUS_CODE_MAP
ERROR_UNKNOWN_FATAL
ERROR_UNKNOWN_TRANSIENT
XMLRPC_CONTENT_TYPE_WHITELIST
REST_CONTENT_TYPE_WHITELIST
WS_DISPATCH
);
@ -49,13 +56,17 @@ our @EXPORT = qw(
use constant WS_ERROR_CODE => {
# Generic errors (Bugzilla::Object and others) are 50-99.
object_not_specified => 50,
reassign_to_empty => 50,
param_required => 50,
params_required => 50,
undefined_field => 50,
object_does_not_exist => 51,
param_must_be_numeric => 52,
number_not_numeric => 52,
param_invalid => 53,
number_too_large => 54,
number_too_small => 55,
illegal_date => 56,
# Bug errors usually occupy the 100-200 range.
improper_bug_id_field_value => 100,
bug_id_does_not_exist => 101,
@ -66,12 +77,14 @@ use constant WS_ERROR_CODE => {
alias_in_use => 103,
alias_is_numeric => 103,
alias_has_comma_or_space => 103,
multiple_alias_not_allowed => 103,
# Misc. bug field errors
illegal_field => 104,
freetext_too_long => 104,
# Component errors
require_component => 105,
component_name_too_long => 105,
require_component => 105,
component_name_too_long => 105,
product_unknown_component => 105,
# Invalid Product
no_products => 106,
entry_access_denied => 106,
@ -87,6 +100,12 @@ use constant WS_ERROR_CODE => {
comment_is_private => 110,
comment_id_invalid => 111,
comment_too_long => 114,
comment_invalid_isprivate => 117,
# Comment tagging
comment_tag_disabled => 125,
comment_tag_invalid => 126,
comment_tag_too_long => 127,
comment_tag_too_short => 128,
# See Also errors
bug_url_invalid => 112,
bug_url_too_long => 112,
@ -95,14 +114,39 @@ use constant WS_ERROR_CODE => {
# Note: 114 is above in the Comment-related section.
# Bug update errors
illegal_change => 115,
# Dependency errors
dependency_loop_single => 116,
dependency_loop_multi => 116,
# Note: 117 is above in the Comment-related section.
# Dup errors
dupe_loop_detected => 118,
dupe_id_required => 119,
# Bug-related group errors
group_invalid_removal => 120,
group_restriction_not_allowed => 120,
# Status/Resolution errors
missing_resolution => 121,
resolution_not_allowed => 122,
illegal_bug_status_transition => 123,
# Flag errors
flag_status_invalid => 129,
flag_update_denied => 130,
flag_type_requestee_disabled => 131,
flag_not_unique => 132,
flag_type_not_unique => 133,
flag_type_inactive => 134,
# Authentication errors are usually 300-400.
invalid_username_or_password => 300,
invalid_login_or_password => 300,
account_disabled => 301,
auth_invalid_email => 302,
extern_id_conflict => -303,
auth_failure => 304,
password_current_too_short => 305,
password_too_short => 305,
password_not_complex => 305,
api_key_not_valid => 306,
api_key_revoked => 306,
auth_invalid_token => 307,
# Except, historically, AUTH_NODATA, which is 410.
login_required => 410,
@ -122,10 +166,105 @@ use constant WS_ERROR_CODE => {
user_access_by_id_denied => 505,
user_access_by_match_denied => 505,
# RPC Server Errors. See the following URL:
# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
xmlrpc_invalid_value => -32600,
unknown_method => -32601,
# Attachment errors are 600-700.
file_too_large => 600,
invalid_content_type => 601,
# Error 602 attachment_illegal_url no longer exists.
file_not_specified => 603,
missing_attachment_description => 604,
# Error 605 attachment_url_disabled no longer exists.
zero_length_file => 606,
# Product erros are 700-800
product_blank_name => 700,
product_name_too_long => 701,
product_name_already_in_use => 702,
product_name_diff_in_case => 702,
product_must_have_description => 703,
product_must_have_version => 704,
product_must_define_defaultmilestone => 705,
# Group errors are 800-900
empty_group_name => 800,
group_exists => 801,
empty_group_description => 802,
invalid_regexp => 803,
invalid_group_name => 804,
group_cannot_view => 805,
# Classification errors are 900-1000
auth_classification_not_enabled => 900,
# Search errors are 1000-1100
buglist_parameters_required => 1000,
# Flag type errors are 1100-1200
flag_type_name_invalid => 1101,
flag_type_description_invalid => 1102,
flag_type_cc_list_invalid => 1103,
flag_type_sortkey_invalid => 1104,
flag_type_not_editable => 1105,
# Component errors are 1200-1300
component_already_exists => 1200,
component_is_last => 1201,
component_has_bugs => 1202,
# Errors thrown by the WebService itself. The ones that are negative
# conform to http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
xmlrpc_invalid_value => -32600,
unknown_method => -32601,
json_rpc_post_only => 32610,
json_rpc_invalid_callback => 32611,
xmlrpc_illegal_content_type => 32612,
json_rpc_illegal_content_type => 32613,
rest_invalid_resource => 32614,
};
# RESTful webservices use the http status code
# to describe whether a call was successful or
# to describe the type of error that occurred.
use constant STATUS_OK => 200;
use constant STATUS_CREATED => 201;
use constant STATUS_ACCEPTED => 202;
use constant STATUS_NO_CONTENT => 204;
use constant STATUS_MULTIPLE_CHOICES => 300;
use constant STATUS_BAD_REQUEST => 400;
use constant STATUS_NOT_AUTHORIZED => 401;
use constant STATUS_NOT_FOUND => 404;
use constant STATUS_GONE => 410;
# The integer value is the error code above returned by
# the related webvservice call. We choose the appropriate
# http status code based on the error code or use the
# default STATUS_BAD_REQUEST.
sub REST_STATUS_CODE_MAP {
my $status_code_map = {
51 => STATUS_NOT_FOUND,
101 => STATUS_NOT_FOUND,
102 => STATUS_NOT_AUTHORIZED,
106 => STATUS_NOT_AUTHORIZED,
109 => STATUS_NOT_AUTHORIZED,
110 => STATUS_NOT_AUTHORIZED,
113 => STATUS_NOT_AUTHORIZED,
115 => STATUS_NOT_AUTHORIZED,
120 => STATUS_NOT_AUTHORIZED,
300 => STATUS_NOT_AUTHORIZED,
301 => STATUS_NOT_AUTHORIZED,
302 => STATUS_NOT_AUTHORIZED,
303 => STATUS_NOT_AUTHORIZED,
304 => STATUS_NOT_AUTHORIZED,
410 => STATUS_NOT_AUTHORIZED,
504 => STATUS_NOT_AUTHORIZED,
505 => STATUS_NOT_AUTHORIZED,
32614 => STATUS_NOT_FOUND,
_default => STATUS_BAD_REQUEST
};
Bugzilla::Hook::process('webservice_status_code_map',
{ status_code_map => $status_code_map });
return $status_code_map;
};
# These are the fallback defaults for errors not in ERROR_CODE.
@ -134,6 +273,19 @@ use constant ERROR_UNKNOWN_TRANSIENT => 32000;
use constant ERROR_GENERAL => 999;
use constant XMLRPC_CONTENT_TYPE_WHITELIST => qw(
text/xml
application/xml
);
# The first content type specified is used as the default.
use constant REST_CONTENT_TYPE_WHITELIST => qw(
application/json
application/javascript
text/javascript
text/html
);
sub WS_DISPATCH {
# We "require" here instead of "use" above to avoid a dependency loop.
require Bugzilla::Hook;
@ -141,14 +293,28 @@ sub WS_DISPATCH {
Bugzilla::Hook::process('webservice', { dispatch => \%hook_dispatch });
my $dispatch = {
'Bugzilla' => 'Bugzilla::WebService::Bugzilla',
'Bug' => 'Bugzilla::WebService::Bug',
'User' => 'Bugzilla::WebService::User',
'Product' => 'Bugzilla::WebService::Product',
'Field' => 'Bugzilla::WebService::Field',
'Bugzilla' => 'Bugzilla::WebService::Bugzilla',
'Bug' => 'Bugzilla::WebService::Bug',
'Classification' => 'Bugzilla::WebService::Classification',
'Component' => 'Bugzilla::WebService::Component',
'FlagType' => 'Bugzilla::WebService::FlagType',
'Group' => 'Bugzilla::WebService::Group',
'Product' => 'Bugzilla::WebService::Product',
'User' => 'Bugzilla::WebService::User',
'Field' => 'Bugzilla::WebService::Field',
%hook_dispatch
};
return $dispatch;
};
1;
=head1 B<Methods in need of POD>
=over
=item REST_STATUS_CODE_MAP
=item WS_DISPATCH
=back

View File

@ -1,62 +1,45 @@
# -*- Mode: perl; indent-tabs-mode: nil -*-
# 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/.
#
# 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 Bugzilla Bug Tracking System.
#
# Contributor(s): Marc Schumann <wurblzap@gmail.com>
# Max Kanat-Alexander <mkanat@bugzilla.org>
# Rosie Clarkson <rosie.clarkson@planningportal.gov.uk>
#
# Portions © Crown copyright 2009 - Rosie Clarkson (development@planningportal.gov.uk) for the Planning Portal
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
package Bugzilla::WebService::Server::XMLRPC;
use 5.10.1;
use strict;
use SOAP::Transport::HTTP;
use warnings;
use XMLRPC::Transport::HTTP;
use Bugzilla::WebService::Server;
our @ISA = qw(Bugzilla::WebService::Server);
push @ISA, 'XMLRPC::Transport::HTTP::' . ($ENV{MOD_PERL} ? 'Apache' : 'CGI');
if ($ENV{MOD_PERL}) {
our @ISA = qw(XMLRPC::Transport::HTTP::Apache Bugzilla::WebService::Server);
} else {
our @ISA = qw(XMLRPC::Transport::HTTP::CGI Bugzilla::WebService::Server);
}
use Bugzilla::WebService::Constants;
use Bugzilla::Error;
use Bugzilla::Util;
sub initialize {
my $self = shift;
my %retval = $self->SUPER::initialize(@_);
$retval{'serializer'} = Bugzilla::XMLRPC::Serializer->new;
$retval{'deserializer'} = Bugzilla::XMLRPC::Deserializer->new;
$retval{'dispatch_with'} = WS_DISPATCH;
return %retval;
}
use List::MoreUtils qw(none);
sub make_response {
my $self = shift;
$self->SUPER::make_response(@_);
# XMLRPC::Transport::HTTP::CGI doesn't know about Bugzilla carrying around
# its cookies in Bugzilla::CGI, so we need to copy them over.
foreach (@{Bugzilla->cgi->{'Bugzilla_cookie_list'}}) {
$self->response->headers->push_header('Set-Cookie', $_);
sub type {
my ($self, $type, $value) = @_;
if ($type eq 'dateTime') {
# This is the XML-RPC implementation, see the README in Bugzilla/WebService/.
# Our "base" implementation is in Bugzilla::WebService::Server.
$value = Bugzilla::WebService::Server->datetime_format_outbound($value);
$value =~ s/-//g;
}
}
sub handle_login {
my ($self, $classes, $action, $uri, $method) = @_;
my $class = $classes->{$uri};
my $full_method = $uri . "." . $method;
$self->SUPER::handle_login($class, $method, $full_method);
return;
elsif ($type eq 'email') {
$type = 'string';
if (Bugzilla->params->{'webservice_email_filter'}) {
$value = email_filter($value);
}
}
return XMLRPC::Data->type($type)->value($value);
}
# Patch SOAP::Transport::HTTP::CGI so it works under CGI like HTTP::Server::Simple
@ -147,22 +130,101 @@ sub handle_login {
$self->response->content;
};
sub initialize {
my $self = shift;
my %retval = $self->SUPER::initialize(@_);
$retval{'serializer'} = Bugzilla::XMLRPC::Serializer->new;
$retval{'deserializer'} = Bugzilla::XMLRPC::Deserializer->new;
$retval{'dispatch_with'} = WS_DISPATCH;
return %retval;
}
sub make_response {
my $self = shift;
my $cgi = Bugzilla->cgi;
# Fix various problems with IIS.
if ($ENV{'SERVER_SOFTWARE'} =~ /IIS/) {
$ENV{CONTENT_LENGTH} = 0;
binmode(STDOUT, ':bytes');
}
$self->SUPER::make_response(@_);
# XMLRPC::Transport::HTTP::CGI doesn't know about Bugzilla carrying around
# its cookies in Bugzilla::CGI, so we need to copy them over.
foreach my $cookie (@{$cgi->{'Bugzilla_cookie_list'}}) {
$self->response->headers->push_header('Set-Cookie', $cookie);
}
# Copy across security related headers from Bugzilla::CGI
foreach my $header (split(/[\r\n]+/, $cgi->header)) {
my ($name, $value) = $header =~ /^([^:]+): (.*)/;
if ($name && !$self->response->headers->header($name)) {
$self->response->headers->header($name => $value);
}
}
}
sub handle_login {
my ($self, $classes, $action, $uri, $method) = @_;
my $class = $classes->{$uri};
my $full_method = $uri . "." . $method;
# Only allowed methods to be used from the module's whitelist
my $file = $class;
$file =~ s{::}{/}g;
$file .= ".pm";
require $file;
if (none { $_ eq $method } $class->PUBLIC_METHODS) {
ThrowCodeError('unknown_method', { method => $full_method });
}
$ENV{CONTENT_LENGTH} = 0 if $ENV{'SERVER_SOFTWARE'} =~ /IIS/;
$self->SUPER::handle_login($class, $method, $full_method);
return;
}
1;
# This exists to validate input parameters (which XMLRPC::Lite doesn't do)
# and also, in some cases, to more-usefully decode them.
package Bugzilla::XMLRPC::Deserializer;
use 5.10.1;
use strict;
# We can't use "use base" because XMLRPC::Serializer doesn't return
use warnings;
# We can't use "use parent" because XMLRPC::Serializer doesn't return
# a true value.
use XMLRPC::Lite;
our @ISA = qw(XMLRPC::Deserializer);
use Bugzilla::Error;
use Bugzilla::WebService::Constants qw(XMLRPC_CONTENT_TYPE_WHITELIST);
use Bugzilla::WebService::Util qw(fix_credentials);
use Scalar::Util qw(tainted);
sub new {
my $self = shift->SUPER::new(@_);
# Initialise XML::Parser to not expand references to entities, to prevent DoS
require XML::Parser;
my $parser = XML::Parser->new( NoExpand => 1, Handlers => { Default => sub {} } );
$self->{_parser}->parser($parser, $parser);
return $self;
}
sub deserialize {
my $self = shift;
# Only allow certain content types to protect against CSRF attacks
my $content_type = lc($ENV{'CONTENT_TYPE'});
# Remove charset, etc, if provided
$content_type =~ s/^([^;]+);.*/$1/;
if (!grep($_ eq $content_type, XMLRPC_CONTENT_TYPE_WHITELIST)) {
ThrowUserError('xmlrpc_illegal_content_type',
{ content_type => $ENV{'CONTENT_TYPE'} });
}
my ($xml) = @_;
my $som = $self->SUPER::deserialize(@_);
if (tainted($xml)) {
@ -172,7 +234,13 @@ sub deserialize {
my $params = $som->paramsin;
# This allows positional parameters for Testopia.
$params = {} if ref $params ne 'HASH';
# Update the params to allow for several convenience key/values
# use for authentication
fix_credentials($params);
Bugzilla->input_params($params);
return $som;
}
@ -236,7 +304,11 @@ sub _validation_subs {
1;
package Bugzilla::XMLRPC::SOM;
use 5.10.1;
use strict;
use warnings;
use XMLRPC::Lite;
our @ISA = qw(XMLRPC::SOM);
use Bugzilla::WebService::Util qw(taint_data);
@ -259,9 +331,13 @@ sub paramsin {
# This package exists to fix a UTF-8 bug in SOAP::Lite.
# See http://rt.cpan.org/Public/Bug/Display.html?id=32952.
package Bugzilla::XMLRPC::Serializer;
use Scalar::Util qw(blessed);
use 5.10.1;
use strict;
# We can't use "use base" because XMLRPC::Serializer doesn't return
use warnings;
use Scalar::Util qw(blessed reftype);
# We can't use "use parent" because XMLRPC::Serializer doesn't return
# a true value.
use XMLRPC::Lite;
our @ISA = qw(XMLRPC::Serializer);
@ -294,8 +370,8 @@ sub envelope {
my $self = shift;
my ($type, $method, $data) = @_;
# If the type isn't a successful response we don't want to change the values.
if ($type eq 'response'){
$data = _strip_undefs($data);
if ($type eq 'response') {
_strip_undefs($data);
}
return $self->SUPER::envelope($type, $method, $data);
}
@ -306,7 +382,9 @@ sub envelope {
# so it cannot be recursed like the other hash type objects.
sub _strip_undefs {
my ($initial) = @_;
if (ref $initial eq "HASH" || (blessed $initial && $initial->isa("HASH"))) {
my $type = reftype($initial) or return;
if ($type eq "HASH") {
while (my ($key, $value) = each(%$initial)) {
if ( !defined $value
|| (blessed $value && $value->isa('XMLRPC::Data') && !defined $value->value) )
@ -315,11 +393,11 @@ sub _strip_undefs {
delete $initial->{$key};
}
else {
$initial->{$key} = _strip_undefs($value);
_strip_undefs($value);
}
}
}
if (ref $initial eq "ARRAY" || (blessed $initial && $initial->isa("ARRAY"))) {
elsif ($type eq "ARRAY") {
for (my $count = 0; $count < scalar @{$initial}; $count++) {
my $value = $initial->[$count];
if ( !defined $value
@ -330,11 +408,10 @@ sub _strip_undefs {
$count--;
}
else {
$initial->[$count] = _strip_undefs($value);
_strip_undefs($value);
}
}
}
return $initial;
}
sub BEGIN {
@ -436,3 +513,15 @@ perl-SOAP-Lite package in versions 0.68-1 and above.
=head1 SEE ALSO
L<Bugzilla::WebService>
=head1 B<Methods in need of POD>
=over
=item make_response
=item initialize
=item handle_login
=back

View File

@ -391,8 +391,8 @@ sub _user_in_any_group {
sub read_new_functionality {
my $time = time;
my $cgi = Bugzilla->cgi;
my @lu = map { $_ - 0} Bugzilla->params->{new_functionality_tsp} =~ m/(\d+)/g;
my $last_updated = POSIX::mktime(@lu[5], @lu[4], @lu[3], @lu[2], @lu[1] - 1, @lu[0] - 1900);
my @lu = map { $_ - 0 } Bugzilla->params->{new_functionality_tsp} =~ m/(\d+)/g;
my $last_updated = POSIX::mktime($lu[5], $lu[4], $lu[3], $lu[2], $lu[1] - 1, $lu[0] - 1900);
$time = $last_updated + 1 if $time < $last_updated;
$cgi->send_cookie('-name', 'read_new_functionality', '-value', $time);
return {status => 'ok'};

View File

@ -17,7 +17,7 @@ use Bugzilla::Error;
use Storable qw(dclone);
use parent qw(Exporter);
use base qw(Exporter);
# We have to "require", not "use" this, because otherwise it tries to
# use features of Test::More during import().