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