Skip to content

Commit 5edbd00

Browse files
committed
Added PermissionsPanel for DebugKit plugin
1 parent fc79d5b commit 5edbd00

File tree

5 files changed

+245
-3
lines changed

5 files changed

+245
-3
lines changed

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@
6767
"@stan",
6868
"@psalm"
6969
],
70-
"cs-check": "phpcs -n -p --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests",
71-
"cs-fix": "phpcbf --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests",
70+
"cs-check": "phpcs -n -p --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests ./templates",
71+
"cs-fix": "phpcbf --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests ./templates",
7272
"test": "phpunit --stderr",
7373
"stan": "phpstan analyse src/",
7474
"psalm": "php vendor/psalm/phar/psalm.phar --show-info=false src/ ",

src/Panel/PermissionsPanel.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace CakeDC\Auth\Panel;
5+
6+
use Cake\Event\EventInterface;
7+
use CakeDC\Auth\Policy\Result\RbacResult;
8+
use CakeDC\Auth\Policy\Result\SuperuserResult;
9+
use DebugKit\DebugPanel;
10+
11+
class PermissionsPanel extends DebugPanel
12+
{
13+
/**
14+
* @inheritDoc
15+
*/
16+
public string $plugin = 'CakeDC/Auth';
17+
18+
/**
19+
* @inheritDoc
20+
*/
21+
public function initialize(): void
22+
{
23+
$this->_data = ['results' => []];
24+
}
25+
26+
/**
27+
* Get the events this panels supports.
28+
*
29+
* @return array<string, mixed>
30+
*/
31+
public function implementedEvents(): array
32+
{
33+
return [
34+
'Controller.shutdown' => 'shutdown',
35+
'CakeDC/Auth.DebugKit.Permission.afterResult' => 'afterResult',
36+
];
37+
}
38+
39+
/**
40+
* @param \Cake\Event\EventInterface $event
41+
* @return void
42+
*/
43+
public function afterResult(EventInterface $event): void
44+
{
45+
$subject = $event->getSubject();
46+
if ($subject instanceof RbacResult) {
47+
$this->parseRbacResult($subject);
48+
49+
return;
50+
}
51+
if ($subject instanceof SuperuserResult) {
52+
$this->parseSuperuserResult($subject);
53+
}
54+
}
55+
56+
/**
57+
* @param \CakeDC\Auth\Policy\Result\RbacResult $subject
58+
* @return void
59+
*/
60+
protected function parseRbacResult(RbacResult $subject): void
61+
{
62+
$this->_data['results'][] = [
63+
'status' => $subject->getStatus(),
64+
'reason' => $subject->getReason(),
65+
'logReason' => $subject->getPermissionMatchResult()->getLogReason(),
66+
'resource' => $subject->getPermissionMatchResult()->getResource(),
67+
'permission' => $subject->getPermissionMatchResult()->getPermission(),
68+
'type' => 'Rbac',
69+
];
70+
}
71+
72+
/**
73+
* @param \CakeDC\Auth\Policy\Result\SuperuserResult $subject
74+
* @return void
75+
*/
76+
protected function parseSuperuserResult(SuperuserResult $subject): void
77+
{
78+
$this->_data['results'][] = [
79+
'status' => $subject->getStatus(),
80+
'reason' => $subject->getReason(),
81+
'resource' => $subject->getResource(),
82+
'permission' => null,
83+
'type' => 'Superuser',
84+
];
85+
}
86+
}

