Skip to content
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
45 changes: 25 additions & 20 deletions src/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,35 +59,19 @@ public function __construct()
});

$this->middleware('is', function ($role) {
Pages::error(
'Page not found',
'The page you are looking for could not be found.',
403
);
static::denyAccess();
});

$this->middleware('isNot', function () {
Pages::error(
'Page not found',
'The page you are looking for could not be found.',
403
);
static::denyAccess();
});

$this->middleware('can', function () {
Pages::error(
'Page not found',
'The page you are looking for could not be found.',
403
);
static::denyAccess();
});

$this->middleware('cannot', function () {
Pages::error(
'Page not found',
'The page you are looking for could not be found.',
403
);
static::denyAccess();
});

$this->middleware('auth.verified', function () {
Expand Down Expand Up @@ -118,6 +102,27 @@ public function __construct()
}
}

/**
* Stop a request that failed a role or permission middleware.
* Leaf 5 (leafs/exception ^5) renders its Pages error view; Leaf 4
* has no Leaf\Crash\Pages, so fall back to a plain 403 response.
* @return void
*/
protected static function denyAccess()
{
if (class_exists(Pages::class)) {
Pages::error(
'Page not found',
'The page you are looking for could not be found.',
403
);

return;
}

response()->exit('The page you are looking for could not be found.', 403);
}

/**
* Read an environment value, with or without leaf core around.
* Reads live rather than through _env()'s per-process cache: these
Expand Down
42 changes: 42 additions & 0 deletions tests/deny.test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

function runDenyAccess(string $mode = ''): array
{
$cmd = implode(' ', array_filter([
escapeshellarg(PHP_BINARY),
escapeshellarg(__DIR__ . '/fixtures/deny-access.php'),
escapeshellarg(dirname(__DIR__) . '/vendor/autoload.php'),
$mode ? escapeshellarg($mode) : null,
]));

$proc = proc_open($cmd, [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes);
$out = stream_get_contents($pipes[1]);
$err = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($proc);

return [$out, $err];
}

test('role middleware denial renders the Crash error page when leafs/exception v5 is present', function () {
if (!class_exists(\Leaf\Crash\Pages::class)) {
$this->markTestSkipped('leafs/exception v5 not installed');
}

[$out, $err] = runDenyAccess();

expect($out)->toContain('Page not found');
expect($out)->toContain('<html');
expect($out)->not->toContain('NOT-STOPPED');
expect($err)->toContain('STATUS=403');
});

test('role middleware denial falls back to a plain 403 when Crash\Pages is unavailable (Leaf 4)', function () {
[$out, $err] = runDenyAccess('no-pages');

expect($out)->toContain('The page you are looking for could not be found.');
expect($out)->not->toContain('<html');
expect($out)->not->toContain('NOT-STOPPED');
expect($err)->toContain('STATUS=403');
});
40 changes: 40 additions & 0 deletions tests/fixtures/deny-access.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

// Runs Auth::denyAccess() in its own process, since both branches exit().
// Usage: php deny-access.php <vendor/autoload.php> [no-pages]
// "no-pages" hides Leaf\Crash\* from the autoloader to mimic a Leaf 4 app,
// where leafs/exception <5 has no Crash\Pages class.

require $argv[1];

if (($argv[2] ?? '') === 'no-pages') {
$loaders = spl_autoload_functions();

foreach ($loaders as $loader) {
spl_autoload_unregister($loader);
}

spl_autoload_register(function ($class) use ($loaders) {
if (strpos($class, 'Leaf\\Crash\\') === 0) {
return;
}

foreach ($loaders as $loader) {
$loader($class);

if (class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false)) {
return;
}
}
});
}

register_shutdown_function(function () {
fwrite(STDERR, 'STATUS=' . var_export(http_response_code(), true));
});

$deny = new ReflectionMethod(Leaf\Auth::class, 'denyAccess');
$deny->setAccessible(true);
$deny->invoke(null);

echo 'NOT-STOPPED';
Loading