Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support password protection #51

Merged
merged 5 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/Http/Middleware/PasswordProtected.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace Rareloop\Lumberjack\Http\Middleware;

use Timber\Timber;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Rareloop\Lumberjack\Exceptions\TwigTemplateNotFoundException;
use Rareloop\Lumberjack\Http\Responses\TimberResponse;
use Rareloop\Lumberjack\Post;

class PasswordProtected implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$passwordRequestResponse = $this->handlePasswordProtected();

if ($passwordRequestResponse) {
return $passwordRequestResponse;
}

return $handler->handle($request);
}

protected function handlePasswordProtected(): ?ResponseInterface
{
if (!post_password_required()) {
return null;
}

$context = Timber::context();
$context['post'] = new Post();

$template = apply_filters('lumberjack/password_protect_template', 'single-password.twig');

try {
return new TimberResponse($template, $context);
} catch (TwigTemplateNotFoundException $e) {
return null;
}
}
}
15 changes: 10 additions & 5 deletions src/Providers/WordPressControllersServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use mindplay\middleman\Dispatcher;
use Rareloop\Router\ResponseFactory;
use Psr\Http\Message\RequestInterface;
use Rareloop\Lumberjack\Http\Middleware\PasswordProtected;
use Zend\Diactoros\ServerRequestFactory;
use Rareloop\Router\ProvidesControllerMiddleware;

Expand Down Expand Up @@ -82,11 +83,15 @@ public function handleRequest(RequestInterface $request, $controllerName, $metho
})->all();
}

$middlewares[] = function ($request) use ($controller, $methodName) {
$invoker = new Invoker($this->app);
$output = $invoker->setRequest($request)->call([$controller, $methodName]);
return ResponseFactory::create($request, $output);
};
$middlewares = [
$this->app->get(PasswordProtected::class),
...$middlewares,
function ($request) use ($controller, $methodName) {
$invoker = new Invoker($this->app);
$output = $invoker->setRequest($request)->call([$controller, $methodName]);
return ResponseFactory::create($request, $output);
}
];

$dispatcher = $this->createDispatcher($middlewares);
return $dispatcher->dispatch($request);
Expand Down
118 changes: 118 additions & 0 deletions tests/Unit/Http/Middleware/PasswordProtectTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php

namespace Rareloop\Lumberjack\Test\Http\Middleware;

use Mockery;
use Timber\Timber;
use Brain\Monkey\Functions;
use Brain\Monkey\Filters;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Rareloop\Lumberjack\Http\Middleware\PasswordProtected;
use Rareloop\Lumberjack\Http\Responses\TimberResponse;
use Rareloop\Lumberjack\Test\Unit\BrainMonkeyPHPUnitIntegration;

/**
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
*/
class PasswordProtectTest extends TestCase
{
use BrainMonkeyPHPUnitIntegration;

/** @test */
public function it_does_nothing_when_password_is_not_required()
{
$middleware = new PasswordProtected;

Functions\expect('post_password_required')
->once()
->andReturn(false);

$request = Mockery::mock(ServerRequestInterface::class);
$response = Mockery::mock(ResponseInterface::class);
$handler = Mockery::mock(RequestHandlerInterface::class);

$handler->shouldReceive('handle')->once()->with($request)->andReturn($response);

$this->assertSame($response, $middleware->process($request, $handler));
}

/** @test */
public function it_does_nothing_when_the_password_twig_file_is_not_found()
{
$middleware = new PasswordProtected;

Functions\expect('post_password_required')
->once()
->andReturn(true);

Functions\expect('get_the_ID')
->once()
->andReturn(123);

Functions\expect('get_post')
->once();

$timber = \Mockery::mock('alias:' . Timber::class);
$timber->shouldReceive('compile')
->withArgs(function ($template) {
return $template === 'single-password.twig';
})
->once()
->andReturn(false);

$timber->shouldReceive('context')
->once()
->andReturn([]);

$request = Mockery::mock(ServerRequestInterface::class);
$response = Mockery::mock(ResponseInterface::class);
$handler = Mockery::mock(RequestHandlerInterface::class);

$handler->shouldReceive('handle')->once()->with($request)->andReturn($response);

$this->assertSame($response, $middleware->process($request, $handler));
}

/** @test */
public function it_renders_the_password_template_when_needed()
{
Functions\expect('post_password_required')
->once()
->andReturn(true);

Functions\expect('get_the_ID')
->once()
->andReturn(123);

Functions\expect('get_post')
->once();

$request = Mockery::mock(ServerRequestInterface::class);
$handler = Mockery::mock(RequestHandlerInterface::class);

$timber = \Mockery::mock('alias:' . Timber::class);
$timber->shouldReceive('compile')
->withArgs(function ($template) {
return $template === 'single-password.twig';
})
->once()
->andReturn('testing123');

$timber->shouldReceive('context')
->once()
->andReturn([]);

$handler->shouldReceive('handle')->never();

$middleware = new PasswordProtected;
$response = $middleware->process($request, $handler);

$this->assertInstanceOf(TimberResponse::class, $response);
$this->assertSame('testing123', $response->getBody()->getContents());
$this->assertTrue(Filters\applied('lumberjack/password_protect_template') > 0);
}
}
58 changes: 58 additions & 0 deletions tests/Unit/Providers/WordPressControllersServiceProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Brain\Monkey\Filters;
use Brain\Monkey\Functions;
use Laminas\Diactoros\Response\HtmlResponse;
use Monolog\Logger;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\RequestInterface;
Expand All @@ -24,6 +25,7 @@
use Zend\Diactoros\Response\TextResponse;
use Zend\Diactoros\ServerRequest;
use \Mockery;
use Rareloop\Lumberjack\Http\Middleware\PasswordProtected;