src/Rbac/Rbac.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ protected function addExtraResourceKeys(ServerRequestInterface $request, array $
292292
'query' => $request->getQueryParams(),
293293
'pass' => $request->getAttribute('params')['pass'] ?? [],
294294
];
295+
295296
return $reserved;
296297
}
297298
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<?php
2+
/**
3+
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4+
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5+
*
6+
* Licensed under The MIT License
7+
* Redistributions of files must retain the above copyright notice.
8+
*
9+
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
10+
* @link https://cakephp.org CakePHP(tm) Project
11+
* @since DebugKit 0.1
12+
* @license https://www.opensource.org/licenses/mit-license.php MIT License
13+
*/
14+
/**
15+
* @var \DebugKit\View\AjaxView $this
16+
* @var array $results
17+
*/
18+
19+
use Cake\Core\Configure;
20+
use Cake\Error\Debugger;
21+
use function Cake\Core\h;
22+
$resourceText = function ($resource) {
23+
if (!$resource) {
24+
return '';
25+
}
26+
try {
27+
$url = [
28+
...$resource,
29+
...$resource['pass'] ?? [],
30+
'?' => $resource['query'] ?? [],
31+
];
32+
$url['_ext'] = $url['extension'];
33+
unset($url['pass'], $url['role'], $url['query']);
34+
$url = $this->Url->build($url);
35+
$html = sprintf('<p style="margin-top: 5px;">Url: <a href="%s" target="_blank">%s</a></p>', $url, $url);
36+
} catch (Throwable) {
37+
$html = '<p style="margin-top: 5px;">Can\' create url</p>';
38+
}
39+
40+
$html = $this->Toolbar->dumpNode(Debugger::exportVarAsNodes($resource)) . $html;
41+
42+
return $html;
43+
}
44+
?>
45+
<style>
46+
.permission-panel .permission-failed {
47+
background: #ffead5;
48+
}
49+
.permission-panel .is-active {
50+
background-color: var(--routes-btn-active-bg);
51+
color: var(--routes-btn-active-text);
52+
border-color: var(--routes-btn-active-border);
53+
box-shadow: 0 2px 0 var(--routes-btn-active-border);
54+
}
55+
</style>
56+
<div class="permission-panel">
57+
<?php
58+
$msg = 'This table shows all permissions results from CollectionPolicy (CakeDC/Auth plugin)';
59+
printf('<p class="c-flash c-flash--info">%s</p>', $msg);
60+
?>
61+
<?php if (!Configure::read('CakeDC/Auth.DebugKit.PermissionPanel.enabled')) :?>
62+
<p class="c-flash c-flash--info">Permissions are not being collected, please update the config:
63+
<br /><br />
64+
<code>Configure::write('CakeDC/Auth.DebugKit.PermissionPanel.enabled', true);</code>
65+
</p>
66+
<?php endif;?>
67+
<section>
68+
<button type="button" data-name="all" class="permission-btn-filter o-button is-active" onclick="PermissionPanel.displayAll()">
69+
All
70+
</button>
71+
<button type="button" data-name="only-allowed" class="permission-btn-filter o-button" onclick="PermissionPanel.displayOnlyAllowed()">
72+
Only Allowed
73+
</button>
74+
<button type="button" data-name="only-not-allowed" class="permission-btn-filter o-button" onclick="PermissionPanel.displayOnlyNotAllowed()">
75+
Only Not Allowed
76+
</button>
77+
<h4>Total shown: <span id="totalShownNumber"><?= count($results)?></span></h4>
78+
<table>
79+
<thead>
80+
<tr>
81+
<th>Type</th>
82+
<th>Allowed</th>
83+
<th>Resource</th>
84+
<th>Permission</th>
85+
<th>Reason</th>
86+
</tr>
87+
</thead>
88+
<tbody id="permissionResultsData">
89+
<?php foreach ($results as $resultItem) : ?>
90+
<tr data-allowed="<?= (int)$resultItem['status']?>" class="<?= $resultItem['status'] ? '' : 'permission-failed'?>">
91+
<td><?= h($resultItem['type']) ?></td>
92+
<td><?= $resultItem['status'] ? 'Yes' : 'No' ?></td>
93+
<td><?= $resourceText($resultItem['resource']) ?></td>
94+
<td><?= $this->Toolbar->dumpNode(Debugger::exportVarAsNodes($resultItem['permission'])) ?></td>
95+
<td>
96+
<?php if (!$resultItem['status']) :?>
97+
<p><?= h($resultItem['reason']) ?></p>
98+
<?php endif;?>
99+
<?php if (!$resultItem['status'] && isset($resultItem['logReason'])) :?>
100+
Log Reason: <?= h($resultItem['logReason']) ?>
101+
<?php endif;?>
102+
</td>
103+
</tr>
104+
<?php endforeach; ?>
105+
</tbody>
106+
</table>
107+
</section>
108+
</div>
109+
<script>
110+
var PermissionPanel = (function() {
111+
function updateSelectedFilter (selectedName) {
112+
document.querySelectorAll(".permission-panel .permission-btn-filter").forEach(function(item) {
113+
item.classList.remove('is-active');
114+
if (item.dataset.name === selectedName) {
115+
item.classList.add('is-active');
116+
}
117+
});
118+
}
119+
function displayHideItems (check) {
120+
var totalShown = 0;
121+
document.querySelectorAll("#permissionResultsData > tr").forEach(function (item) {
122+
if (typeof item.dataset === 'undefined') {
123+
return;
124+
}
125+
let allowed = item.dataset.allowed || '0';
126+
let show = check(allowed);
127+
if (show) {
128+
totalShown++;
129+
}
130+
item.style.display = show ? 'table-row' : 'none';
131+
});
132+
document.getElementById('totalShownNumber').innerText = '' + totalShown;
133+
}
134+
return {
135+
displayAll: function() {
136+
displayHideItems(function() {
137+
return true;
138+
})
139+
updateSelectedFilter('all');
140+
},
141+
displayOnlyAllowed() {
142+
displayHideItems(function(allowed) {
143+
return allowed === '1';
144+
})
145+
updateSelectedFilter('only-allowed');
146+
},
147+
displayOnlyNotAllowed() {
148+
displayHideItems(function(allowed) {
149+
return allowed === '0';
150+
})
151+
updateSelectedFilter('only-not-allowed');
152+
}
153+
};
154+
})();
155+
</script>

tests/TestCase/Rbac/RbacTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1210,7 +1210,7 @@ public function testBadPermission($permissions, $user, $requestParams, $expected
12101210
$rbac
12111211
->expects($this->once())
12121212
->method('log')
1213-
->with($this->callback(function($message) use ($expectedMsg) {
1213+
->with($this->callback(function ($message) use ($expectedMsg) {
12141214
$this->assertStringStartsWith($expectedMsg, $message);
12151215

12161216
return true;

0 commit comments

Comments
 (0)