[Resolved] I get an error to request with Guzzle

Hi everyone :slight_smile:

I develop an application for Nextcloud and I get an error when I request with the POST verb from Guzzle.

There here my code :

<?php
namespace OCA\Workspace\Controller;

use OCP\IRequest;
use OCP\AppFramework\Controller;
use \GuzzleHttp\Client;
use OCP\AppFramework\Http\JSONResponse;

class AclManagerController extends Controller {
    
    public function __construct($AppName, IRequest $request)
    {
        parent::__construct($AppName, $request);
    }

    /**
     * @NoAdminRequired
     * @NoCSRFRequired
     * 
     * @var string $folderId
     * @var string $gid
     */
    public function addGroupAdvancedPermissions($folderId, $gid){

        $client = new Client();

        $response = $client->request(
            'POST',
            'https://server-nextcloud/apps/groupfolders/folders/'. $folderId .'/manageACL',
            [
                'body' =>
                    [
                        'mappingType' => 'group',
                        'mappingId' => $gid,
                        'manageAcl' => true
                    ],
                'headers' =>
                    [
                        'Content-Type' => 'application/x-www-form-urlencoded',
                        'OCS-APIRequest' => true
                    ]
            ]
        );
        print_r($response);
        
        return new JSONResponse([ 'folderId' => $folderId, 'gid' => $gid  ]);
    }
}

To test the code, I use curl from my terminal :

curl -H "OCS-APIRequest: true" -X POST "https://username:password@server-nextcloud/apps/workspace/space/107/group/wsp_Iroh_GE/acl"

And I get this message : Message: Return value of OC\AppFramework\Middleware\MiddlewareDispatcher::afterException() must be an instance of OCP\AppFramework\Http\Response, null returned.

I don’t understand this error…

I have tried to write the basic auth in my request (code side), but I have same problem :confused:

It is notable that I have tried with APIs from jsonPlaceholder (https://jsonplaceholder.typicode.com) with the POST verb and it succed !

Just as a side note: don’t use Guzzle directly but get it from the OCP\Http\Client\IClientService

Thanks @nickvergessen :slight_smile:

Do you have a documentation to understand working this lib ?

I have the impression to limit with the get, post or head http verb/method, no ?

I tried with this code and same problem :

<?php

// code

        $client = $this->clientService->newClient();
        $response = $client->post(
            'https://server-nextcloud/apps/groupfolders/folders/'. $folderId .'/manageACL',
            [
                'body' =>
                    [
                        'mappingType' => 'group',
                        'mappingId' => $gid,
                        'manageAcl' => true
                    ],
                'headers' =>
                    [
                        'Content-Type' => 'application/x-www-form-urlencoded',
                        'OCS-APIRequest' => true
                    ]
            ]
        );

        print_r($response);

// code

Looks good.

I think the problem is not on this part of things but on the other server which are you calling there.
Is there a full log message in the log file?

Of course, this here my full log message :slight_smile: :

{
    "reqId": "ocpYtQGqYVwpRAQ0CWj2",
    "level": 3,
    "time": "2021-05-05T11:48:34+02:00",
    "remoteAddr": "2a01:e0a:35:3010:fdc1:24b1:282a:80f3",
    "user": "my-username",
    "app": "index",
    "method": "POST",
    "url": "/apps/workspace/space/107/group/wsp_Iroh_GE/acl",
    "message": {
      "Exception": "TypeError",
      "Message": "Return value of OC\\AppFramework\\Middleware\\MiddlewareDispatcher::afterException() must be an instance of OCP\\AppFramework\\Http\\Response, null returned",
      "Code": 0,
      "Trace": [
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/lib/private/AppFramework/Http/Dispatcher.php",
          "line": 157,
          "function": "afterException",
          "class": "OC\\AppFramework\\Middleware\\MiddlewareDispatcher",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/lib/private/AppFramework/App.php",
          "line": 157,
          "function": "dispatch",
          "class": "OC\\AppFramework\\Http\\Dispatcher",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/lib/private/Route/Router.php",
          "line": 302,
          "function": "main",
          "class": "OC\\AppFramework\\App",
          "type": "::"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/lib/base.php",
          "line": 993,
          "function": "match",
          "class": "OC\\Route\\Router",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/index.php",
          "line": 37,
          "function": "handleRequest",
          "class": "OC",
          "type": "::"
        }
      ],
      "File": "/var/www/html/nextcloud/server-nextcloud/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php",
      "Line": 122,
      "CustomMessage": "--"
    },
    "userAgent": "curl/7.68.0",
    "version": "21.0.0.18"
  }