class WordPressControllersServiceProviderTest extends TestCase
{
Expand Down Expand Up @@ -156,6 +158,10 @@ public function handle_request_writes_warning_to_logs_if_controller_does_not_exi
/** @test */
public function handle_request_will_mark_request_handled_in_app_if_controller_does_exist()
{
Functions\expect('post_password_required')
->once()
->andReturn(false);

$app = new Application(__DIR__ . '/../');

$provider = new WordPressControllersServiceProvider($app);
Expand All @@ -182,6 +188,10 @@ public function handle_request_will_not_mark_request_handled_in_app_if_controlle
/** @test */
public function handle_request_returns_response_when_controller_does_exist()
{
Functions\expect('post_password_required')
->once()
->andReturn(false);

$app = new Application(__DIR__ . '/../');

$provider = new WordPressControllersServiceProvider($app);
Expand All @@ -195,6 +205,10 @@ public function handle_request_returns_response_when_controller_does_exist()
/** @test */
public function handle_request_returns_response_when_controller_returns_a_responsable()
{
Functions\expect('post_password_required')
->once()
->andReturn(false);

$app = new Application(__DIR__ . '/../');

$provider = new WordPressControllersServiceProvider($app);
Expand All @@ -209,6 +223,10 @@ public function handle_request_returns_response_when_controller_returns_a_respon
/** @test */
public function handle_request_resolves_constructor_params_from_container()
{
Functions\expect('post_password_required')
->once()
->andReturn(false);

$app = new Application(__DIR__ . '/../');

$provider = new WordPressControllersServiceProvider($app);
Expand All @@ -222,6 +240,10 @@ public function handle_request_resolves_constructor_params_from_container()
/** @test */
public function handle_request_resolves_controller_method_params_from_container()
{
Functions\expect('post_password_required')
->once()
->andReturn(false);

$app = new Application(__DIR__ . '/../');

$provider = new WordPressControllersServiceProvider($app);
Expand All @@ -235,6 +257,10 @@ public function handle_request_resolves_controller_method_params_from_container(
/** @test */
public function handle_request_supports_middleware()
{
Functions\expect('post_password_required')
->once()
->andReturn(false);

$app = new Application(__DIR__ . '/../');
$controller = new TestControllerWithMiddleware;
$controller->middleware(new AddHeaderMiddleware('X-Header', 'testing123'));
Expand All @@ -249,9 +275,33 @@ public function handle_request_supports_middleware()
$this->assertSame('testing123', $response->getHeader('X-Header')[0]);
}

/** @test */
public function handle_request_adds_password_protect_middleware()
{
$mock = Mockery::mock(PasswordProtected::class);
$mock->shouldReceive('process')->once()->andReturn(new HtmlResponse('password-protected'));

$app = new Application(__DIR__ . '/../');
$app->bind(PasswordProtected::class, $mock);

$controller = new TestControllerWithMiddleware;
$app->bind(TestControllerWithMiddleware::class, $controller);

$provider = new WordPressControllersServiceProvider($app);
$provider->boot($app);

$response = $provider->handleRequest(new ServerRequest, TestControllerWithMiddleware::class, 'handle');

$this->assertSame('password-protected', $response->getBody()->getContents());
}

/** @test */
public function handle_request_supports_middleware_applied_to_a_specific_method_using_only()
{
Functions\expect('post_password_required')
->once()
->andReturn(false);

$app = new Application(__DIR__ . '/../');
$controller = new TestControllerWithMiddleware;
$controller->middleware(new AddHeaderMiddleware('X-Header', 'testing123'))->only('notHandle');
Expand All @@ -268,6 +318,10 @@ public function handle_request_supports_middleware_applied_to_a_specific_method_
/** @test */
public function handle_request_supports_middleware_applied_to_a_specific_method_using_except()
{
Functions\expect('post_password_required')
->once()
->andReturn(false);

$app = new Application(__DIR__ . '/../');
$controller = new TestControllerWithMiddleware;
$controller->middleware(new AddHeaderMiddleware('X-Header', 'testing123'))->except('handle');
Expand All @@ -284,6 +338,10 @@ public function handle_request_supports_middleware_applied_to_a_specific_method_
/** @test */
public function handle_request_supports_middleware_aliases()
{
Functions\expect('post_password_required')
->once()
->andReturn(false);

Functions\when('get_bloginfo')->alias(function ($key) {
if ($key === 'url') {
return 'http://example.com';
Expand Down
Loading