# 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/. # # 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.14.0; use strict; use warnings; use parent 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 API_AUTH_HEADERS ); # This maps the error names in global/*-error.html.tmpl to numbers. # Generally, transient errors should have a number above 0, and # fatal errors should have a number below 0. # # This hash should generally contain any error that could be thrown # by the WebService interface. If it's extremely unlikely that the # error could be thrown (like some CodeErrors), it doesn't have to # be listed here. # # "Transient" means "If you resubmit that request with different data, # it may work." # # "Fatal" means, "There's something wrong with Bugzilla, probably # something an administrator would have to fix." # # NOTE: Numbers must never be recycled. If you remove a number, leave a # comment that it was retired. Also, if an error changes its name, you'll # have to fix it here. 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, param_integer_required => 57, param_scalar_array_required => 58, # Bug errors usually occupy the 100-200 range. improper_bug_id_field_value => 100, bug_id_does_not_exist => 101, bug_access_denied => 102, bug_access_query => 102, # These all mean "invalid alias" alias_too_long => 103, 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, product_unknown_component => 105, # Invalid Product no_products => 106, entry_access_denied => 106, product_access_denied => 106, product_disabled => 106, # Invalid Summary require_summary => 107, # Invalid field name invalid_field_name => 108, # Not authorized to edit the bug product_edit_denied => 109, # Comment-related errors comment_is_private => 110, comment_id_invalid => 111, comment_too_long => 114, comment_invalid_isprivate => 117, markdown_disabled => 140, # 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, # Insidergroup Errors user_not_insider => 113, # 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_login_or_password => 300, account_disabled => 301, auth_invalid_email => 302, extern_id_conflict => -303, auth_failure => 304, 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, # User errors are 500-600. account_exists => 500, illegal_email_address => 501, auth_cant_create_account => 501, account_creation_disabled => 501, account_creation_restricted => 501, password_too_short => 502, # Error 503 password_too_long no longer exists. invalid_username => 504, # This is from strict_isolation, but it also basically means # "invalid user." invalid_user_group => 504, user_access_by_id_denied => 505, user_access_by_match_denied => 505, # 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, product_admin_denied => 706, # 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, component_blank_name => 1210, component_blank_description => 1211, multiple_components_update_not_allowed => 1212, component_need_initialowner => 1213, # BugUserLastVisited errors user_not_involved => 1300, # 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. use constant ERROR_UNKNOWN_FATAL => -32000; 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; my %hook_dispatch; Bugzilla::Hook::process('webservice', { dispatch => \%hook_dispatch }); my $dispatch = { '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', 'BugUserLastVisit' => 'Bugzilla::WebService::BugUserLastVisit', %hook_dispatch }; return $dispatch; }; # Custom HTTP headers that can be used for API authentication rather than # passing as URL parameters. This is useful if you do not want sensitive # information to show up in webserver log files. use constant API_AUTH_HEADERS => { X_BUGZILLA_LOGIN => 'Bugzilla_login', X_BUGZILLA_PASSWORD => 'Bugzilla_password', X_BUGZILLA_API_KEY => 'Bugzilla_api_key', X_BUGZILLA_TOKEN => 'Bugzilla_token', }; 1; =head1 B<Methods in need of POD> =over =item REST_STATUS_CODE_MAP =item WS_DISPATCH =back