Note : I changed a few values by my-username for user key and server-nextcloud for the server name :slight_smile:

I’m sorry for the available verbs I can’t see the delete and put ^^’

Ouch…

I think to know where is a part problem :confused:

I coded a middleware to authorize a specific group to access to application !

My Middleware :

<?php

namespace OCA\Workspace\Middleware;

use OCP\AppFramework\Middleware;
use OCA\Workspace\AppInfo\Application;
use OCP\IGroupManager;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\IURLGenerator;
use OCP\IUserSession;
use OCA\Workspace\Middleware\Exceptions\NotGeneralManagerException;

class GeneralManagerMiddleware extends Middleware{

    private $groupManager;

    private $userSession;

    public function __construct(
        IGroupManager $groupManager,
        IURLGenerator $urlGenerator,
        IUserSession $userSession
    )
    {
        $this->groupManager = $groupManager;
        $this->urlGenerator = $urlGenerator;
        $this->userSession = $userSession;
    }

    public function beforeController($controller, $methodName ){

        if(! $this->groupManager->isInGroup($this->userSession->getUser()->getUID(), Application::GENERAL_MANAGER)){

            throw new NotGeneralManagerException();

        }        

    }

    // TODO: Find a solution to use this method.
    public function afterException($controller, $methodName, \Exception $exception){
        if($exception instanceof NotGeneralManagerException){
            /**
             * errorAccess tempalte is not exist.
            */ 
            $route = 'workspace.page.errorAccess';          
            $url = $this->urlGenerator->linkToRouteAbsolute($route, [ ]);
            
            /** 
             * TODO: Find a solution to use RedirectResponse class
             * return new RedirectResponse($url);
             * 
             * For example : return new TemplateResponse('workspace', 'errorAccess');
             */
            return new JSONResponse([
                'status' => 'forbidden',
                'msg' => 'You cannot to access this application.'
            ]);

        }

    }

}

And I comment using to this middleware in Application.php file :

<?php

namespace OCA\Workspace\AppInfo;

use OCP\AppFramework\App;

use OCP\IURLGenerator;
use OCP\IUser;

use \OCA\Workspace\Middleware\GeneralManagerMiddleware;

class Application extends App {
        public const APP_ID = 'workspace';

        public const GENERAL_MANAGER = "GeneralManager";

        public function __construct(array $urlParams=[] ) {
                parent::__construct(self::APP_ID, $urlParams);

                $container = $this->getContainer();

                // $container->registerService('GeneralManagerMiddleware', function($c){
                //     return new GeneralManagerMiddleware(
                //         $c->query(IURLGenerator::class),
                //         $c->query(IUser::class)
                //     );
                // });

                // $container->registerMiddleware('OCA\Workspace\Middleware\GeneralManagerMiddleware');
        }
}

I run my curl command again and I got this response :

