aboutsummaryrefslogblamecommitdiffhomepage
path: root/tests/api/ApiMiddlewareTest.php
blob: 2afac28bd121d1886211b0c1944afa0afec3d2cf (plain) (tree)
1
2
3
4
5
6
     

                      
                                 
                    
                                 














                                                                        
                                                 






                                                              
                                  













                                      
                                                                                       
       
                                    
     
                                                                         




                                                             

                                                      

                                               
                                               
                                                                           




                                                   
                                       




                                      














































                                                                                                         









































                                                                                         
                                                                                      





















                                                                                         
                                                                                      













                                                                                         
                                                








                                                                                                                     
                                                                                      


       


















                                                                                  
                                                                                      



                                                               










                                                                                         
                                                








                                                                                   
                                                                                      

     
<?php
namespace Shaarli\Api;

use Shaarli\Config\ConfigManager;
use Shaarli\History;
use Shaarli\Plugin\PluginManager;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;

/**
 * Class ApiMiddlewareTest
 *
 * Test the REST API Slim Middleware.
 *
 * Note that we can't test a valid use case here, because the middleware
 * needs to call a valid controller/action during its execution.
 *
 * @package Api
 */
class ApiMiddlewareTest extends \Shaarli\TestCase
{
    /**
     * @var string datastore to test write operations
     */
    protected static $testDatastore = 'sandbox/datastore.php';

    /**
     * @var ConfigManager instance
     */
    protected $conf;

    /**
     * @var \ReferenceLinkDB instance.
     */
    protected $refDB = null;

    /**
     * @var Container instance.
     */
    protected $container;

    /**
     * Before every test, instantiate a new Api with its config, plugins and bookmarks.
     */
    protected function setUp(): void
    {
        $this->conf = new ConfigManager('tests/utils/config/configJson');
        $this->conf->set('api.secret', 'NapoleonWasALizard');

        $this->refDB = new \ReferenceLinkDB();
        $this->refDB->write(self::$testDatastore);

        $history = new History('sandbox/history.php');

        $this->container = new Container();
        $this->container['conf'] = $this->conf;
        $this->container['history'] = $history;
        $this->container['pluginManager'] = new PluginManager($this->conf);
    }

    /**
     * After every test, remove the test datastore.
     */
    protected function tearDown(): void
    {
        @unlink(self::$testDatastore);
    }

    /**
     * Invoke the middleware with a valid token
     */
    public function testInvokeMiddlewareWithValidToken(): void
    {
        $next = function (Request $request, Response $response): Response {
            return $response;
        };
        $mw = new ApiMiddleware($this->container);
        $env = Environment::mock([
            'REQUEST_METHOD' => 'GET',
            'REQUEST_URI' => '/echo',
            'HTTP_AUTHORIZATION'=> 'Bearer ' . ApiUtilsTest::generateValidJwtToken('NapoleonWasALizard'),
        ]);
        $request = Request::createFromEnvironment($env);
        $response = new Response();
        /** @var Response $response */
        $response = $mw($request, $response, $next);

        $this->assertEquals(200, $response->getStatusCode());
    }

    /**
     * Invoke the middleware with a valid token
     * Using specific Apache CGI redirected authorization.
     */
    public function testInvokeMiddlewareWithValidTokenFromRedirectedHeader(): void
    {
        $next = function (Request $request, Response $response): Response {
            return $response;
        };

        $token = 'Bearer ' . ApiUtilsTest::generateValidJwtToken('NapoleonWasALizard');
        $this->container->environment['REDIRECT_HTTP_AUTHORIZATION'] = $token;
        $mw = new ApiMiddleware($this->container);
        $env = Environment::mock([
            'REQUEST_METHOD' => 'GET',
            'REQUEST_URI' => '/echo',
        ]);
        $request = Request::createFromEnvironment($env);
        $response = new Response();
        /** @var Response $response */
        $response = $mw($request, $response, $next);

        $this->assertEquals(200, $response->getStatusCode());
    }

    /**
     * Invoke the middleware with the API disabled:
     * should return a 401 error Unauthorized.
     */
    public function testInvokeMiddlewareApiDisabled()
    {
        $this->conf->set('api.enabled', false);
        $mw = new ApiMiddleware($this->container);
        $env = Environment::mock([
            'REQUEST_METHOD' => 'GET',
            'REQUEST_URI' => '/echo',
        ]);
        $request = Request::createFromEnvironment($env);
        $response = new Response();
        /** @var Response $response */
        $response = $mw($request, $response, null);

        $this->assertEquals(401, $response->getStatusCode());
        $body = json_decode((string) $response->getBody());
        $this->assertEquals('Not authorized', $body);
    }

