diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml index a8c6a2e1a33ff..ef8d61bcd8855 100644 --- a/build/psalm-baseline.xml +++ b/build/psalm-baseline.xml @@ -3337,9 +3337,6 @@ getOverwriteHost()]]> - cookies[$key]) ? $this->cookies[$key] : null]]> - env[$key]) ? $this->env[$key] : null]]> - files[$key]) ? $this->files[$key] : null]]> diff --git a/lib/private/AppFramework/Http/Request.php b/lib/private/AppFramework/Http/Request.php index e02f20ca308c5..a41b6fb57938a 100644 --- a/lib/private/AppFramework/Http/Request.php +++ b/lib/private/AppFramework/Http/Request.php @@ -20,16 +20,20 @@ use Symfony\Component\HttpFoundation\IpUtils; /** - * Class for accessing variables in the request. - * This class provides an immutable object with request variables. + * Default immutable IRequest implementation. * - * @property mixed[] $cookies - * @property mixed[] $env - * @property mixed[] $files - * @property string $method - * @property mixed[] $parameters - * @property mixed[] $server - * @template-implements \ArrayAccess + * @property-read array $get + * @property-read array $post + * @property-read array|resource $put + * @property-read array $patch + * @property-read string $method + * @property-read array $server + * @property-read array $urlParams + * @property-read array $cookies + * @property-read array $env + * @property-read array $files + * @property-read array $parameters + * @template-implements \ArrayAccess */ class Request implements \ArrayAccess, \Countable, IRequest { public const USER_AGENT_IE = '/(MSIE)|(Trident)/'; @@ -45,9 +49,24 @@ class Request implements \ArrayAccess, \Countable, IRequest { // Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent public const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#'; public const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#'; + public const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost|\[::1\])$/'; + + /** + * Whether the raw PUT body stream has already been returned. + */ private bool $isPutStreamContentAlreadySent = false; + + /** + * Internal request data store. + */ protected array $items = []; + + /** + * Magic properties that are exposed directly from $items. + * + * @var list + */ protected array $allowedKeys = [ 'get', 'post', @@ -61,24 +80,27 @@ class Request implements \ArrayAccess, \Countable, IRequest { 'requesttoken', ]; + /** + * Whether request-body decoding has already been attempted. + */ protected bool $contentDecoded = false; + + /** + * Deferred decoding error from the request body, if any. + */ private ?\JsonException $decodingException = null; /** - * @param array $vars An associative array with the following optional values: - * - array 'urlParams' the parameters which were matched from the URL + * @param array $vars Associative request data with the following optional keys: + * - array 'urlParams' route parameters extracted from the URL * - array 'get' the $_GET array - * - array|string 'post' the $_POST array or JSON string + * - array 'post' the $_POST array * - array 'files' the $_FILES array * - array 'server' the $_SERVER array * - array 'env' the $_ENV array * - array 'cookies' the $_COOKIE array - * - string 'method' the request method (GET, POST etc) - * - string|false 'requesttoken' the requesttoken or false when not available - * @param IRequestId $requestId - * @param IConfig $config - * @param CsrfTokenManager|null $csrfTokenManager - * @param string $inputStream + * - string 'method' the HTTP request method, for example GET or POST + * - string|false 'requesttoken' the request token, or false if unavailable * @see https://www.php.net/manual/en/reserved.variables.php */ public function __construct( @@ -105,8 +127,16 @@ public function __construct( $this->items['params'] ); } + /** + * Replaces the current URL parameters and merges them into the parameter set. + * + * URL parameters take precedence over previously merged values with the same + * key. + * * @param array $parameters + * + * @internal public only so it can be consumed by OC\AppFramework\App */ public function setUrlParameters(array $parameters) { $this->items['urlParams'] = $parameters; @@ -117,8 +147,7 @@ public function setUrlParameters(array $parameters) { } /** - * Countable method - * @return int + * Returns the number of merged request parameters. */ #[\Override] public function count(): int { @@ -126,24 +155,12 @@ public function count(): int { } /** - * ArrayAccess methods + * Whether a merged request parameter exists. * - * Gives access to the combined GET, POST and urlParams arrays + * ArrayAccess operates on the merged parameter set. * - * Examples: - * - * $var = $request['myvar']; - * - * or - * - * if(!isset($request['myvar']) { - * // Do something - * } - * - * $request['myvar'] = 'something'; // This throws an exception. - * - * @param string $offset The key to lookup - * @return boolean + * @param string $offset Parameter name + * @return bool */ #[\Override] public function offsetExists($offset): bool { @@ -151,8 +168,9 @@ public function offsetExists($offset): bool { } /** - * @see offsetExists - * @param string $offset + * Returns a merged request parameter value, or null if it is missing. + * + * @param string $offset Parameter name * @return mixed */ #[\Override] @@ -162,7 +180,8 @@ public function offsetGet($offset) { } /** - * @see offsetExists + * Request objects are immutable. + * * @param string $offset * @param mixed $value */ @@ -172,7 +191,8 @@ public function offsetSet($offset, $value): void { } /** - * @see offsetExists + * Request objects are immutable. + * * @param string $offset */ #[\Override] @@ -181,7 +201,8 @@ public function offsetUnset($offset): void { } /** - * Magic property accessors + * Request objects are immutable. + * * @param string $name * @param mixed $value */ @@ -190,17 +211,16 @@ public function __set($name, $value) { } /** - * Access request variables by method and name. - * Examples: + * Returns request data through magic property access. * - * $request->post['myvar']; // Only look for POST variables - * $request->myvar; or $request->{'myvar'}; or $request->{$myvar} - * Looks in the combined GET, POST and urlParams array. + * Named properties read from the merged parameter set. Method-specific + * properties (`get`, `post`, `put`, `patch`) are only available for the + * matching HTTP method and throw a \LogicException otherwise. * - * If you access e.g. ->post but the current HTTP request method - * is GET a \LogicException will be thrown. + * Depending on the method and content type, `put` may return either parsed + * parameters or a readable stream for the raw request body. * - * @param string $name The key to look for. + * @param string $name Property name * @throws \LogicException * @return mixed|null */ @@ -235,6 +255,8 @@ public function __get($name) { } /** + * Whether a magic property is available. + * * @param string $name * @return bool */ @@ -246,55 +268,38 @@ public function __isset($name) { } /** + * Request objects are immutable. + * * @param string $id */ public function __unset($id) { throw new \RuntimeException('You cannot change the contents of the request object'); } - /** - * Returns the value for a specific http header. - * - * This method returns an empty string if the header did not exist. - * - * @param string $name - * @return string - */ #[\Override] public function getHeader(string $name): string { - $name = strtoupper(str_replace('-', '_', $name)); - if (isset($this->server['HTTP_' . $name])) { - return $this->server['HTTP_' . $name]; + $elementName = strtoupper(str_replace('-', '_', $name)); + + // Check if standard HTTP header + $clientHeaderKey = 'HTTP_' . $elementName; + if (isset($this->server[$clientHeaderKey])) { + return $this->server[$clientHeaderKey]; } - // There's a few headers that seem to end up in the top-level - // server array. - switch ($name) { - case 'CONTENT_TYPE': - case 'CONTENT_LENGTH': - case 'REMOTE_ADDR': - if (isset($this->server[$name])) { - return $this->server[$name]; - } - break; + // Check if special request-related element + $specialKeys = [ + 'CONTENT_TYPE' => true, + 'CONTENT_LENGTH' => true, + 'REMOTE_ADDR' => true, + ]; + + if (isset($specialKeys[$elementName]) && isset($this->server[$elementName])) { + return $this->server[$elementName]; } return ''; } - /** - * Lets you access post and get parameters by the index - * In case of json requests the encoded json body is accessed - * - * @param string $key the key which you want to access in the URL Parameter - * placeholder, $_POST or $_GET array. - * The priority how they're returned is the following: - * 1. URL parameters - * 2. POST parameters - * 3. GET parameters - * @param mixed $default If the key is not found, this value will be returned - * @return mixed the content of the array - */ #[\Override] public function getParam(string $key, $default = null) { return isset($this->parameters[$key]) @@ -302,68 +307,43 @@ public function getParam(string $key, $default = null) { : $default; } - /** - * Returns all params that were received, be it from the request - * (as GET or POST) or through the URL by the route - * @return array the array with all parameters - */ #[\Override] public function getParams(): array { return is_array($this->parameters) ? $this->parameters : []; } - /** - * Returns the method of the request - * @return string the method of the request (POST, GET, etc) - */ #[\Override] public function getMethod(): string { return $this->method; } - /** - * Shortcut for accessing an uploaded file through the $_FILES array - * @param string $key the key that will be taken from the $_FILES array - * @return array the file in the $_FILES element - */ #[\Override] public function getUploadedFile(string $key) { return isset($this->files[$key]) ? $this->files[$key] : null; } - /** - * Shortcut for getting env variables - * @param string $key the key that will be taken from the $_ENV array - * @return array the value in the $_ENV element - */ #[\Override] public function getEnv(string $key) { return isset($this->env[$key]) ? $this->env[$key] : null; } - /** - * Shortcut for getting cookie variables - * @param string $key the key that will be taken from the $_COOKIE array - * @return string the value in the $_COOKIE element - */ #[\Override] public function getCookie(string $key) { return isset($this->cookies[$key]) ? $this->cookies[$key] : null; } /** - * Returns the request body content. + * Returns request body content for method-specific magic accessors. * - * If the HTTP request method is PUT and the body - * not application/x-www-form-urlencoded or application/json a stream - * resource is returned, otherwise an array. - * - * @return array|string|resource The request body content or a resource to read the body stream. + * For PUT requests with a non-empty body that is neither JSON nor + * form-encoded, a readable stream resource for the raw body is returned. + * Otherwise, parsed parameters are returned as an array. * + * @return array|string|resource The request body content or a resource for the raw body stream * @throws \LogicException */ protected function getContent() { - // If the content can't be parsed into an array then return a stream resource. + // If the content cannot be parsed into parameters, return a raw body stream. if ($this->isPutStreamContent()) { if ($this->isPutStreamContentAlreadySent) { throw new \LogicException( @@ -388,7 +368,13 @@ private function isPutStreamContent(): bool { } /** - * Attempt to decode the content and populate parameters + * Decodes the request body, if applicable, and merges decoded parameters + * into the parameter set. + * + * JSON-compatible content types are decoded from the input stream. For + * non-GET and non-POST form-encoded requests, the input stream is parsed + * into parameters. Decoding errors are stored and can later be rethrown via + * throwDecodingExceptionIfAny(). */ protected function decodeContent() { if ($this->contentDecoded) { @@ -396,7 +382,7 @@ protected function decodeContent() { } $params = []; - // 'application/json' and other JSON-related content types must be decoded manually. + // JSON-compatible content types must be decoded manually. if (preg_match(self::JSON_CONTENT_TYPE_REGEX, $this->getHeader('Content-Type')) === 1) { $content = file_get_contents($this->inputStream); if ($content !== '') { @@ -412,8 +398,7 @@ protected function decodeContent() { $this->items['post'] = $params; } } - // Handle application/x-www-form-urlencoded for methods other than GET - // or post correctly + // Handle form-encoded request bodies for methods other than GET and POST. } elseif ($this->method !== 'GET' && $this->method !== 'POST' && str_contains($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded')) { @@ -436,10 +421,6 @@ public function throwDecodingExceptionIfAny(): void { } } - /** - * Checks if the CSRF check was correct - * @return bool true if CSRF check passed - */ #[\Override] public function passesCSRFCheck(): bool { if ($this->csrfTokenManager === null) { @@ -470,9 +451,7 @@ public function passesCSRFCheck(): bool { } /** - * Whether the cookie checks are required - * - * @return bool + * Whether cookie-based same-site checks are required for this request. */ private function cookieCheckRequired(): bool { if ($this->getHeader('OCS-APIREQUEST')) { @@ -486,19 +465,14 @@ private function cookieCheckRequired(): bool { } /** - * Wrapper around session_get_cookie_params - * - * @return array + * Wrapper around session_get_cookie_params(). */ public function getCookieParams(): array { return session_get_cookie_params(); } /** - * Appends the __Host- prefix to the cookie if applicable - * - * @param string $name - * @return string + * Returns the cookie name with the __Host- prefix applied when appropriate. */ protected function getProtectedCookieName(string $name): string { $cookieParams = $this->getCookieParams(); @@ -510,13 +484,6 @@ protected function getProtectedCookieName(string $name): string { return $prefix . $name; } - /** - * Checks if the strict cookie has been sent with the request if the request - * is including any cookies. - * - * @return bool - * @since 9.1.0 - */ #[\Override] public function passesStrictCookieCheck(): bool { if (!$this->cookieCheckRequired()) { @@ -531,13 +498,6 @@ public function passesStrictCookieCheck(): bool { return false; } - /** - * Checks if the lax cookie has been sent with the request if the request - * is including any cookies. - * - * @return bool - * @since 9.1.0 - */ #[\Override] public function passesLaxCookieCheck(): bool { if (!$this->cookieCheckRequired()) { @@ -551,39 +511,30 @@ public function passesLaxCookieCheck(): bool { return false; } - /** - * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging - * If `mod_unique_id` is installed this value will be taken. - * @return string - */ #[\Override] public function getId(): string { return $this->requestId->getId(); } /** - * Checks if given $remoteAddress matches any entry in the given array $trustedProxies. - * For details regarding what "match" means, refer to `matchesTrustedProxy`. - * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise + * Checks whether the given remote address matches one of the configured + * trusted proxies. + * + * Invalid trusted proxy configuration is treated as non-matching. + * + * @return bool true if $remoteAddress matches a trusted proxy, false otherwise */ protected function isTrustedProxy($trustedProxies, $remoteAddress) { try { return IpUtils::checkIp($remoteAddress, $trustedProxies); } catch (\Throwable) { - // We can not log to our log here as the logger is using `getRemoteAddress` which uses the function, so we would have a cyclic dependency - // Reaching this line means `trustedProxies` is in invalid format. + // Cannot log through the regular logger here because it may depend on + // getRemoteAddress(), which would create a cyclic dependency. error_log('Nextcloud trustedProxies has malformed entries'); return false; } } - /** - * Returns the remote address, if the connection came from a trusted proxy - * and `forwarded_for_headers` has been configured then the IP address - * specified in this header will be returned instead. - * Do always use this instead of $_SERVER['REMOTE_ADDR'] - * @return string IP address - */ #[\Override] public function getRemoteAddress(): string { $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : ''; @@ -627,26 +578,12 @@ public function getRemoteAddress(): string { return $remoteAddress; } - /** - * Check overwrite condition - * @return bool - */ private function isOverwriteCondition(): bool { $regex = '/' . $this->config->getSystemValueString('overwritecondaddr', '') . '/'; $remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : ''; return $regex === '//' || preg_match($regex, $remoteAddr) === 1; } - /** - * Returns the server protocol. It respects one or more reverse proxies servers - * and load balancers. Precedence: - * 1. `overwriteprotocol` config value - * 2. `X-Forwarded-Proto` header value - * 3. $_SERVER['HTTPS'] value - * If an invalid protocol is provided, defaults to http, continues, but logs as an error. - * - * @return string Server protocol (http or https) - */ #[\Override] public function getServerProtocol(): string { $proto = 'http'; @@ -682,11 +619,6 @@ public function getServerProtocol(): string { return $proto === 'https' ? 'https' : 'http'; } - /** - * Returns the used HTTP protocol. - * - * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0. - */ #[\Override] public function getHttpProtocol(): string { $claimedProtocol = $this->server['SERVER_PROTOCOL'] ?? ''; @@ -708,11 +640,6 @@ public function getHttpProtocol(): string { return 'HTTP/1.1'; } - /** - * Returns the request uri, even if the website uses one or more - * reverse proxies - * @return string - */ #[\Override] public function getRequestUri(): string { $uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : ''; @@ -722,11 +649,6 @@ public function getRequestUri(): string { return $uri; } - /** - * Get raw PathInfo from request (not urldecoded) - * @throws \Exception - * @return string Path info - */ #[\Override] public function getRawPathInfo(): string { $requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : ''; @@ -768,22 +690,12 @@ public function getRawPathInfo(): string { } } - /** - * Get PathInfo from request (rawurldecoded) - * @throws \Exception - * @return string|false Path info or false when not found - */ #[\Override] public function getPathInfo(): string|false { $pathInfo = $this->getRawPathInfo(); return \Sabre\HTTP\decodePath($pathInfo); } - /** - * Returns the script name, even if the website uses one or more - * reverse proxies - * @return string the script name - */ #[\Override] public function getScriptName(): string { $name = $this->server['SCRIPT_NAME'] ?? ''; @@ -797,11 +709,6 @@ public function getScriptName(): string { return $name; } - /** - * Checks whether the user agent matches a given regex - * @param array $agent array of agent names - * @return bool true if at least one of the given agent matches, false otherwise - */ #[\Override] public function isUserAgent(array $agent): bool { if (!isset($this->server['HTTP_USER_AGENT'])) { @@ -815,11 +722,6 @@ public function isUserAgent(array $agent): bool { return false; } - /** - * Returns the unverified server host from the headers without checking - * whether it is a trusted domain - * @return string Server host - */ #[\Override] public function getInsecureServerHost(): string { if ($this->fromTrustedProxy() && $this->getOverwriteHost() !== null) { @@ -845,11 +747,6 @@ public function getInsecureServerHost(): string { return $host; } - /** - * Returns the server host from the headers, or the first configured - * trusted domain if the host isn't in the trusted list - * @return string Server host - */ #[\Override] public function getServerHost(): string { // overwritehost is always trusted @@ -878,10 +775,9 @@ public function getServerHost(): string { } /** - * Returns the overwritehost setting from the config if set and - * if the overwrite condition is met - * @return string|null overwritehost value or null if not defined or the defined condition - * isn't met + * Returns the overwritehost config value if configured and applicable. + * + * @return string|null */ private function getOverwriteHost() { if ($this->config->getSystemValueString('overwritehost') !== '' && $this->isOverwriteCondition()) { diff --git a/lib/private/AppFramework/Http/RequestId.php b/lib/private/AppFramework/Http/RequestId.php index ba4982895906d..d63f5e63c7ad6 100644 --- a/lib/private/AppFramework/Http/RequestId.php +++ b/lib/private/AppFramework/Http/RequestId.php @@ -18,11 +18,6 @@ public function __construct( ) { } - /** - * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging - * If `mod_unique_id` is installed this value will be taken. - * @return string - */ #[\Override] public function getId(): string { if (empty($this->requestId)) { diff --git a/lib/public/IRequest.php b/lib/public/IRequest.php index 1ca66c646e830..2510c48834d4b 100644 --- a/lib/public/IRequest.php +++ b/lib/public/IRequest.php @@ -6,35 +6,40 @@ * SPDX-FileCopyrightText: 2016 ownCloud, Inc. * SPDX-License-Identifier: AGPL-3.0-only */ -// use OCP namespace for all classes that are considered public. -// This means that they should be used by apps instead of the internal Nextcloud classes namespace OCP; /** - * This interface provides an immutable object with with accessors to - * request variables and headers. + * Immutable request wrapper with accessors for request variables and other + * request-related data. * - * Access request variables by method and name. + * Request data should be retrieved through this interface whenever possible. * - * Examples: + * Parameters can be accessed through dedicated methods or via magic property + * access, for example: * - * $request->post['myvar']; // Only look for POST variables - * $request->myvar; or $request->{'myvar'}; or $request->{$myvar} - * Looks in the combined GET, POST and urlParams array. + * $request->post['myvar']; // POST body parameters on POST requests + * $request->myvar; // merged request parameters * - * If you access e.g. ->post but the current HTTP request method - * is GET a \LogicException will be thrown. + * Magic access to a named parameter reads from the merged request parameter + * set. Method-specific properties such as `get`, `post`, `put`, and `patch` + * are only available for the matching HTTP method and may throw a + * \LogicException otherwise. * - * NOTE: - * - When accessing ->put a stream resource is returned and the accessor - * will return false on subsequent access to ->put or ->patch. - * - When accessing ->patch and the Content-Type is either application/json - * or application/x-www-form-urlencoded (most cases) it will act like ->get - * and ->post and return an array. Otherwise the raw data will be returned. + * In PUT requests, if the body is JSON or form-encoded, `->put` behaves like + * the other method-specific accessors and returns parsed request parameters. + * Otherwise, for non-empty request bodies, it returns a readable stream + * resource for the raw request body. Such streamed PUT bodies can only be + * accessed once; repeated access throws a \LogicException. * - * @property-read string[] $server + * @property-read array $get + * @property-read array $post + * @property-read array|resource $put + * @property-read array $patch + * @property-read string $method + * @property-read array $server * @property-read string[] $urlParams + * * @since 6.0.0 */ interface IRequest { @@ -92,111 +97,121 @@ interface IRequest { public const JSON_CONTENT_TYPE_REGEX = '/^application\/(?:[a-z0-9.-]+\+)?json\b/'; /** - * @param string $name + * Returns the value of a request header, or an empty string if missing. + * + * Header names are matched case-insensitively. + * + * Besides normal HTTP headers, also supports selected request-related + * server values such as `REMOTE_ADDR`. * * @psalm-taint-source input * - * @return string * @since 6.0.0 */ public function getHeader(string $name): string; /** - * Lets you access post and get parameters by the index - * In case of json requests the encoded json body is accessed + * Returns a parameter value from the merged parameter set. + * + * The merged parameter set is primarily composed from route URL parameters, + * POST parameters and GET parameters. Depending on request content type and + * prior access, lazily decoded request-body parameters may also be present. * * @psalm-taint-source input * - * @param string $key the key which you want to access in the URL Parameter - * placeholder, $_POST or $_GET array. - * The priority how they're returned is the following: - * 1. URL parameters - * 2. POST parameters - * 3. GET parameters - * @param mixed $default If the key is not found, this value will be returned - * @return mixed the content of the array + * @param string $key the key to look up + * @param mixed $default the value to return if the key is not found + * @return mixed the parameter value, or $default if the key is not present * @since 6.0.0 */ public function getParam(string $key, $default = null); /** - * Returns all params that were received, be it from the request + * Returns the merged parameter set currently available on the request. * - * (as GET or POST) or through the URL by the route + * This includes request parameters from GET, POST and route URL parameters, + * and may also include decoded request-body parameters. * * @psalm-taint-source input * - * @return array the array with all parameters + * @return array the merged parameters * @since 6.0.0 */ public function getParams(): array; /** - * Returns the method of the request + * Returns the request method. * - * @return string the method of the request (POST, GET, etc) + * @return string the HTTP method, for example GET, POST, PUT, or PATCH * @since 6.0.0 */ public function getMethod(): string; /** - * Shortcut for accessing an uploaded file through the $_FILES array + * Returns an uploaded file entry from the `$_FILES` data, if present. * - * @param string $key the key that will be taken from the $_FILES array - * @return array the file in the $_FILES element + * @param string $key the file field name + * @return array|null the matching uploaded file entry, or null if missing * @since 6.0.0 */ public function getUploadedFile(string $key); /** - * Shortcut for getting env variables + * Returns an environment value from the request environment, if present. * - * @param string $key the key that will be taken from the $_ENV array - * @return array the value in the $_ENV element + * @param string $key the environment variable name + * @return mixed|null the environment value, or null if missing * @since 6.0.0 */ public function getEnv(string $key); /** - * Shortcut for getting cookie variables + * Returns a cookie value, if present. * * @psalm-taint-source input * - * @param string $key the key that will be taken from the $_COOKIE array - * @return string|null the value in the $_COOKIE element + * @param string $key the cookie name + * @return string|null the cookie value, or null if missing * @since 6.0.0 */ public function getCookie(string $key); /** - * Checks if the CSRF check was correct + * Checks whether the request passes CSRF validation. + * + * Depending on the request, this may include same-site cookie checks and + * token validation from request parameters or headers. OCS API requests are + * handled specially by the implementation (if the OCS-APIRequest header is + * included in the request). * - * @return bool true if CSRF check passed + * @return bool true if the request passes CSRF validation * @since 6.0.0 */ public function passesCSRFCheck(): bool; /** - * Checks if the strict cookie has been sent with the request if the request - * is including any cookies. + * Checks whether the strict same-site cookie requirement is satisfied when + * session or authentication cookies are part of the request. * - * @return bool + * @return bool true if the strict cookie check passes * @since 9.0.0 */ public function passesStrictCookieCheck(): bool; /** - * Checks if the lax cookie has been sent with the request if the request - * is including any cookies. + * Checks whether the lax same-site cookie requirement is satisfied when + * session or authentication cookies are part of the request. * - * @return bool + * @return bool true if the lax cookie check passes * @since 9.0.0 */ public function passesLaxCookieCheck(): bool; /** - * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging - * If `mod_unique_id` is installed this value will be taken. + * Returns a request identifier intended primarily for logging and tracing. + * + * The value is not guaranteed to be globally unique. If `mod_unique_id` is + * installed, that value may be used by the implementation. * * @return string * @since 8.1.0 @@ -204,10 +219,14 @@ public function passesLaxCookieCheck(): bool; public function getId(): string; /** - * Returns the remote address, if the connection came from a trusted proxy - * and `forwarded_for_headers` has been configured then the IP address - * specified in this header will be returned instead. - * Do always use this instead of $_SERVER['REMOTE_ADDR'] + * Returns the effective remote IP address. + * + * If the connection comes from a trusted proxy and `forwarded_for_headers` + * is configured, the client IP from those forwarded headers is used + * instead. + * + * Do not use `$_SERVER['REMOTE_ADDR']` directly when this method is + * available. * * @return string IP address * @since 8.1.0 @@ -215,25 +234,31 @@ public function getId(): string; public function getRemoteAddress(): string; /** - * Returns the server protocol. It respects reverse proxy servers and load - * balancers. + * Returns the effective server protocol. + * + * Respects reverse proxies and load balancers. Precedence: + * 1. `overwriteprotocol` config value + * 2. `X-Forwarded-Proto` header value + * 3. `$_SERVER['HTTPS']` value + * + * Invalid values fall back to `http`. * - * @return string Server protocol (http or https) + * @return string Server protocol: `http` or `https` * @since 8.1.0 */ public function getServerProtocol(): string; /** - * Returns the used HTTP protocol. + * Returns the HTTP protocol version used for the request. * - * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0. + * @return string HTTP protocol, for example HTTP/2, HTTP/1.1, or HTTP/1.0 * @since 8.2.0 */ public function getHttpProtocol(): string; /** - * Returns the request uri, even if the website uses one or more - * reverse proxies + * Returns the request URI, taking reverse-proxy and overwrite settings into + * account. * * @psalm-taint-source input * @@ -243,30 +268,30 @@ public function getHttpProtocol(): string; public function getRequestUri(): string; /** - * Get raw PathInfo from request (not urldecoded) + * Returns raw path info from the request without URL decoding. * * @psalm-taint-source input * * @throws \Exception - * @return string Path info + * @return string path info * @since 8.1.0 */ public function getRawPathInfo(): string; /** - * Get PathInfo from request + * Returns decoded path info from the request. * * @psalm-taint-source input * * @throws \Exception - * @return string|false Path info or false when not found + * @return string|false path info, or false when it cannot be determined * @since 8.1.0 */ public function getPathInfo(); /** - * Returns the script name, even if the website uses one or more - * reverse proxies + * Returns the effective script name, taking reverse-proxy and overwrite + * settings into account. * * @return string the script name * @since 8.1.0 @@ -274,38 +299,46 @@ public function getPathInfo(); public function getScriptName(): string; /** - * Checks whether the user agent matches a given regex + * Checks whether the current user agent matches at least one of the given + * regular expressions. * - * @param array $agent array of agent names - * @return bool true if at least one of the given agent matches, false otherwise + * @param array $agent array of user-agent regex patterns + * @return bool true if at least one pattern matches, false otherwise * @since 8.1.0 */ public function isUserAgent(array $agent): bool; /** - * Returns the unverified server host from the headers without checking - * whether it is a trusted domain + * Returns the effective host value without validating it against the trusted + * domains configuration. + * + * This may be derived from request headers, proxy headers, or server + * variables, depending on the deployment setup. * * @psalm-taint-source input * - * @return string Server host + * @return string server host * @since 8.1.0 */ public function getInsecureServerHost(): string; /** - * Returns the server host from the headers, or the first configured - * trusted domain if the host isn't in the trusted list + * Returns the validated effective server host. + * + * The implementation may use overwrite host configuration first. Otherwise + * it derives the host from the request and returns it only if it is trusted; + * if not, it falls back to the first configured trusted domain. * - * @return string Server host + * @return string server host * @since 8.1.0 */ public function getServerHost(): string; /** - * If decoding the request content failed, throw an exception. - * Currently only \JsonException for json decoding errors, - * but in the future may throw other exceptions for other decoding issues. + * Throws any stored request-content decoding exception. + * + * Currently this is used for JSON decoding errors, but implementations may + * throw other decoding-related exceptions in the future. * * @throws \Exception * @since 32.0.0 @@ -313,9 +346,10 @@ public function getServerHost(): string; public function throwDecodingExceptionIfAny(): void; /** - * Returns the format of the response to this request. + * Returns the requested response format, if it can be determined. * - * The `Accept` header and the `format` query parameter control the format. + * The `format` request parameter takes precedence. Otherwise the format may + * be inferred from the `Accept` header. * * @return string|null * @since 33.0.0 diff --git a/lib/public/IRequestId.php b/lib/public/IRequestId.php index fdbebfe2fc7f7..828568bf8238a 100644 --- a/lib/public/IRequestId.php +++ b/lib/public/IRequestId.php @@ -12,9 +12,12 @@ * @since 24.0.0 */ interface IRequestId { + /** - * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging - * If `mod_unique_id` is installed this value will be taken. + * Returns a request identifier intended primarily for logging and tracing. + * + * The value is not guaranteed to be globally unique. If `mod_unique_id` is + * installed, that value may be used by the implementation. * * @return string * @since 24.0.0