{
    "reqId": "nJ3dCOObo49O3a1t8tps",
    "level": 3,
    "time": "2021-05-05T14:03:46+02:00",
    "remoteAddr": "2a01:e0a:35:3010:fdc1:24b1:282a:80f3",
    "user": "username",
    "app": "index",
    "method": "POST",
    "url": "/apps/workspace/space/107/group/wsp_Iroh_GE/acl",
    "message": {
      "Exception": "GuzzleHttp\\Exception\\ClientException",
      "Message": "Client error: `POST https://server-nextcloud/apps/groupfolders/folders/107/manageACL` resulted in a `401 Unauthorized` response:\n<?xml version=\"1.0\"?>\n<ocs>\n <meta>\n  <status>failure</status>\n  <statuscode>997</statuscode>\n  <message>Current user is (truncated...)\n",
      "Code": 401,
      "Trace": [
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/3rdparty/guzzlehttp/guzzle/src/Middleware.php",
          "line": 69,
          "function": "create",
          "class": "GuzzleHttp\\Exception\\RequestException",
          "type": "::"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/3rdparty/guzzlehttp/promises/src/Promise.php",
          "line": 204,
          "function": "GuzzleHttp\\{closure}",
          "class": "GuzzleHttp\\Middleware",
          "type": "::",
          "args": [
            "*** sensitive parameters replaced ***"
          ]
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/3rdparty/guzzlehttp/promises/src/Promise.php",
          "line": 153,
          "function": "callHandler",
          "class": "GuzzleHttp\\Promise\\Promise",
          "type": "::"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/3rdparty/guzzlehttp/promises/src/TaskQueue.php",
          "line": 48,
          "function": "GuzzleHttp\\Promise\\{closure}",
          "class": "GuzzleHttp\\Promise\\Promise",
          "type": "::",
          "args": [
            "*** sensitive parameters replaced ***"
          ]
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/3rdparty/guzzlehttp/promises/src/Promise.php",
          "line": 248,
          "function": "run",
          "class": "GuzzleHttp\\Promise\\TaskQueue",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/3rdparty/guzzlehttp/promises/src/Promise.php",
          "line": 224,
          "function": "invokeWaitFn",
          "class": "GuzzleHttp\\Promise\\Promise",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/3rdparty/guzzlehttp/promises/src/Promise.php",
          "line": 269,
          "function": "waitIfPending",
          "class": "GuzzleHttp\\Promise\\Promise",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/3rdparty/guzzlehttp/promises/src/Promise.php",
          "line": 226,
          "function": "invokeWaitList",
          "class": "GuzzleHttp\\Promise\\Promise",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/3rdparty/guzzlehttp/promises/src/Promise.php",
          "line": 62,
          "function": "waitIfPending",
          "class": "GuzzleHttp\\Promise\\Promise",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/3rdparty/guzzlehttp/guzzle/src/Client.php",
          "line": 187,
          "function": "wait",
          "class": "GuzzleHttp\\Promise\\Promise",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/lib/private/Http/Client/Client.php",
          "line": 307,
          "function": "request",
          "class": "GuzzleHttp\\Client",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/apps/workspace/lib/Controller/AclManagerController.php",
          "line": 35,
          "function": "post",
          "class": "OC\\Http\\Client\\Client",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/lib/private/AppFramework/Http/Dispatcher.php",
          "line": 218,
          "function": "addGroupAdvancedPermissions",
          "class": "OCA\\Workspace\\Controller\\AclManagerController",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/lib/private/AppFramework/Http/Dispatcher.php",
          "line": 127,
          "function": "executeController",
          "class": "OC\\AppFramework\\Http\\Dispatcher",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/lib/private/AppFramework/App.php",
          "line": 157,
          "function": "dispatch",
          "class": "OC\\AppFramework\\Http\\Dispatcher",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/lib/private/Route/Router.php",
          "line": 302,
          "function": "main",
          "class": "OC\\AppFramework\\App",
          "type": "::"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/lib/base.php",
          "line": 993,
          "function": "match",
          "class": "OC\\Route\\Router",
          "type": "->"
        },
        {
          "file": "/var/www/html/nextcloud/server-nextcloud/index.php",
          "line": 37,
          "function": "handleRequest",
          "class": "OC",
          "type": "::"
        }
      ],
      "File": "/var/www/html/nextcloud/server-nextcloud/3rdparty/guzzlehttp/guzzle/src/Exception/RequestException.php",
      "Line": 113,
      "CustomMessage": "--"
    },
    "userAgent": "curl/7.68.0",
    "version": "21.0.0.18"
  }

