aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoas Schilling <213943+nickvergessen@users.noreply.github.com>2022-12-20 08:58:33 +0100
committerGitHub <noreply@github.com>2022-12-20 08:58:33 +0100
commit0af4e9d4fe80ad3cffc6f3ff6a3d19d5f808cba9 (patch)
treeb91084689580200f48e77f50d0d2b7efae0c893f
parentfcf5789916e914613addd7d7fe32cf50bfd614ae (diff)
parent7dcd6eb561f4bba7ed36ce1178c725278dd9b80e (diff)
downloadnextcloud-server-0af4e9d4fe80ad3cffc6f3ff6a3d19d5f808cba9.tar.gz
nextcloud-server-0af4e9d4fe80ad3cffc6f3ff6a3d19d5f808cba9.zip
Merge pull request #34172 from audriga/add-scim-json-support
Add support for application/scim+json
-rw-r--r--lib/private/AppFramework/Http/Request.php5
-rw-r--r--lib/public/IRequest.php5
-rw-r--r--tests/lib/AppFramework/Http/RequestTest.php183
3 files changed, 191 insertions, 2 deletions
diff --git a/lib/private/AppFramework/Http/Request.php b/lib/private/AppFramework/Http/Request.php
index 32cce8a88e1..d8aac063f3e 100644
--- a/lib/private/AppFramework/Http/Request.php
+++ b/lib/private/AppFramework/Http/Request.php
@@ -26,6 +26,7 @@ declare(strict_types=1);
* @author Thomas Tanghus <thomas@tanghus.net>
* @author Vincent Petry <vincent@nextcloud.com>
* @author Simon Leiner <simon@leiner.me>
+ * @author Stanimir Bozhilov <stanimir@audriga.com>
*
* @license AGPL-3.0
*
@@ -419,8 +420,8 @@ class Request implements \ArrayAccess, \Countable, IRequest {
}
$params = [];
- // 'application/json' must be decoded manually.
- if (strpos($this->getHeader('Content-Type'), 'application/json') !== false) {
+ // 'application/json' and other JSON-related content types must be decoded manually.
+ if (preg_match(self::JSON_CONTENT_TYPE_REGEX, $this->getHeader('Content-Type')) === 1) {
$params = json_decode(file_get_contents($this->inputStream), true);
if (\is_array($params) && \count($params) > 0) {
$this->items['params'] = $params;
diff --git a/lib/public/IRequest.php b/lib/public/IRequest.php
index 7696c3fa8c3..bb290233306 100644
--- a/lib/public/IRequest.php
+++ b/lib/public/IRequest.php
@@ -98,6 +98,11 @@ interface IRequest {
public const USER_AGENT_THUNDERBIRD_ADDON = '/^Mozilla\/5\.0 \([A-Za-z ]+\) Nextcloud\-Thunderbird v.*$/';
/**
+ * @since 26.0.0
+ */
+ public const JSON_CONTENT_TYPE_REGEX = '/^application\/(?:[a-z0-9.-]+\+)?json\b/';
+
+ /**
* @param string $name
*
* @psalm-taint-source input
diff --git a/tests/lib/AppFramework/Http/RequestTest.php b/tests/lib/AppFramework/Http/RequestTest.php
index 78f4f80f8be..78e656f5fc3 100644
--- a/tests/lib/AppFramework/Http/RequestTest.php
+++ b/tests/lib/AppFramework/Http/RequestTest.php
@@ -2,6 +2,7 @@
/**
* @copyright 2013 Thomas Tanghus (thomas@tanghus.net)
* @copyright 2016 Lukas Reschke lukas@owncloud.com
+ * @copyright 2022 Stanimir Bozhilov (stanimir@audriga.com)
*
* This file is licensed under the Affero General Public License version 3 or
* later.
@@ -207,6 +208,54 @@ class RequestTest extends \Test\TestCase {
$this->assertSame('Joey', $request['nickname']);
}
+ public function testScimJsonPost() {
+ global $data;
+ $data = '{"userName":"testusername", "displayName":"Example User"}';
+ $vars = [
+ 'method' => 'POST',
+ 'server' => ['CONTENT_TYPE' => 'application/scim+json; utf-8']
+ ];
+
+ $request = new Request(
+ $vars,
+ $this->requestId,
+ $this->config,
+ $this->csrfTokenManager,
+ $this->stream
+ );
+
+ $this->assertSame('POST', $request->method);
+ $result = $request->post;
+ $this->assertSame('testusername', $result['userName']);
+ $this->assertSame('Example User', $result['displayName']);
+ $this->assertSame('Example User', $request->params['displayName']);
+ $this->assertSame('Example User', $request['displayName']);
+ }
+
+ public function testCustomJsonPost() {
+ global $data;
+ $data = '{"propertyA":"sometestvalue", "propertyB":"someothertestvalue"}';
+
+ // Note: the content type used here is fictional and intended to check if the regex for JSON content types works fine
+ $vars = [
+ 'method' => 'POST',
+ 'server' => ['CONTENT_TYPE' => 'application/custom-type+json; utf-8']
+ ];
+
+ $request = new Request(
+ $vars,
+ $this->requestId,
+ $this->config,
+ $this->csrfTokenManager,
+ $this->stream
+ );
+
+ $this->assertSame('POST', $request->method);
+ $result = $request->post;
+ $this->assertSame('sometestvalue', $result['propertyA']);
+ $this->assertSame('someothertestvalue', $result['propertyB']);
+ }
+
public function notJsonDataProvider() {
return [
['this is not valid json'],
@@ -239,6 +288,48 @@ class RequestTest extends \Test\TestCase {
// ensure there's no error attempting to decode the content
}
+ public function testNotScimJsonPost() {
+ global $data;
+ $data = 'this is not valid scim json';
+ $vars = [
+ 'method' => 'POST',
+ 'server' => ['CONTENT_TYPE' => 'application/scim+json; utf-8']
+ ];
+
+ $request = new Request(
+ $vars,
+ $this->requestId,
+ $this->config,
+ $this->csrfTokenManager,
+ $this->stream
+ );
+
+ $this->assertEquals('POST', $request->method);
+ $result = $request->post;
+ // ensure there's no error attempting to decode the content
+ }
+
+ public function testNotCustomJsonPost() {
+ global $data;
+ $data = 'this is not valid json';
+ $vars = [
+ 'method' => 'POST',
+ 'server' => ['CONTENT_TYPE' => 'application/custom-type+json; utf-8']
+ ];
+
+ $request = new Request(
+ $vars,
+ $this->requestId,
+ $this->config,
+ $this->csrfTokenManager,
+ $this->stream
+ );
+
+ $this->assertEquals('POST', $request->method);
+ $result = $request->post;
+ // ensure there's no error attempting to decode the content
+ }
+
public function testPatch() {
global $data;
$data = http_build_query(['name' => 'John Q. Public', 'nickname' => 'Joey'], '', '&');
@@ -309,6 +400,98 @@ class RequestTest extends \Test\TestCase {
$this->assertSame(null, $result['nickname']);
}
+ public function testScimJsonPatchAndPut() {
+ global $data;
+
+ // PUT content
+ $data = '{"userName": "sometestusername", "displayName": "Example User"}';
+ $vars = [
+ 'method' => 'PUT',
+ 'server' => ['CONTENT_TYPE' => 'application/scim+json; utf-8'],
+ ];
+
+ $request = new Request(
+ $vars,
+ $this->requestId,
+ $this->config,
+ $this->csrfTokenManager,
+ $this->stream
+ );
+
+ $this->assertSame('PUT', $request->method);
+ $result = $request->put;
+
+ $this->assertSame('sometestusername', $result['userName']);
+ $this->assertSame('Example User', $result['displayName']);
+
+ // PATCH content
+ $data = '{"userName": "sometestusername", "displayName": null}';
+ $vars = [
+ 'method' => 'PATCH',
+ 'server' => ['CONTENT_TYPE' => 'application/scim+json; utf-8'],
+ ];
+
+ $request = new Request(
+ $vars,
+ $this->requestId,
+ $this->config,
+ $this->csrfTokenManager,
+ $this->stream
+ );
+
+ $this->assertSame('PATCH', $request->method);
+ $result = $request->patch;
+
+ $this->assertSame('sometestusername', $result['userName']);
+ $this->assertSame(null, $result['displayName']);
+ }
+
+ public function testCustomJsonPatchAndPut() {
+ global $data;
+
+ // PUT content
+ $data = '{"propertyA": "sometestvalue", "propertyB": "someothertestvalue"}';
+ $vars = [
+ 'method' => 'PUT',
+ 'server' => ['CONTENT_TYPE' => 'application/custom-type+json; utf-8'],
+ ];
+
+ $request = new Request(
+ $vars,
+ $this->requestId,
+ $this->config,
+ $this->csrfTokenManager,
+ $this->stream
+ );
+
+ $this->assertSame('PUT', $request->method);
+ $result = $request->put;
+
+ $this->assertSame('sometestvalue', $result['propertyA']);
+ $this->assertSame('someothertestvalue', $result['propertyB']);
+
+ // PATCH content
+ $data = '{"propertyA": "sometestvalue", "propertyB": null}';
+ $vars = [
+ 'method' => 'PATCH',
+ 'server' => ['CONTENT_TYPE' => 'application/custom-type+json; utf-8'],
+ ];
+
+ $request = new Request(
+ $vars,
+ $this->requestId,
+ $this->config,
+ $this->csrfTokenManager,
+ $this->stream
+ );
+
+ $this->assertSame('PATCH', $request->method);
+ $result = $request->patch;
+
+ $this->assertSame('sometestvalue', $result['propertyA']);
+ $this->assertSame(null, $result['propertyB']);
+ }
+
public function testPutStream() {
global $data;
$data = file_get_contents(__DIR__ . '/../../../data/testimage.png');