    /**
     * Invoke the middleware with the API disabled in debug mode:
     * should return a 401 error Unauthorized - with a specific message and a stacktrace.
     */
    public function testInvokeMiddlewareApiDisabledDebug()
    {
        $this->conf->set('api.enabled', false);
        $this->conf->set('dev.debug', true);
        $mw = new ApiMiddleware($this->container);
        $env = Environment::mock([
            'REQUEST_METHOD' => 'GET',
            'REQUEST_URI' => '/echo',
        ]);
        $request = Request::createFromEnvironment($env);
        $response = new Response();
        /** @var Response $response */
        $response = $mw($request, $response, null);

        $this->assertEquals(401, $response->getStatusCode());
        $body = json_decode((string) $response->getBody());
        $this->assertEquals('Not authorized: API is disabled', $body->message);
        $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
    }

    /**
     * Invoke the middleware without a token (debug):
     * should return a 401 error Unauthorized - with a specific message and a stacktrace.
     */
    public function testInvokeMiddlewareNoTokenProvidedDebug()
    {
        $this->conf->set('dev.debug', true);
        $mw = new ApiMiddleware($this->container);
        $env = Environment::mock([
            'REQUEST_METHOD' => 'GET',
            'REQUEST_URI' => '/echo',
        ]);
        $request = Request::createFromEnvironment($env);
        $response = new Response();
        /** @var Response $response */
        $response = $mw($request, $response, null);

        $this->assertEquals(401, $response->getStatusCode());
        $body = json_decode((string) $response->getBody());
        $this->assertEquals('Not authorized: JWT token not provided', $body->message);
        $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
    }

    /**
     * Invoke the middleware without a secret set in settings (debug):
     * should return a 401 error Unauthorized - with a specific message and a stacktrace.
     */
    public function testInvokeMiddlewareNoSecretSetDebug()
    {
        $this->conf->set('dev.debug', true);
        $this->conf->set('api.secret', '');
        $mw = new ApiMiddleware($this->container);
        $env = Environment::mock([
            'REQUEST_METHOD' => 'GET',
            'REQUEST_URI' => '/echo',
            'HTTP_AUTHORIZATION'=> 'Bearer jwt',
        ]);
        $request = Request::createFromEnvironment($env);
        $response = new Response();
        /** @var Response $response */
        $response = $mw($request, $response, null);

        $this->assertEquals(401, $response->getStatusCode());
        $body = json_decode((string) $response->getBody());
        $this->assertEquals('Not authorized: Token secret must be set in Shaarli\'s administration', $body->message);
        $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
    }

    /**
     * Invoke the middleware with an invalid JWT token header
     */
    public function testInvalidJwtAuthHeaderDebug()
    {
        $this->conf->set('dev.debug', true);
        $mw = new ApiMiddleware($this->container);
        $env = Environment::mock([
            'REQUEST_METHOD' => 'GET',
            'REQUEST_URI' => '/echo',
            'HTTP_AUTHORIZATION'=> 'PolarBearer jwt',
        ]);
        $request = Request::createFromEnvironment($env);
        $response = new Response();
        /** @var Response $response */
        $response = $mw($request, $response, null);

        $this->assertEquals(401, $response->getStatusCode());
        $body = json_decode((string) $response->getBody());
        $this->assertEquals('Not authorized: Invalid JWT header', $body->message);
        $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
    }

    /**
     * Invoke the middleware with an invalid JWT token (debug):
     * should return a 401 error Unauthorized - with a specific message and a stacktrace.
     *
     * Note: specific JWT errors tests are handled in ApiUtilsTest.
     */
    public function testInvokeMiddlewareInvalidJwtDebug()
    {
        $this->conf->set('dev.debug', true);
        $mw = new ApiMiddleware($this->container);
        $env = Environment::mock([
            'REQUEST_METHOD' => 'GET',
            'REQUEST_URI' => '/echo',
            'HTTP_AUTHORIZATION'=> 'Bearer jwt',
        ]);
        $request = Request::createFromEnvironment($env);
        $response = new Response();
        /** @var Response $response */
        $response = $mw($request, $response, null);

        $this->assertEquals(401, $response->getStatusCode());
        $body = json_decode((string) $response->getBody());
        $this->assertEquals('Not authorized: Malformed JWT token', $body->message);
        $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
    }
}