From 0fea1035764ceadf0ba4944cb2aae93909f40c16 Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Thu, 3 Oct 2024 20:31:51 +0200 Subject: [PATCH 01/14] 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 48ed0e06f8ef347c6f907c598f41eb8a06012cb4 Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Thu, 3 Oct 2024 20:32:08 +0200 Subject: [PATCH 02/14] 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 3b4e42e59bdf742dd3ca831dcf9632ba20be1cf5 Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Fri, 4 Oct 2024 00:04:53 +0200 Subject: [PATCH 03/14] 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 f5b12c87dd9751cfd2246d9c793d6fed35f53ab7 Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Mon, 7 Oct 2024 23:58:02 +0200 Subject: [PATCH 04/14] 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 a4ad18d4a03e9a548f82aff2f303238cc120e82d Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Wed, 9 Oct 2024 00:30:22 +0200 Subject: [PATCH 05/14] 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 b07d45e655411f5a8274ab36a0e6861f93787f54 Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Thu, 10 Oct 2024 00:36:14 +0200 Subject: [PATCH 06/14] 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 1141f66da8bdef12e723731ed3b4659d4b7101c7 Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Wed, 30 Oct 2024 19:34:02 +0100 Subject: [PATCH 07/14] 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``. From c1e324f01879e7f4382895a17a3b31056de14fe3 Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Mon, 20 Jan 2025 16:06:05 +0100 Subject: [PATCH 08/14] Restructure significant part of the controller documentation Signed-off-by: Christian Wolf --- developer_manual/basics/controllers.rst | 543 +++++++++++++++--------- developer_manual/basics/routing.rst | 47 +- 2 files changed, 378 insertions(+), 212 deletions(-) diff --git a/developer_manual/basics/controllers.rst b/developer_manual/basics/controllers.rst index a8c9d14edc2..56af3a18a07 100644 --- a/developer_manual/basics/controllers.rst +++ b/developer_manual/basics/controllers.rst @@ -335,15 +335,185 @@ Cookies can be set or modified directly on the response class: } } +.. _controller_html_responses: -Responses ---------- +HTML-based Responses +-------------------- Similar to how every controller receives a request object, every controller method has to return a Response. This can be in the form of a Response subclass or in the form of a value that can be handled by a registered responder. +The usage of templates allows to return HTML code to the user. +This is typically used as a starting point to load the website. +This code is by default encapsulated by the server to provide some common styling (e.g. the header row). +The code then uses JavaScript to load further components (see :ref:`Frontend building in Vue`) and the actual data. +This section only focuses on the actual HTML content, not the data to fill into the dynamic pages. + + +.. _controller_template: + +Templates +^^^^^^^^^ + +A :doc:`template ` can be rendered by returning a TemplateResponse. A TemplateResponse takes the following parameters: + +* **appName**: tells the template engine in which app the template should be located +* **templateName**: the name of the template inside the templates/ folder without the .php extension +* **parameters**: optional array parameters that are available in the template through $_, e.g.:: + + array('key' => 'something') + + can be accessed through:: + + $_['key'] + +* **renderAs**: defaults to *user*, tells Nextcloud if it should include it in the web interface, or in case *blank* is passed solely render the template + +.. code-block:: php + + 'hi'); + return new TemplateResponse($this->appName, $templateName, $parameters); + } + + } + +Showing a template is the only exception to the rule to :ref:`not disable CSRF checks `: +The user might type the URL directly (or use a browser bookmark or similar) to navigate to a HTML template. +Therefore, usage of the ``#[NoCSRFRequired]`` attribute (see :ref:`below`) is acceptable in this context. + +Public page templates +^^^^^^^^^^^^^^^^^^^^^ + +For public pages, that are rendered to users who are not logged in to the +Nextcloud instance, a ``OCP\\AppFramework\\Http\\Template\\PublicTemplateResponse`` should be used, to load the +correct base template. It also allows adding an optional set of actions that +will be shown in the top right corner of the public page. + + +.. code-block:: php + + appName, 'main', []); + $template->setHeaderTitle('Public page'); + $template->setHeaderDetails('some details'); + $template->setHeaderActions([ + new SimpleMenuAction('download', 'Label 1', 'icon-css-class1', 'link-url', 0), + new SimpleMenuAction('share', 'Label 2', 'icon-css-class2', 'link-url', 10), + ]); + return $template; + } + + } + +The header title and subtitle will be rendered in the header, next to the logo. +The action with the highest priority (lowest number) will be used as the +primary action, others will shown in the popover menu on demand. + +A ``OCP\\AppFramework\\Http\\Template\\SimpleMenuAction`` will be a link with an icon added to the menu. App +developers can implement their own types of menu renderings by adding a custom +class implementing the ``OCP\\AppFramework\\Http\\Template\\IMenuAction`` interface. + +As the public template is also some HTML template, the same argumentation as for :ref:`regular templates` regarding the CSRF checks hold true: +The usage of ``#[NoCSRFRequired]`` for public pages is considered acceptable and is actually needed to visit the page without an active account. + +Data-based responses +-------------------- + +In contrast to the HTML template responses, the data responses return some user-data in packed form. +There are different encodings thinkable like JSON, XML, or other formats. +The main point is that the data is requested by the browser using JavaScript on behalf of the shown website. +The user only indirectly requested the data by user interaction with the frontend. + + +.. _ocscontroller: + +OCS +^^^ + +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. + +.. 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 + + ` can be registered as with any other ``Controller`` method. +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=`` 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 :ref:`a separate 'ocs' entry` to the routing table in ``appinfo/routes.php`` of your app. +Inside these are normal routes. + +.. code-block:: php + + [ + [ + 'name' => 'Share#getShares', + 'url' => '/api/v1/shares', + 'verb' => 'GET', + ], + ], + ]; + +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/routes.php`` as an alternative. + + JSON ^^^^ +.. warning:: + The usage of standard controller to access content data like JSON (no HTML) is considered legacy. + Better use :ref:`OCS ` for this type of requests. + Returning JSON is simple, just pass an array to a JSONResponse: .. code-block:: php @@ -382,6 +552,40 @@ 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. +.. deprecated:: 30 + + Usage of classical controllers for data transmission should be avoided. Use OCS instead. + + +Handling errors +^^^^^^^^^^^^^^^ + +Sometimes a request should fail, for instance if an author with id 1 is requested but does not exist. In that case use an appropriate `HTTP error code `_ to signal the client that an error occurred. + +Each response subclass has access to the **setStatus** method which lets you set an HTTP status code. To return a JSONResponse signaling that the author with id 1 has not been found, use the following code: + +.. code-block:: php + + ` can be rendered by returning a TemplateResponse. A TemplateResponse takes the following parameters: - -* **appName**: tells the template engine in which app the template should be located -* **templateName**: the name of the template inside the templates/ folder without the .php extension -* **parameters**: optional array parameters that are available in the template through $_, e.g.:: - - array('key' => 'something') - - can be accessed through:: - - $_['key'] - -* **renderAs**: defaults to *user*, tells Nextcloud if it should include it in the web interface, or in case *blank* is passed solely render the template - -.. code-block:: php - - 'hi'); - return new TemplateResponse($this->appName, $templateName, $parameters); - } - - } - -Public page templates -^^^^^^^^^^^^^^^^^^^^^ - -For public pages, that are rendered to users who are not logged in to the -Nextcloud instance, a ``OCP\\AppFramework\\Http\\Template\\PublicTemplateResponse`` should be used, to load the -correct base template. It also allows adding an optional set of actions that -will be shown in the top right corner of the public page. - - -.. code-block:: php - - appName, 'main', []); - $template->setHeaderTitle('Public page'); - $template->setHeaderDetails('some details'); - $template->setHeaderActions([ - new SimpleMenuAction('download', 'Label 1', 'icon-css-class1', 'link-url', 0), - new SimpleMenuAction('share', 'Label 2', 'icon-css-class2', 'link-url', 10), - ]); - return $template; - } - - } - -The header title and subtitle will be rendered in the header, next to the logo. -The action with the highest priority (lowest number) will be used as the -primary action, others will shown in the popover menu on demand. - -A ``OCP\\AppFramework\\Http\\Template\\SimpleMenuAction`` will be a link with an icon added to the menu. App -developers can implement their own types of menu renderings by adding a custom -class implementing the ``OCP\\AppFramework\\Http\\Template\\IMenuAction`` interface. - - +Some special responses are present as well. +These are discussed here. Redirects ^^^^^^^^^ @@ -668,45 +799,58 @@ If you want to use a custom, lazily rendered response simply implement the inter .. note:: Because this code is rendered after several usually built in helpers, you need to take care of errors and proper HTTP caching by yourself. -Modifying the content security policy -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -By default Nextcloud disables all resources which are not served on the same domain, forbids cross domain requests and disables inline CSS and JavaScript by setting a `Content Security Policy `_. -However if an app relies on third-party media or other features which are forbidden by the current policy the policy can be relaxed. +Security considerations +----------------------- -.. note:: Double check your content and edge cases before you relax the policy! Also read the `documentation provided by MDN `_ +Depending on your use-case, you can tighten or loosen the security measurements installed by default on the routes. +This section gives you a quick overview over the options. -To relax the policy pass an instance of the ContentSecurityPolicy class to your response. The methods on the class can be chained. +.. _controller_authentication: -The following methods turn off security features by passing in **true** as the **$isAllowed** parameter +Authentication +^^^^^^^^^^^^^^ -* **allowInlineScript** (bool $isAllowed) -* **allowInlineStyle** (bool $isAllowed) -* **allowEvalScript** (bool $isAllowed) -* **useStrictDynamic** (bool $isAllowed) +By default every controller method enforces the maximum security, which is: - Trust all scripts that are loaded by a trusted script, see 'script-src' and 'strict-dynamic' +* Ensure that the user is admin +* Ensure that the user is logged in +* Ensure that the user has passed the two-factor challenge, if applicable +* Ensure the request is no CSRF attack, that is at least one of the following: -* **useStrictDynamicOnScripts** (bool $isAllowed) + - Ensure the CSRF token is present and valid + - Ensure the ``OCS-APIRequest`` header is present and set to ``true`` [1]_ - Trust all scripts that are loaded by a trusted script which was loaded using a ``