3 namespace MediaWiki\Rest\Validator
;
5 use MediaWiki\Rest\Handler
;
6 use MediaWiki\Rest\HttpException
;
7 use MediaWiki\Rest\RequestInterface
;
9 use Wikimedia\ObjectFactory
;
10 use Wikimedia\ParamValidator\ParamValidator
;
11 use Wikimedia\ParamValidator\TypeDef\BooleanDef
;
12 use Wikimedia\ParamValidator\TypeDef\EnumDef
;
13 use Wikimedia\ParamValidator\TypeDef\FloatDef
;
14 use Wikimedia\ParamValidator\TypeDef\IntegerDef
;
15 use Wikimedia\ParamValidator\TypeDef\PasswordDef
;
16 use Wikimedia\ParamValidator\TypeDef\StringDef
;
17 use Wikimedia\ParamValidator\TypeDef\TimestampDef
;
18 use Wikimedia\ParamValidator\TypeDef\UploadDef
;
19 use Wikimedia\ParamValidator\ValidationException
;
22 * Wrapper for ParamValidator
24 * It's intended to be used in the REST API classes by composition.
30 /** @var array Type defs for ParamValidator */
31 private static $typeDefs = [
32 'boolean' => [ 'class' => BooleanDef
::class ],
33 'enum' => [ 'class' => EnumDef
::class ],
34 'integer' => [ 'class' => IntegerDef
::class ],
35 'float' => [ 'class' => FloatDef
::class ],
36 'double' => [ 'class' => FloatDef
::class ],
38 'class' => StringDef
::class,
40 'allowEmptyWhenRequired' => true,
43 'password' => [ 'class' => PasswordDef
::class ],
44 'string' => [ 'class' => StringDef
::class ],
45 'timestamp' => [ 'class' => TimestampDef
::class ],
46 'upload' => [ 'class' => UploadDef
::class ],
49 /** @var string[] HTTP request methods that we expect never to have a payload */
50 private static $noBodyMethods = [ 'GET', 'HEAD', 'DELETE' ];
52 /** @var string[] HTTP request methods that we expect always to have a payload */
53 private static $bodyMethods = [ 'POST', 'PUT' ];
55 /** @var string[] Content types handled via $_POST */
56 private static $formDataContentTypes = [
57 'application/x-www-form-urlencoded',
58 'multipart/form-data',
61 /** @var ParamValidator */
62 private $paramValidator;
66 * @param ObjectFactory $objectFactory
67 * @param RequestInterface $request
70 public function __construct(
71 ObjectFactory
$objectFactory, RequestInterface
$request, User
$user
73 $this->paramValidator
= new ParamValidator(
74 new ParamValidatorCallbacks( $request, $user ),
77 'typeDefs' => self
::$typeDefs,
84 * @param array[] $paramSettings Parameter settings
85 * @return array Validated parameters
86 * @throws HttpException on validaton failure
88 public function validateParams( array $paramSettings ) {
89 $validatedParams = [];
90 foreach ( $paramSettings as $name => $settings ) {
92 $validatedParams[$name] = $this->paramValidator
->getValue( $name, $settings, [
93 'source' => $settings[Handler
::PARAM_SOURCE
] ??
'unspecified',
95 } catch ( ValidationException
$e ) {
96 throw new HttpException( 'Parameter validation failed', 400, [
97 'error' => 'parameter-validation-failed',
98 'name' => $e->getParamName(),
99 'value' => $e->getParamValue(),
100 'failureCode' => $e->getFailureCode(),
101 'failureData' => $e->getFailureData(),
105 return $validatedParams;
109 * Validate the body of a request.
111 * This may return a data structure representing the parsed body. When used
112 * in the context of Handler::validateParams(), the returned value will be
113 * available to the handler via Handler::getValidatedBody().
115 * @param RequestInterface $request
116 * @param Handler $handler Used to call getBodyValidator()
117 * @return mixed May be null
118 * @throws HttpException on validation failure
120 public function validateBody( RequestInterface
$request, Handler
$handler ) {
121 $method = strtoupper( trim( $request->getMethod() ) );
123 // If the method should never have a body, don't bother validating.
124 if ( in_array( $method, self
::$noBodyMethods, true ) ) {
128 // Get the content type
129 list( $ct ) = explode( ';', $request->getHeaderLine( 'Content-Type' ), 2 );
130 $ct = strtolower( trim( $ct ) );
132 // No Content-Type was supplied. RFC 7231 ยง 3.1.1.5 allows this, but since it's probably a
133 // client error let's return a 415. But don't 415 for unknown methods and an empty body.
134 if ( !in_array( $method, self
::$bodyMethods, true ) ) {
135 $body = $request->getBody();
136 $size = $body->getSize();
137 if ( $size === null ) {
138 // No size available. Try reading 1 byte.
139 if ( $body->isSeekable() ) {
142 $size = $body->read( 1 ) === '' ?
0 : 1;
148 throw new HttpException( "A Content-Type header must be supplied with a request payload.", 415, [
149 'error' => 'no-content-type',
153 // Form data is parsed into $_POST and $_FILES by PHP and from there is accessed as parameters,
154 // don't bother trying to handle these via BodyValidator too.
155 if ( in_array( $ct, self
::$formDataContentTypes, true ) ) {
159 // Validate the body. BodyValidator throws an HttpException on failure.
160 return $handler->getBodyValidator( $ct )->validateBody( $request );