Hovewer, my current user is in this specefic group…

I would like to precise the basic auth if possible…

Edit 5th may, 14:10:

There is my controller :

<?php
namespace OCA\Workspace\Controller;

use OCP\IRequest;
use OCP\AppFramework\Controller;
// use \GuzzleHttp\Client;
use OCP\Http\Client\IClientService;
use OCP\AppFramework\Http\JSONResponse;

class AclManagerController extends Controller {
    
    private $clientService;

    public function __construct($AppName, IRequest $request, IClientService $clientService)
    {
        parent::__construct($AppName, $request);
        $this->clientService =  $clientService;
    }

    /**
     * @NoAdminRequired
     * @NoCSRFRequired
     * 
     * @var string $folderId
     * @var string $gid
     */
    public function addGroupAdvancedPermissions($folderId, $gid){
        // shell_exec('php occ groupfolders:permissions ' . $folderId . ' --group ' . $gid . ' -m');

        $client = $this->clientService->newClient();
        $response = $client->post(
            'https://server-nextcloud/apps/groupfolders/folders/'. $folderId .'/manageACL',
            [
                'body' => [
                        'mappingType' => 'group',
                        'mappingId' => $gid,
                        'manageAcl' => true
                ],
                'headers' => [
                        'Content-Type' => 'application/x-www-form-urlencoded',
                        'OCS-APIRequest' => true
                ]
            ]
        );

        print_r($response);


        // $client = new Client();

        // $response = $client->request(
        //     'POST',
        //     'https://server-nextcloud/apps/groupfolders/folders/'. $folderId .'/manageACL',
        //     [
        //         'body' =>
        //             [
        //                 'mappingType' => 'group',
        //                 'mappingId' => $gid,
        //                 'manageAcl' => true
        //             ],
        //         'headers' =>
        //             [
        //                 'Content-Type' => 'application/x-www-form-urlencoded',
        //                 'OCS-APIRequest' => true
        //             ]
        //     ]
        // );
        // print_r($response);
        
        return new JSONResponse([ 'folderId' => $folderId, 'gid' => $gid  ]);
    }
}

Okay, you should me be using the CORSMiddleware instead of Middleware to custom middleware’s exception ?
Aah okay, when I use my login in this request I have an CSRF Check error…
In the option from post() of the IClient interface. What should I write to cookie ?

In the example I have this :

<?php
              'cookies' => ['
                  'foo' => 'bar',
],

Src: server/IClient.php at master · nextcloud/server · GitHub

What should I replace by foo and bar ?

I find one part of this problem.

I must define the true value like a string value and not boolean value :

<?php

// code

        $dataResponse = $client->post(
            'https://server-nextcloud/apps/groupfolders/folders/'. $folderId .'/manageACL',
            [
                // 'auth' => [
                //     'username',
                //     'password'
                // ],
                'body' => [
                        'mappingType' => 'group',
                        'mappingId' => $gid,
                        'manageAcl' => true
                ],
                'headers' => [
                        'Content-Type' => 'application/x-www-form-urlencoded',
                        'OCS-APIRequest' => 'true',
                        'Accept' => 'application/json',
                ]
            ]
        );


// code

I must find a solution for this authentication problem…

@nickvergessen I resolved my poblem thanks to @StCyr !! :blush:

We are identify 2 solutions to authenticate :

  • Bearer ;
  • Basic ;

Option 1 : Bearer type

After reading this page : Login Flow — Nextcloud latest Developer Manual latest documentation .

I tested this resource : https://server-nextcloud/ocs/v2.php/core/getapppasswordwith curl :

