X-Git-Url: http://git.cyclocoop.org/?a=blobdiff_plain;f=includes%2Fapi%2FApiMain.php;h=e69300bb3be5ba26ecf3e0960cfea3d76ff6d7a4;hb=19a84db108b86f8d4b94bde8e9c34add9f13cc31;hp=4068a50bb68191594369a3e54951c07999250219;hpb=bf9c2b0cb30cbe994f3a1859cf46224dd52039e1;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index 4068a50bb6..e69300bb3b 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -1,7 +1,5 @@ @gmail.com" @@ -28,6 +26,8 @@ use MediaWiki\Logger\LoggerFactory; use MediaWiki\MediaWikiServices; use Wikimedia\Timestamp\TimestampException; +use Wikimedia\Rdbms\DBQueryError; +use Wikimedia\Rdbms\DBError; /** * This is the main API class, used for both external and internal processing. @@ -236,6 +236,7 @@ class ApiMain extends ApiBase { wfDebug( "API: stripping user credentials when the same-origin policy is not applied\n" ); $wgUser = new User(); $this->getContext()->setUser( $wgUser ); + $request->response()->header( 'MediaWiki-Login-Suppressed: true' ); } } @@ -369,7 +370,7 @@ class ApiMain extends ApiBase { /** * Set the continuation manager - * @param ApiContinuationManager|null + * @param ApiContinuationManager|null $manager */ public function setContinuationManager( $manager ) { if ( $manager !== null ) { @@ -580,23 +581,12 @@ class ApiMain extends ApiBase { // T65145: Rollback any open database transactions if ( !( $e instanceof ApiUsageException || $e instanceof UsageException ) ) { // UsageExceptions are intentional, so don't rollback if that's the case - try { - MWExceptionHandler::rollbackMasterChangesAndLog( $e ); - } catch ( DBError $e2 ) { - // Rollback threw an exception too. Log it, but don't interrupt - // our regularly scheduled exception handling. - MWExceptionHandler::logException( $e2 ); - } + MWExceptionHandler::rollbackMasterChangesAndLog( $e ); } // Allow extra cleanup and logging Hooks::run( 'ApiMain::onException', [ $this, $e ] ); - // Log it - if ( !( $e instanceof ApiUsageException || $e instanceof UsageException ) ) { - MWExceptionHandler::logException( $e ); - } - // Handle any kind of exception by outputting properly formatted error message. // If this fails, an unhandled exception should be thrown so that global error // handler will process and log it. @@ -703,13 +693,17 @@ class ApiMain extends ApiBase { $request = $this->getRequest(); $response = $request->response(); - $matchOrigin = false; + $matchedOrigin = false; $allowTiming = false; $varyOrigin = true; if ( $originParam === '*' ) { // Request for anonymous CORS - $matchOrigin = true; + // Technically we should check for the presence of an Origin header + // and not process it as CORS if it's not set, but that would + // require us to vary on Origin for all 'origin=*' requests which + // we don't want to do. + $matchedOrigin = true; $allowOrigin = '*'; $allowCredentials = 'false'; $varyOrigin = false; // No need to vary @@ -736,7 +730,7 @@ class ApiMain extends ApiBase { } $config = $this->getConfig(); - $matchOrigin = count( $origins ) === 1 && self::matchOrigin( + $matchedOrigin = count( $origins ) === 1 && self::matchOrigin( $originParam, $config->get( 'CrossSiteAJAXdomains' ), $config->get( 'CrossSiteAJAXdomainExceptions' ) @@ -747,19 +741,21 @@ class ApiMain extends ApiBase { $allowTiming = $originHeader; } - if ( $matchOrigin ) { + if ( $matchedOrigin ) { $requestedMethod = $request->getHeader( 'Access-Control-Request-Method' ); $preflight = $request->getMethod() === 'OPTIONS' && $requestedMethod !== false; if ( $preflight ) { // This is a CORS preflight request if ( $requestedMethod !== 'POST' && $requestedMethod !== 'GET' ) { // If method is not a case-sensitive match, do not set any additional headers and terminate. + $response->header( 'MediaWiki-CORS-Rejection: Unsupported method requested in preflight' ); return true; } // We allow the actual request to send the following headers $requestedHeaders = $request->getHeader( 'Access-Control-Request-Headers' ); if ( $requestedHeaders !== false ) { if ( !self::matchRequestedHeaders( $requestedHeaders ) ) { + $response->header( 'MediaWiki-CORS-Rejection: Unsupported header requested in preflight' ); return true; } $response->header( 'Access-Control-Allow-Headers: ' . $requestedHeaders ); @@ -767,6 +763,12 @@ class ApiMain extends ApiBase { // We only allow the actual request to be GET or POST $response->header( 'Access-Control-Allow-Methods: POST, GET' ); + } elseif ( $request->getMethod() !== 'POST' && $request->getMethod() !== 'GET' ) { + // Unsupported non-preflight method, don't handle it as CORS + $response->header( + 'MediaWiki-CORS-Rejection: Unsupported method for simple request or actual request' + ); + return true; } $response->header( "Access-Control-Allow-Origin: $allowOrigin" ); @@ -778,9 +780,12 @@ class ApiMain extends ApiBase { if ( !$preflight ) { $response->header( - 'Access-Control-Expose-Headers: MediaWiki-API-Error, Retry-After, X-Database-Lag' + 'Access-Control-Expose-Headers: MediaWiki-API-Error, Retry-After, X-Database-Lag, ' + . 'MediaWiki-Login-Suppressed' ); } + } else { + $response->header( 'MediaWiki-CORS-Rejection: Origin mismatch' ); } if ( $varyOrigin ) { @@ -1036,7 +1041,7 @@ class ApiMain extends ApiBase { // None of the rest have any messages for non-error types } elseif ( $e instanceof UsageException ) { // User entered incorrect parameters - generate error response - $data = $e->getMessageArray(); + $data = MediaWiki\quietCall( [ $e, 'getMessageArray' ] ); $code = $data['code']; $info = $data['info']; unset( $data['code'], $data['info'] ); @@ -1044,7 +1049,8 @@ class ApiMain extends ApiBase { } else { // Something is seriously wrong $config = $this->getConfig(); - $code = 'internal_api_error_' . get_class( $e ); + $class = preg_replace( '#^Wikimedia\\\Rdbms\\\#', '', get_class( $e ) ); + $code = 'internal_api_error_' . $class; if ( ( $e instanceof DBQueryError ) && !$config->get( 'ShowSQLErrors' ) ) { $params = [ 'apierror-databaseerror', WebRequest::getRequestId() ]; } else { @@ -1823,7 +1829,7 @@ class ApiMain extends ApiBase { ApiBase::PARAM_TYPE => 'submodule', ], 'format' => [ - ApiBase::PARAM_DFLT => ApiMain::API_DEFAULT_FORMAT, + ApiBase::PARAM_DFLT => self::API_DEFAULT_FORMAT, ApiBase::PARAM_TYPE => 'submodule', ], 'maxlag' => [ @@ -1925,15 +1931,20 @@ class ApiMain extends ApiBase { $header = $this->msg( 'api-help-datatypes-header' )->parse(); - // Add an additional span with sanitized ID - if ( !$this->getConfig()->get( 'ExperimentalHtmlIds' ) ) { - $header = Html::element( 'span', [ 'id' => Sanitizer::escapeId( 'main/datatypes' ) ] ) . - $header; - } - $help['datatypes'] .= Html::rawElement( 'h' . min( 6, $level ), - [ 'id' => 'main/datatypes', 'class' => 'apihelp-header' ], - $header + $id = Sanitizer::escapeIdForAttribute( 'main/datatypes', Sanitizer::ID_PRIMARY ); + $idFallback = Sanitizer::escapeIdForAttribute( 'main/datatypes', Sanitizer::ID_FALLBACK ); + $headline = Linker::makeHeadline( min( 6, $level ), + ' class="apihelp-header"', + $id, + $header, + '', + $idFallback ); + // Ensure we have a sane anchor + if ( $id !== 'main/datatypes' && $idFallback !== 'main/datatypes' ) { + $headline = '
' . $headline; + } + $help['datatypes'] .= $headline; $help['datatypes'] .= $this->msg( 'api-help-datatypes' )->parseAsBlock(); if ( !isset( $tocData['main/datatypes'] ) ) { $tocnumber[$level]++; @@ -1947,16 +1958,21 @@ class ApiMain extends ApiBase { ]; } - // Add an additional span with sanitized ID - if ( !$this->getConfig()->get( 'ExperimentalHtmlIds' ) ) { - $header = Html::element( 'span', [ 'id' => Sanitizer::escapeId( 'main/credits' ) ] ) . - $header; - } $header = $this->msg( 'api-credits-header' )->parse(); - $help['credits'] .= Html::rawElement( 'h' . min( 6, $level ), - [ 'id' => 'main/credits', 'class' => 'apihelp-header' ], - $header + $id = Sanitizer::escapeIdForAttribute( 'main/credits', Sanitizer::ID_PRIMARY ); + $idFallback = Sanitizer::escapeIdForAttribute( 'main/credits', Sanitizer::ID_FALLBACK ); + $headline = Linker::makeHeadline( min( 6, $level ), + ' class="apihelp-header"', + $id, + $header, + '', + $idFallback ); + // Ensure we have a sane anchor + if ( $id !== 'main/credits' && $idFallback !== 'main/credits' ) { + $headline = '
' . $headline; + } + $help['credits'] .= $headline; $help['credits'] .= $this->msg( 'api-credits' )->useDatabase( false )->parseAsBlock(); if ( !isset( $tocData['main/credits'] ) ) { $tocnumber[$level]++;