From 71d81da6c24a5fa24164e55d4090293f4f6fc9ac Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Thu, 3 Oct 2024 20:31:51 +0200 Subject: [PATCH 1/7] Started to fix unclear statements about OCS and REST Signed-off-by: Christian Wolf --- developer_manual/basics/controllers.rst | 15 +++- developer_manual/digging_deeper/rest_apis.rst | 69 ++++++++++++++++++- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/developer_manual/basics/controllers.rst b/developer_manual/basics/controllers.rst index 34bb1ddc314..172716f7fc3 100644 --- a/developer_manual/basics/controllers.rst +++ b/developer_manual/basics/controllers.rst @@ -732,10 +732,15 @@ The following policy for instance allows images, audio and videos from other dom OCS ^^^ -.. note:: This is purely for compatibility reasons. If you are planning to offer an external API, go for a :doc:`../digging_deeper/rest_apis` instead. +In order to simplify exchange of data between the Nextcloud backend and any client (be it the web frontend or whatever else), the OCS API has been introduced. +Here, JSON and XML responders have been prepared and are installed without additional effort. -In order to ease migration from OCS API routes to the App Framework, an additional controller and response have been added. To migrate your API you can use the **OCP\\AppFramework\\OCSController** base class and return your data in the form of a DataResponse in the following way: +.. note:: + The usage of OCS is closely related to the usage of :doc:`../digging_deeper/rest_apis`. + Unless you have a clear use-case, it is advised to use OCS over pure REST. + A more detailed description can be found in :ref:`ocs-vs-rest`. +To use OCS in your API you can use the **OCP\\AppFramework\\OCSController** base class and return your data in the form of a **DataResponse** in the following way: .. code-block:: php @@ -759,7 +764,7 @@ In order to ease migration from OCS API routes to the App Framework, an addition The format parameter works out of the box, no intervention is required. -In order to make routing work for OCS routes you need to add a separate 'ocs' entry to the routing table of your app. +In order to make routing work for OCS routes you need to add a separate 'ocs' entry to the routing table in ``appinf/routes.php`` of your app. Inside these are normal routes. .. code-block:: php @@ -778,6 +783,10 @@ Inside these are normal routes. Now your method will be reachable via ``/ocs/v2.php/apps//api/v1/shares`` +.. versionadded:: 29 + You can use the attribute ``ApiRoute`` as described in :doc:`Routing ` instead of the entry in ``appinfo/routed.php`` as an alternative. + + Handling errors ^^^^^^^^^^^^^^^ diff --git a/developer_manual/digging_deeper/rest_apis.rst b/developer_manual/digging_deeper/rest_apis.rst index 8a634f24027..354e70ee557 100644 --- a/developer_manual/digging_deeper/rest_apis.rst +++ b/developer_manual/digging_deeper/rest_apis.rst @@ -6,7 +6,8 @@ REST APIs .. sectionauthor:: Bernhard Posselt -Offering a RESTful API is not different from creating a :doc:`route <../basics/routing>` and :doc:`controllers <../basics/controllers>` for the web interface. It is recommended though to inherit from ApiController and add **@CORS** annotations to the methods so that `web applications will also be able to access the API `_. +Offering a RESTful API is not different from creating a :doc:`route <../basics/routing>` and :doc:`controllers <../basics/controllers>` for the web interface. +It is recommended though to inherit from ApiController and add **@CORS** annotations to the methods so that `web applications will also be able to access the API `_. .. code-block:: php @@ -44,7 +45,8 @@ CORS also needs a separate URL for the preflighted **OPTIONS** request that can ) -Keep in mind that multiple apps will likely depend on the API interface once it is published and they will move at different speeds to react to changes implemented in the API. Therefore it is recommended to version the API in the URL to not break existing apps when backwards incompatible changes are introduced:: +Keep in mind that multiple apps will likely depend on the API interface once it is published and they will move at different speeds to react to changes implemented in the API. +Therefore it is recommended to version the API in the URL to not break existing apps when backwards incompatible changes are introduced:: /index.php/apps/myapp/api/1.0/resource @@ -79,3 +81,66 @@ To add an additional method or header or allow less headers, simply pass additio } } + +.. _ocs-vs-rest: + +Relation of REST and OCS +------------------------ + +There is a close relationship between REST APIs and :ref:`OCS `. +Both provide a way to transmit data between the backend of the app in the Nextcloud server and some frontend. + +The following combinations of attributes might be useful for various scenarios: + +#. Plain frontend route: No special attribute related to CSRF or CORS +#. Plain Frontend with CRSF checks disabled: Add the ``#[NoCSRFRequired]`` attribute +#. REST route with CORS enabled: Add the ``#[CORS]`` attribute +#. OCS-based route + +There are different ways a clients might interact with your APIs. +These ways depend on your API configuration (what you allow) and on which route the request is finally made. + +- *Access from web frontend* means the user is browses the Nextcloud web frontend with a browser. +- *Access from an external app* indicates that the user is not using the normal browser (as logged in) but directly navigates a certain URL. + This can be in a browser tab or an external program (like an Android app or simply a curl command line). +- *Access from external website* means that the user browses some third party web site and *magically* data from your app appears. + Technically, the other website would embed/load/use images, JSON data, or other resources from a URL pointing to the Nextcloud server. + +.. list-table:: Comparision of different API types + :header-rows: 1 + :align: center + + * - Description + - 1 (plain) + - 2 (no CSRF) + - 3 (CORS) + - 4 (OCS) + * - URL prefix (relative to server) + - ``/apps//`` + - ``/apps//`` + - ``/apps//`` + - ``/ocs/v2.php/apps//`` + * - Access from web frontend + - yes + - yes (CSRF risk) + - yes (CSRF risk) + - yes + * - Access from external app + - --- (CSRF protection blocks) + - yes + - yes + - yes + * - Access from external web page + - --- + - --- + - yes + - yes + * - Transmitted data type + - plain data + - plain data + - plain data + - encapsulated data + +As a rule of thumb one can conclude that OCS provides a good way to handle most use cases. +The only exception to this is if you want to provide an API for external usage where you have to comply with an externally defined API scheme. +Here, the encapsulation introduced in OCS might be in your way. From 94c86946fe3bbc007fe2aaeca21d32c8074e5196 Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Thu, 3 Oct 2024 20:32:08 +0200 Subject: [PATCH 2/7] Fix some sphinx build watrnings Signed-off-by: Christian Wolf --- developer_manual/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/developer_manual/conf.py b/developer_manual/conf.py index 7770ac004b1..d8f7f37a838 100644 --- a/developer_manual/conf.py +++ b/developer_manual/conf.py @@ -183,7 +183,7 @@ #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. -'preamble': '\extrafloats{100}\maxdeadcycles=500\DeclareUnicodeCharacter{274C}{\sffamily X}', +'preamble': '\\extrafloats{100}\\maxdeadcycles=500\\DeclareUnicodeCharacter{274C}{\\sffamily X}', } # Grouping the document tree into LaTeX files. List of tuples From 0758bc05b0c613fc6f983f2e343c569211432805 Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Fri, 4 Oct 2024 00:04:53 +0200 Subject: [PATCH 3/7] Apply suggestions from code review Co-authored-by: Kate <26026535+provokateurin@users.noreply.github.com> Signed-off-by: Christian Wolf --- developer_manual/basics/controllers.rst | 2 +- developer_manual/digging_deeper/rest_apis.rst | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/developer_manual/basics/controllers.rst b/developer_manual/basics/controllers.rst index 172716f7fc3..587c0d9ab82 100644 --- a/developer_manual/basics/controllers.rst +++ b/developer_manual/basics/controllers.rst @@ -784,7 +784,7 @@ Inside these are normal routes. Now your method will be reachable via ``/ocs/v2.php/apps//api/v1/shares`` .. versionadded:: 29 - You can use the attribute ``ApiRoute`` as described in :doc:`Routing ` instead of the entry in ``appinfo/routed.php`` as an alternative. + You can use the attribute ``ApiRoute`` as described in :doc:`Routing ` instead of the entry in ``appinfo/routes.php`` as an alternative. Handling errors diff --git a/developer_manual/digging_deeper/rest_apis.rst b/developer_manual/digging_deeper/rest_apis.rst index 354e70ee557..9594609b6fd 100644 --- a/developer_manual/digging_deeper/rest_apis.rst +++ b/developer_manual/digging_deeper/rest_apis.rst @@ -92,10 +92,10 @@ Both provide a way to transmit data between the backend of the app in the Nextcl The following combinations of attributes might be useful for various scenarios: -#. Plain frontend route: No special attribute related to CSRF or CORS +#. Plain frontend route: ``Controller`` class #. Plain Frontend with CRSF checks disabled: Add the ``#[NoCSRFRequired]`` attribute -#. REST route with CORS enabled: Add the ``#[CORS]`` attribute -#. OCS-based route +#. REST route with CORS enabled: ``Controller`` class and ``#[CORS]`` attribute on the route +#. OCS-based route: ``OCSController`` class There are different ways a clients might interact with your APIs. These ways depend on your API configuration (what you allow) and on which route the request is finally made. @@ -134,13 +134,13 @@ These ways depend on your API configuration (what you allow) and on which route - --- - --- - yes - - yes + - no * - Transmitted data type - plain data - plain data - plain data - - encapsulated data + - encapsulated data (JSON or XML) -As a rule of thumb one can conclude that OCS provides a good way to handle most use cases. +As a rule of thumb one can conclude that OCS provides a good way to handle most use cases including sufficient security checks. The only exception to this is if you want to provide an API for external usage where you have to comply with an externally defined API scheme. -Here, the encapsulation introduced in OCS might be in your way. +Here, the encapsulation introduced in OCS and CSRF checks might be in your way. From 4b76fecfe3c8fa16455ca82d07ac3c5c89eaddd8 Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Mon, 7 Oct 2024 23:58:02 +0200 Subject: [PATCH 4/7] Fix OCS description after some research Signed-off-by: Christian Wolf --- developer_manual/digging_deeper/rest_apis.rst | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/developer_manual/digging_deeper/rest_apis.rst b/developer_manual/digging_deeper/rest_apis.rst index 9594609b6fd..f8eb89a847e 100644 --- a/developer_manual/digging_deeper/rest_apis.rst +++ b/developer_manual/digging_deeper/rest_apis.rst @@ -90,57 +90,63 @@ Relation of REST and OCS There is a close relationship between REST APIs and :ref:`OCS `. Both provide a way to transmit data between the backend of the app in the Nextcloud server and some frontend. -The following combinations of attributes might be useful for various scenarios: +The following combinations of attributes might be relevant for various scenarios: #. Plain frontend route: ``Controller`` class -#. Plain Frontend with CRSF checks disabled: Add the ``#[NoCSRFRequired]`` attribute -#. REST route with CORS enabled: ``Controller`` class and ``#[CORS]`` attribute on the route +#. Plain Frontend with CRSF checks disabled: ``Controller`` class and ``#[NoCSRFRequired]`` attribute on the method #. OCS-based route: ``OCSController`` class +#. OCS-based route with CSRF disabled: ``OCSController`` class and ``#[NoCSRFRequired]`` attribute on the method + +.. warning:: + Adding the ``#[NoCRSFRequired]`` attribute imposes a security risk. + You should not add this to your controller methods unless you understand the implications and be sure that you absolutely need the attribute. There are different ways a clients might interact with your APIs. These ways depend on your API configuration (what you allow) and on which route the request is finally made. - *Access from web frontend* means the user is browses the Nextcloud web frontend with a browser. - *Access from an external app* indicates that the user is not using the normal browser (as logged in) but directly navigates a certain URL. - This can be in a browser tab or an external program (like an Android app or simply a curl command line). -- *Access from external website* means that the user browses some third party web site and *magically* data from your app appears. - Technically, the other website would embed/load/use images, JSON data, or other resources from a URL pointing to the Nextcloud server. + This can be in a new browser tab or an external program (like an Android app or simply a curl command line). -.. list-table:: Comparision of different API types +.. list-table:: Comparison of different API types :header-rows: 1 :align: center * - Description - 1 (plain) - - 2 (no CSRF) - - 3 (CORS) + - 2 (w/o CSRF) - 4 (OCS) + - 4 (OCS w/o CSRF) * - URL prefix (relative to server) - - ``/apps//`` - ``/apps//`` - ``/apps//`` - ``/ocs/v2.php/apps//`` + - ``/ocs/v2.php/apps//`` * - Access from web frontend - yes - yes (CSRF risk) - - yes (CSRF risk) - yes + - yes (CSRF risk) * - Access from external app - - --- (CSRF protection blocks) - - yes - - yes - - yes - * - Access from external web page - - --- - --- - yes + - yes (with header [#]_) + - yes + * - Encapsulated data - no - * - Transmitted data type - - plain data - - plain data - - plain data - - encapsulated data (JSON or XML) + - no + - yes (JSON or XML) + - yes (JSON or XML) + +Methods from ``Controller`` classes can return ``DataResponse`` objects similar to ``OCSController`` class methods. +For methods of a ``Controller`` class, the data of this response is sent e.g. as JSON as you provide it. +Basically, the output is very similar to what ``json_encode`` would do. +In contrast, the OCS controller will encapsulate the data in an outer shell that provides some more (meta) information. +For example a status code (similar to the HTTP status code) is transmitted at top level. +The actual data is transmitted in the ``data`` property. As a rule of thumb one can conclude that OCS provides a good way to handle most use cases including sufficient security checks. The only exception to this is if you want to provide an API for external usage where you have to comply with an externally defined API scheme. Here, the encapsulation introduced in OCS and CSRF checks might be in your way. + +.. [#] The OCS controller needs the request header ``OCS-APIREQUEST`` to be set to ``true``. From ca6794c995f8b00cdd6fff9ab29d4f5f13ce9e89 Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Wed, 9 Oct 2024 00:30:22 +0200 Subject: [PATCH 5/7] Fix text about responders Signed-off-by: Christian Wolf --- developer_manual/basics/controllers.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/developer_manual/basics/controllers.rst b/developer_manual/basics/controllers.rst index 587c0d9ab82..65bdc782f54 100644 --- a/developer_manual/basics/controllers.rst +++ b/developer_manual/basics/controllers.rst @@ -382,6 +382,8 @@ Because returning JSON is such a common task, there's even a shorter way to do t Why does this work? Because the dispatcher sees that the controller did not return a subclass of a Response and asks the controller to turn the value into a Response. That's where responders come in. +.. _controller-responders: + Responders ^^^^^^^^^^ @@ -762,7 +764,11 @@ To use OCS in your API you can use the **OCP\\AppFramework\\OCSController** base } -The format parameter works out of the box, no intervention is required. +For ``OCSController`` classes and their methods, :ref:`responders ` can be registered as with any other ``Controller`` method. +The ``OCSController`` class have however automatically two respo nders pre-installed: +Both JSON (``application/json``) and XML (``text/xml``) are generated on-the-fly depending on the request by the browser/user. +To select the output format, the format parameter or the ``Accept`` header of the request work out of the box, no intervention is required. +It is advised to prefer the header generally, as this is the more programmatic way. In order to make routing work for OCS routes you need to add a separate 'ocs' entry to the routing table in ``appinf/routes.php`` of your app. Inside these are normal routes. From d5f5e94c0267929ccd45920ae13e659245c3cd89 Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Thu, 10 Oct 2024 00:36:14 +0200 Subject: [PATCH 6/7] Adding CORS back into the mix Signed-off-by: Christian Wolf --- developer_manual/digging_deeper/rest_apis.rst | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/developer_manual/digging_deeper/rest_apis.rst b/developer_manual/digging_deeper/rest_apis.rst index f8eb89a847e..f4ff8697cae 100644 --- a/developer_manual/digging_deeper/rest_apis.rst +++ b/developer_manual/digging_deeper/rest_apis.rst @@ -93,20 +93,29 @@ Both provide a way to transmit data between the backend of the app in the Nextcl The following combinations of attributes might be relevant for various scenarios: #. Plain frontend route: ``Controller`` class -#. Plain Frontend with CRSF checks disabled: ``Controller`` class and ``#[NoCSRFRequired]`` attribute on the method +#. Plain frontend with CRSF checks disabled: ``Controller`` class and ``#[NoCSRFRequired]`` attribute on the method +#. REST route with CORS enabled: ``Controller`` class and ``#[CORS]`` and ``#[NoCSRFRequired]`` attributes on the route #. OCS-based route: ``OCSController`` class -#. OCS-based route with CSRF disabled: ``OCSController`` class and ``#[NoCSRFRequired]`` attribute on the method +#. OCS-based route with CORS enabled: ``OCSController`` class and ``#[CORS]`` attribute on the method .. warning:: Adding the ``#[NoCRSFRequired]`` attribute imposes a security risk. You should not add this to your controller methods unless you understand the implications and be sure that you absolutely need the attribute. +.. warning:: + Adding the attribute ``#[CORS]`` alone is not sufficient to allow access using CORS. + The CSRF checker will typically fail, so enabling CORS enforces you to disable the CSRF checker as well. + Although the disabled CSRF checker in itself is a security issue to consider, adding CORS opens up this even more. + You should make sure, that you understand the implications completely when enabling CORS and do so only when there is a good use case. + There are different ways a clients might interact with your APIs. These ways depend on your API configuration (what you allow) and on which route the request is finally made. - *Access from web frontend* means the user is browses the Nextcloud web frontend with a browser. - *Access from an external app* indicates that the user is not using the normal browser (as logged in) but directly navigates a certain URL. This can be in a new browser tab or an external program (like an Android app or simply a curl command line). +- *Access from external website* means that the user browses some third party web site and *magically* data from your app appears. + Technically, the other website would embed/load/use images, JSON data, or other resources from a URL pointing to the Nextcloud server. .. list-table:: Comparison of different API types :header-rows: 1 @@ -115,9 +124,11 @@ These ways depend on your API configuration (what you allow) and on which route * - Description - 1 (plain) - 2 (w/o CSRF) + - 3 (CORS) - 4 (OCS) - - 4 (OCS w/o CSRF) + - 5 (OCS+CORS) * - URL prefix (relative to server) + - ``/apps//`` - ``/apps//`` - ``/apps//`` - ``/ocs/v2.php/apps//`` @@ -125,14 +136,23 @@ These ways depend on your API configuration (what you allow) and on which route * - Access from web frontend - yes - yes (CSRF risk) - - yes - yes (CSRF risk) + - yes + - yes (CSRF risk [#]_) * - Access from external app - --- - yes + - yes - yes (with header [#]_) - yes + * - Access from external website + - --- + - --- + - yes + - --- + - yes * - Encapsulated data + - no - no - no - yes (JSON or XML) @@ -149,4 +169,7 @@ As a rule of thumb one can conclude that OCS provides a good way to handle most The only exception to this is if you want to provide an API for external usage where you have to comply with an externally defined API scheme. Here, the encapsulation introduced in OCS and CSRF checks might be in your way. +.. [#] Only if you have set ``#[NoCSRFRequired]``. + OCS controllers have other CSRF checks in place that might with CORS without disabling the CSRF checks completely. + Using the ``OCS-APIREQUEST`` header is also a CSRF protection but is compatible with CORS. .. [#] The OCS controller needs the request header ``OCS-APIREQUEST`` to be set to ``true``. From adef92fc9611e54cf63cc9d3385ca5ccd35e8690 Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Wed, 30 Oct 2024 19:34:02 +0100 Subject: [PATCH 7/7] Apply suggestions from code review Fix typos and other errors in the code as suggested by review process Co-authored-by: Kate <26026535+provokateurin@users.noreply.github.com> Signed-off-by: Christian Wolf --- developer_manual/basics/controllers.rst | 6 +++--- developer_manual/digging_deeper/rest_apis.rst | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/developer_manual/basics/controllers.rst b/developer_manual/basics/controllers.rst index 65bdc782f54..a8c9d14edc2 100644 --- a/developer_manual/basics/controllers.rst +++ b/developer_manual/basics/controllers.rst @@ -765,12 +765,12 @@ To use OCS in your API you can use the **OCP\\AppFramework\\OCSController** base } For ``OCSController`` classes and their methods, :ref:`responders ` can be registered as with any other ``Controller`` method. -The ``OCSController`` class have however automatically two respo nders pre-installed: +The ``OCSController`` class have however automatically two responders pre-installed: Both JSON (``application/json``) and XML (``text/xml``) are generated on-the-fly depending on the request by the browser/user. -To select the output format, the format parameter or the ``Accept`` header of the request work out of the box, no intervention is required. +To select the output format, the `?format=` query parameter or the ``Accept`` header of the request work out of the box, no intervention is required. It is advised to prefer the header generally, as this is the more programmatic way. -In order to make routing work for OCS routes you need to add a separate 'ocs' entry to the routing table in ``appinf/routes.php`` of your app. +In order to make routing work for OCS routes you need to add a separate 'ocs' entry to the routing table in ``appinfo/routes.php`` of your app. Inside these are normal routes. .. code-block:: php diff --git a/developer_manual/digging_deeper/rest_apis.rst b/developer_manual/digging_deeper/rest_apis.rst index f4ff8697cae..38e785915d6 100644 --- a/developer_manual/digging_deeper/rest_apis.rst +++ b/developer_manual/digging_deeper/rest_apis.rst @@ -94,9 +94,9 @@ The following combinations of attributes might be relevant for various scenarios #. Plain frontend route: ``Controller`` class #. Plain frontend with CRSF checks disabled: ``Controller`` class and ``#[NoCSRFRequired]`` attribute on the method -#. REST route with CORS enabled: ``Controller`` class and ``#[CORS]`` and ``#[NoCSRFRequired]`` attributes on the route -#. OCS-based route: ``OCSController`` class -#. OCS-based route with CORS enabled: ``OCSController`` class and ``#[CORS]`` attribute on the method +#. Plain frontend route with CORS enabled: ``Controller`` class and ``#[CORS]`` and ``#[NoCSRFRequired]`` attributes on the route +#. OCS route: ``OCSController`` class +#. OCS route with CORS enabled: ``OCSController`` class and ``#[CORS]`` attribute on the method .. warning:: Adding the ``#[NoCRSFRequired]`` attribute imposes a security risk. @@ -114,8 +114,8 @@ These ways depend on your API configuration (what you allow) and on which route - *Access from web frontend* means the user is browses the Nextcloud web frontend with a browser. - *Access from an external app* indicates that the user is not using the normal browser (as logged in) but directly navigates a certain URL. This can be in a new browser tab or an external program (like an Android app or simply a curl command line). -- *Access from external website* means that the user browses some third party web site and *magically* data from your app appears. - Technically, the other website would embed/load/use images, JSON data, or other resources from a URL pointing to the Nextcloud server. +- *Access from external website* means that the user browses some third party web site and data from your Nextcloud server appears. + The other website has to embed/load/use images, JSON data, or other resources from a URL pointing to the Nextcloud server, to be able to do this. .. list-table:: Comparison of different API types :header-rows: 1 @@ -170,6 +170,6 @@ The only exception to this is if you want to provide an API for external usage w Here, the encapsulation introduced in OCS and CSRF checks might be in your way. .. [#] Only if you have set ``#[NoCSRFRequired]``. - OCS controllers have other CSRF checks in place that might with CORS without disabling the CSRF checks completely. - Using the ``OCS-APIREQUEST`` header is also a CSRF protection but is compatible with CORS. -.. [#] The OCS controller needs the request header ``OCS-APIREQUEST`` to be set to ``true``. + OCS controllers have other CSRF checks in place that work with CORS without disabling the CSRF checks completely. + Using the ``OCS-APIRequest`` header is a CSRF protection which is compatible with CORS. +.. [#] The OCS controller needs the request header ``OCS-APIRequest`` to be set to ``true``.