Skip to content

Commit 3ef7b8a

Browse files
committed
AdmissionLoader: if global.store_roles_in_db, load roles from database
1 parent 67d9a8b commit 3ef7b8a

File tree

1 file changed

+164
-4
lines changed

1 file changed

+164
-4
lines changed

library/Icinga/Authentication/AdmissionLoader.php

Lines changed: 164 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,29 @@
33

44
namespace Icinga\Authentication;
55

6+
use Exception;
67
use Generator;
78
use Icinga\Application\Config;
89
use Icinga\Application\Logger;
10+
use Icinga\Common\Database;
911
use Icinga\Exception\ConfigurationError;
10-
use Icinga\Exception\NotReadableError;
1112
use Icinga\Data\ConfigObject;
13+
use Icinga\Model\Role as RoleModel;
14+
use Icinga\Model\RolePermission;
15+
use Icinga\Model\RoleRestriction;
1216
use Icinga\User;
1317
use Icinga\Util\StringHelper;
18+
use ipl\Sql\Connection;
19+
use ipl\Sql\Select;
20+
use ipl\Stdlib\Filter;
1421

1522
/**
1623
* Retrieve restrictions and permissions for users
1724
*/
1825
class AdmissionLoader
1926
{
27+
use Database;
28+
2029
const LEGACY_PERMISSIONS = [
2130
'admin' => 'application/announcements',
2231
'application/stacktraces' => 'user/application/stacktraces',
@@ -52,12 +61,27 @@ class AdmissionLoader
5261
/** @var ConfigObject */
5362
protected $roleConfig;
5463

64+
/**
65+
* Database where the roles are stored
66+
*
67+
* @var ?Connection
68+
*/
69+
protected $rolesDb = null;
70+
5571
public function __construct()
5672
{
5773
try {
58-
$this->roleConfig = Config::app('roles');
59-
} catch (NotReadableError $e) {
60-
Logger::error('Can\'t access roles configuration. An exception was thrown:', $e);
74+
if (Config::app()->get('global', 'store_roles_in_db')) {
75+
$db = $this->getDb();
76+
77+
RoleModel::on($db)->limit(1)->columns('id')->first();
78+
79+
$this->rolesDb = $db;
80+
} else {
81+
$this->roleConfig = Config::app('roles');
82+
}
83+
} catch (Exception $e) {
84+
Logger::error('Can\'t access roles storage. An exception was thrown:', $e);
6185
}
6286
}
6387

@@ -170,6 +194,10 @@ protected function loadRole($name, ConfigObject $section)
170194
*/
171195
public function applyRoles(User $user)
172196
{
197+
if ($this->rolesDb !== null) {
198+
$this->applyDbRoles($user);
199+
}
200+
173201
if ($this->roleConfig === null) {
174202
return;
175203
}
@@ -229,6 +257,138 @@ public function applyRoles(User $user)
229257
$user->setRoles(array_values($roles));
230258
}
231259

260+
/**
261+
* Apply permissions, restrictions and roles from the database to the given user
262+
*
263+
* @param User $user
264+
*/
265+
private function applyDbRoles(User $user): void
266+
{
267+
$direct = (new Select())
268+
->from('icingaweb_role')
269+
->where([
270+
'id IN ?' => (new Select())
271+
->from('icingaweb_role_user')
272+
->where(['user_name IN (?)' => [$user->getUsername(), '*']])
273+
->columns('role_id')
274+
])
275+
->columns(['id', 'parent_id', 'name', 'unrestricted', 'direct' => '1']);
276+
277+
$userGroups = $user->getGroups();
278+
$roleData = [];
279+
$roles = [];
280+
$assignedRoles = [];
281+
$unrestricted = false;
282+
283+
if ($userGroups) {
284+
$userGroups = array_values($userGroups);
285+
286+
$direct->orWhere([
287+
'id IN ?' => (new Select())
288+
->from('icingaweb_role_group')
289+
->where(['group_name IN (?)' => $userGroups])
290+
->columns('role_id')
291+
]);
292+
}
293+
294+
// Not a UNION ALL to handle circular relationships.
295+
// Due to the "direct" column such may still appear twice.
296+
// Hence ORDER BY direct, so that the last one (direct=1) wins.
297+
$query = (new Select())
298+
->with(
299+
$direct->union(
300+
(new Select())
301+
->from(['r' => 'icingaweb_role'])
302+
->join('rl', 'rl.parent_id = r.id')
303+
->columns(['r.id', 'r.parent_id', 'r.name', 'r.unrestricted', 'direct' => '0'])
304+
),
305+
'rl',
306+
true
307+
)
308+
->from('rl')
309+
->orderBy('direct')
310+
->columns(['id', 'parent_id', 'name', 'unrestricted', 'direct']);
311+
312+
foreach ($this->rolesDb->select($query) as $row) {
313+
$roleData[$row->id] = $row;
314+
}
315+
316+
foreach ($roleData as $row) {
317+
$roles[$row->id] = (new Role())
318+
->setName($row->name)
319+
->setIsUnrestricted($row->unrestricted);
320+
321+
if ($row->direct) {
322+
$assignedRoles[] = $row->name;
323+
}
324+
325+
if ($row->unrestricted) {
326+
$unrestricted = true;
327+
}
328+
}
329+
330+
foreach ($roleData as $row) {
331+
if ($row->parent_id) {
332+
$parent = $roles[$row->parent_id];
333+
$child = $roles[$row->id];
334+
335+
$child->setParent($parent);
336+
$parent->addChild($child);
337+
}
338+
}
339+
340+
$filter = Filter::equal('role_id', array_keys($roles));
341+
$permissions = [];
342+
$allPermissions = [];
343+
$refusals = [];
344+
$restrictions = [];
345+
$allRestrictions = [];
346+
347+
foreach (RolePermission::on($this->rolesDb)->filter($filter) as $row) {
348+
if ($row->allowed) {
349+
$permissions[$row->role_id][] = $row->permission;
350+
}
351+
352+
if ($row->denied) {
353+
$refusals[$row->role_id][] = $row->permission;
354+
}
355+
}
356+
357+
foreach ($permissions as $roleId => & $rolePermissions) {
358+
list($rolePermissions, $newRefusals) = $this->migrateLegacyPermissions($rolePermissions);
359+
360+
if ($newRefusals) {
361+
array_push($refusals[$roleId], ...$newRefusals);
362+
}
363+
364+
$roles[$roleId]->setPermissions($rolePermissions);
365+
array_push($allPermissions, ...$rolePermissions);
366+
}
367+
368+
foreach ($refusals as $roleId => $roleRefusals) {
369+
$roles[$roleId]->setRefusals($roleRefusals);
370+
}
371+
372+
foreach (RoleRestriction::on($this->rolesDb)->filter($filter) as $row) {
373+
$restrictions[$row->role_id][$row->restriction] = $row->filter;
374+
}
375+
376+
foreach ($restrictions as $roleId => & $roleRestrictions) {
377+
foreach ($roleRestrictions as $name => & $restriction) {
378+
$restriction = str_replace('$user.local_name$', $user->getLocalUsername(), $restriction);
379+
$allRestrictions[$name][] = $restriction;
380+
}
381+
382+
$roles[$roleId]->setRestrictions($roleRestrictions);
383+
}
384+
385+
$user->setAdditional('assigned_roles', $assignedRoles);
386+
$user->setIsUnrestricted($unrestricted);
387+
$user->setRestrictions($unrestricted ? [] : $allRestrictions);
388+
$user->setPermissions(array_values(array_unique($allPermissions)));
389+
$user->setRoles(array_values($roles));
390+
}
391+
232392
public static function migrateLegacyPermissions(array $permissions)
233393
{
234394
$migratedGrants = [];

0 commit comments

Comments
 (0)