ILIAS  release_8 Revision v8.24
class.ilRbacSystem.php
Go to the documentation of this file.
1<?php
2
3declare(strict_types=1);
4
23
34{
35 private const MAX_CACHE_ENTRIES = 1000;
36
37 protected static ?ilRbacSystem $instance = null;
38
39 protected array $mem_view = [];
40
41 protected static array $user_role_cache = [];
42
43 // Cache accesses to RBAC PA
44 private static array $_paCache = [];
45
46 // Cache outcomes of calls to checkAccessOfuser
47 private static array $_checkAccessOfUserCache = [];
48
49 protected ilObjUser $user;
50 protected ilDBInterface $db;
53 protected ilTree $tree;
55 protected Factory $refinery;
56
60 protected function __construct()
61 {
62 global $DIC;
63
64 $this->user = $DIC->user();
65 $this->db = $DIC->database();
66 $this->review = $DIC->rbac()->review();
67 $this->objectDataCache = $DIC['ilObjDataCache'];
68 $this->tree = $DIC->repositoryTree();
69 $this->http = $DIC->http();
70 $this->refinery = $DIC->refinery();
71 }
72
73 public static function getInstance(): ilRbacSystem
74 {
75 if (self::$instance === null) {
76 self::$instance = new self();
77 }
78 return self::$instance;
79 }
80
84 public static function resetCaches(): void
85 {
86 self::$user_role_cache = [];
87 self::$_paCache = [];
88 self::$_checkAccessOfUserCache = [];
89 }
90
108 public function checkAccess(string $a_operations, int $a_ref_id, string $a_type = ""): bool
109 {
110 return $this->checkAccessOfUser($this->user->getId(), $a_operations, $a_ref_id, $a_type);
111 }
112
113 public function checkAccessOfUser(int $a_user_id, string $a_operations, int $a_ref_id, string $a_type = ""): bool
114 {
115 // Create the user cache key
116 $cacheKey = $a_user_id . ':' . $a_operations . ':' . $a_ref_id . ':' . $a_type;
117
118 // Create the cache if it does not yet exist
119 if (!is_array(self::$_checkAccessOfUserCache)) {
120 self::$_checkAccessOfUserCache = [];
121 }
122 // Try to return result from cache
123 if (array_key_exists($cacheKey, self::$_checkAccessOfUserCache)) {
124 return self::$_checkAccessOfUserCache[$cacheKey];
125 }
126
127 // Check For owner
128 // Owners do always have full access to their objects
129 // Excluded are some of the permissions like create, perm, learning progress.
130 // This method call return all operations that are NOT granted by the owner status
131 if (!$a_operations = $this->filterOwnerPermissions($a_user_id, $a_operations, $a_ref_id)) {
132 // Store positive outcome in cache.
133 // Note: we only cache up to 1000 results to avoid memory overflows
134 if (count(self::$_checkAccessOfUserCache) < self::MAX_CACHE_ENTRIES) {
135 self::$_checkAccessOfUserCache[$cacheKey] = true;
136 }
137 return true;
138 }
139
140 // get roles using role cache
141 $roles = $this->fetchAssignedRoles($a_user_id, $a_ref_id);
142
143 // exclude system role from rbac
144 if (in_array(SYSTEM_ROLE_ID, $roles)) {
145 // Store positive outcome in cache.
146 // Note: we only cache up to 1000 results to avoid memory overflows
147 if (count(self::$_checkAccessOfUserCache) < self::MAX_CACHE_ENTRIES) {
148 self::$_checkAccessOfUserCache[$cacheKey] = true;
149 }
150 return true;
151 }
152
153 // Create the PA cache if it does not exist yet
154 $paCacheKey = $a_user_id . ':' . $a_ref_id;
155 if (!is_array(self::$_paCache)) {
156 self::$_paCache = array();
157 }
158
159 if (array_key_exists($paCacheKey, self::$_paCache)) {
160 // Return result from PA cache
161 $ops = self::$_paCache[$paCacheKey];
162 } else {
163 // Data is not in PA cache, perform database query
164 $q = "SELECT * FROM rbac_pa " .
165 "WHERE ref_id = " . $this->db->quote($a_ref_id, 'integer');
166
167 $r = $this->db->query($q);
168
169 $ops = [];
170 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
171 if (in_array((int) $row->rol_id, $roles)) {
172 $ops = array_merge($ops, unserialize(stripslashes($row->ops_id)));
173 }
174 }
175 // Cache up to 1000 entries in the PA cache
176 if (count(self::$_paCache) < self::MAX_CACHE_ENTRIES) {
177 self::$_paCache[$paCacheKey] = $ops;
178 }
179 }
180
181 $operations = explode(",", $a_operations);
182 foreach ($operations as $operation) {
183 if ($operation == "create") {
184 if (empty($a_type)) {
185 throw new DomainException(
186 'checkAccess(): ' . "Expect a type definition for checking a 'create' permission"
187 );
188 }
189 $ops_id = ilRbacReview::_getOperationIdByName($operation . "_" . $a_type);
190 } else {
191 $ops_id = ilRbacReview::_getOperationIdByName($operation);
192 }
193 if (!in_array($ops_id, (array) $ops)) {
194 if (count(self::$_checkAccessOfUserCache) < self::MAX_CACHE_ENTRIES) {
195 self::$_checkAccessOfUserCache[$cacheKey] = false;
196 }
197 return false;
198 }
199 }
200
201 // Store positive outcome in cache.
202 // Note: we only cache up to 1000 results to avoid memory overflows
203 if (count(self::$_checkAccessOfUserCache) < self::MAX_CACHE_ENTRIES) {
204 //$ilLog->write('PERMISSION: '.$a_ref_id.' -> '.$ops_id.' granted');
205 self::$_checkAccessOfUserCache[$cacheKey] = true;
206 }
207 return true;
208 }
209
210 public function preloadRbacPaCache(array $a_ref_ids, int $a_user_id): void
211 {
212 $ref_ids = [];
213 $roles = $ops = [];
214 foreach ($a_ref_ids as $ref_id) {
215 if (!isset(self::$_paCache[$a_user_id . ":" . $ref_id])) {
216 $roles[$ref_id] = $this->fetchAssignedRoles($a_user_id, $ref_id);
217 $ops[$ref_id] = [];
218 $ref_ids[] = $ref_id;
219 }
220 }
221
222 if ($ref_ids !== []) {
223 // Data is not in PA cache, perform database query
224 $q = "SELECT * FROM rbac_pa " .
225 "WHERE " . $this->db->in("ref_id", $ref_ids, false, "integer");
226
227 $r = $this->db->query($q);
228
229 while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
230 if (in_array($row->rol_id, $roles[(int) $row->ref_id])) {
231 $ops[(int) $row->ref_id] = array_merge(
232 $ops[(int) $row->ref_id],
233 unserialize(stripslashes($row->ops_id))
234 );
235 }
236 }
237 foreach ($a_ref_ids as $ref_id) {
238 // #11313
239 if (!isset(self::$_paCache[$a_user_id . ":" . $ref_id])) {
240 self::$_paCache[$a_user_id . ":" . $ref_id] = $ops[$ref_id];
241 }
242 }
243 }
244 }
245
249 public function checkPermission(int $a_ref_id, int $a_rol_id, string $a_operation): bool
250 {
251 $ops = [];
252 $query = 'SELECT ops_id FROM rbac_operations ' .
253 'WHERE operation = ' . $this->db->quote($a_operation, 'text');
254 $res = $this->db->query($query);
255 $ops_id = 0;
256 while ($row = $this->db->fetchObject($res)) {
257 $ops_id = (int) $row->ops_id;
258 }
259
260 $query = "SELECT * FROM rbac_pa " .
261 "WHERE rol_id = " . $this->db->quote($a_rol_id, 'integer') . " " .
262 "AND ref_id = " . $this->db->quote($a_ref_id, 'integer') . " ";
263 $res = $this->db->query($query);
264
265 while ($row = $this->db->fetchObject($res)) {
266 $ops = array_merge($ops, unserialize($row->ops_id));
267 }
268 return in_array($ops_id, $ops);
269 }
270
271 protected function filterOwnerPermissions(int $a_user_id, string $a_operations, int $a_ref_id): string
272 {
273 // member view constraints
274 if (($this->mem_view['active'] ?? null) and $a_user_id == $this->user->getId()) {
275 if (in_array($a_ref_id, $this->mem_view['items'])) {
276 return $a_operations;
277 }
278 }
279
280 if ($a_user_id != $this->objectDataCache->lookupOwner($this->objectDataCache->lookupObjId($a_ref_id))) {
281 return $a_operations;
282 }
283 // Is owner
284 $new_ops = '';
285 foreach (explode(",", $a_operations) as $operation) {
286 if ($operation != 'cat_administrate_users' &&
287 $operation != 'edit_permission' &&
288 $operation != 'edit_learning_progress' &&
289 $operation != 'read_learning_progress' &&
290 !preg_match('/^create/', $operation) &&
291 $operation != 'read_outcomes'
292 ) {
293 continue;
294 }
295 if (!strlen($new_ops)) {
296 $new_ops = $operation;
297 } else {
298 $new_ops .= (',' . $operation);
299 }
300 }
301 return $new_ops;
302 }
303
308 private function fetchAssignedRoles(int $a_usr_id, int $a_ref_id): array
309 {
310 // Member view constraints
311 if (isset($this->mem_view['active']) && $this->mem_view['active'] && $a_usr_id == $this->user->getId()) {
312 // check if current ref_id is subitem of active container
313 if (in_array($a_ref_id, $this->mem_view['items']) && $this->mem_view['role']) {
314 // Return default member role
315 return [$this->mem_view['role']];
316 }
317 }
318
319 if (isset(self::$user_role_cache[$a_usr_id]) and is_array(self::$user_role_cache)) {
320 return self::$user_role_cache[$a_usr_id];
321 }
322 return self::$user_role_cache[$a_usr_id] = $this->review->assignedRoles($a_usr_id);
323 }
324
325 public function initMemberView(): void
326 {
328 $member_view_activation = null;
329 if ($this->http->wrapper()->query()->has('mv')) {
330 $member_view_activation = $this->http->wrapper()->query()->retrieve(
331 'mv',
332 $this->refinery->kindlyTo()->bool()
333 );
334 }
335 $ref_id = 0;
336 if ($this->http->wrapper()->query()->has('ref_id')) {
337 $ref_id = $this->http->wrapper()->query()->retrieve(
338 'ref_id',
339 $this->refinery->kindlyTo()->int()
340 );
341 }
342 if ($member_view_activation === true) {
343 if ($this->checkAccess('write', $ref_id)) {
344 $settings->toggleActivation($ref_id, true);
346 }
347 }
348 if ($member_view_activation === false) {
349 $settings->toggleActivation($ref_id, false);
350 }
351 if (!$settings->isActive()) {
352 $this->mem_view['active'] = false;
353 $this->mem_view['items'] = [];
354 $this->mem_view['role'] = 0;
355 } else {
356 $this->mem_view['active'] = true;
357 $this->mem_view['items'] = $this->tree->getSubTreeIds($settings->getContainer());
358 $this->mem_view['items'] = array_merge($this->mem_view['items'], array($settings->getContainer()));
359 $this->mem_view['role'] = ilParticipants::getDefaultMemberRole($settings->getContainer());
360 }
361 }
362
363 public function addTemporaryRole(int $a_usr_id, int $a_role_id): void
364 {
365 if (!in_array($a_role_id, self::$user_role_cache[$a_usr_id])) {
366 self::$user_role_cache[$a_usr_id][] = $a_role_id;
367 }
368 }
369
370 public function resetPACache(int $a_usr_id, int $a_ref_id): void
371 {
372 $paCacheKey = $a_usr_id . ':' . $a_ref_id;
373 unset(self::$_paCache[$paCacheKey]);
374 }
375} // END class.RbacSystem
Builds data types.
Definition: Factory.php:21
User class.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getDefaultMemberRole(int $a_ref_id)
class ilRbacReview Contains Review functions of core Rbac.
static _getOperationIdByName(string $a_operation)
get operation id by name of operation
class ilRbacSystem system function like checkAccess, addActiveRole ... Supporting system functions ar...
ilDBInterface $db
addTemporaryRole(int $a_usr_id, int $a_role_id)
checkAccess(string $a_operations, int $a_ref_id, string $a_type="")
checkAccess represents the main method of the RBAC-system in ILIAS3 developers want to use With this ...
fetchAssignedRoles(int $a_usr_id, int $a_ref_id)
Fetch assigned roles This method caches the assigned roles per user.
static resetCaches()
Reset internal caches.
static array $user_role_cache
__construct()
Constructor.
checkAccessOfUser(int $a_user_id, string $a_operations, int $a_ref_id, string $a_type="")
preloadRbacPaCache(array $a_ref_ids, int $a_user_id)
static array $_checkAccessOfUserCache
resetPACache(int $a_usr_id, int $a_ref_id)
ilObjectDataCache $objectDataCache
static array $_paCache
static ilRbacSystem $instance
checkPermission(int $a_ref_id, int $a_rol_id, string $a_operation)
check if a specific role has the permission '$a_operation' of an object
ilRbacReview $review
GlobalHttpState $http
filterOwnerPermissions(int $a_user_id, string $a_operations, int $a_ref_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
const SYSTEM_ROLE_ID
Definition: constants.php:29
global $DIC
Definition: feed.php:28
Interface GlobalHttpState.
Interface ilDBInterface.
$ref_id
Definition: ltiauth.php:67
$res
Definition: ltiservices.php:69
static http()
Fetches the global http state from ILIAS.
array $settings
Setting values (LTI parameters, custom parameters and local parameters).
Definition: System.php:200
$query