curl -u my-username:my-password -H 'OCS-APIRequest: true' https://server-nextcloud/ocs/v2.php/core/getapppassword

And I got a token :

<?xml version="1.0"?>
<ocs>
 <meta>
  <status>ok</status>
  <statuscode>200</statuscode>
  <message>OK</message>
 </meta>
 <data>
  <apppassword>hGHryLTuxLm6VByKZUBOxjo7afLp2hje6Fi8A8pgmCVnihjL26mgIeXfLwRa4txFO0mdsLML</apppassword>
 </data>
</ocs>

Okay, so I change my function from my Controller :

    /**
     * @NoAdminRequired
     * @NoCSRFRequired
     * 
     * @var string $folderId
     * @var string $gid
     */
    public function addGroupAdvancedPermissions($folderId, $gid, $token){

        $client = $this->clientService->newClient();
        
        $dataResponse = $client->post(
            $this->urlGenerator->getBaseUrl() . '/apps/groupfolders/folders/'. $folderId .'/manageACL',
            [
                'body' => [
                        'mappingType' => 'group',
                        'mappingId' => $gid,
                        'manageAcl' => true
                ],
                'headers' => [
                        'Content-Type' => 'application/x-www-form-urlencoded',
                        'OCS-APIRequest' => 'true',
                        'Accept' => 'application/json',
                        'Authorization' => 'Bearer ' . $token,
                ]
            ]
        );

        $jsonResponse = $dataResponse->getBody();
        $response = json_decode($jsonResponse, true);
        
        return new JSONResponse($response);
    }

Then, I run curl on the corresponding route :

~$ curl -H "OCS-APIRequest: true" -X POST "https://my-username:my-password@server-nextcloud/apps/workspace/space/107/group/wsp_Iroh_GE/acl" -d "token=hGHryLTuxLm6VByKZUBOxjo7afLp2hje6Fi8A8pgmCVnihjL26mgIeXfLwRa4txFO0mdsLML"
{"ocs":{"meta":{"status":"ok","statuscode":100,"message":"OK","totalitems":"","itemsperpage":""},"data":{"success":true}}}

Great ! I can send a API request with a token for authentication !

Option 2 : Basic type

This option use the username and password from one user.

After reading and to understand this lib : server/IStore.php at master · nextcloud/server · GitHub .

The IStore be able to get the username and password.

I modified my function :

    /**
     * @NoAdminRequired
     * @NoCSRFRequired
     * 
     * @var string $folderId
     * @var string $gid
     */
    public function addGroupAdvancedPermissions($folderId, $gid){

        $login = $this->IStore->getLoginCredentials();

        $client = $this->clientService->newClient();
        
        $dataResponse = $client->post(
            $this->urlGenerator->getBaseUrl() . '/apps/groupfolders/folders/'. $folderId .'/manageACL',
            [
                'auth' => [
                    $login->getUID(),
                    $login->getPassword()
                ],
                'body' => [
                        'mappingType' => 'group',
                        'mappingId' => $gid,
                        'manageAcl' => true
                ],
                'headers' => [
                        'Content-Type' => 'application/x-www-form-urlencoded',
                        'OCS-APIRequest' => 'true',
                        'Accept' => 'application/json',
                ]
            ]
        );

        $jsonResponse = $dataResponse->getBody();
        $response = json_decode($jsonResponse, true);
        
        return new JSONResponse($response);
    }

Then, I run curl on the corresponding route :

~$ curl -H "OCS-APIRequest: true" -X POST "https://my-username:my-password@server-nextcloud/apps/workspace/space/107/group/wsp_Iroh_GE/acl"
{"ocs":{"meta":{"status":"ok","statuscode":100,"message":"OK","totalitems":"","itemsperpage":""},"data":{"success":true}}}

Great again ! We can send a API request with Basic auth with this method ! :slight_smile:

Thanks @StCyr again !! :blush: