3 * FusionForge RBAC engine
5 * Copyright 2010, Roland Mas
7 * This file is part of FusionForge. FusionForge is free software;
8 * you can redistribute it and/or modify it under the terms of the
9 * GNU General Public License as published by the Free Software
10 * Foundation; either version 2 of the Licence, or (at your option)
13 * FusionForge is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 require_once $gfcommon.'include/RBAC.php' ;
26 * TODO: Enter description here ...
29 class RBACEngine extends Error implements PFO_RBACEngine {
30 private static $_instance ;
31 private $_cached_roles = array () ;
32 private $_cached_available_roles = NULL ;
33 private $_cached_global_roles = NULL ;
34 private $_cached_public_roles = NULL ;
36 // singleton constructor
37 public static function getInstance() {
38 if (!isset(self::$_instance)) {
40 self::$_instance = new $c;
43 return self::$_instance;
47 * @see PFO_RBACEngine::getAvailableRoles()
49 public function getAvailableRoles() {
50 if ($this->_cached_available_roles != NULL) {
51 return $this->_cached_available_roles ;
54 $this->_cached_available_roles = array () ;
56 $this->_cached_available_roles[] = RoleAnonymous::getInstance() ;
58 if (session_loggedin()) {
59 $this->_cached_available_roles[] = RoleLoggedIn::getInstance() ;
60 $user = session_get_user() ;
63 $res = db_query_params ('SELECT role_id FROM pfo_user_role WHERE user_id=$1',
64 array ($user->getID()));
65 while ($arr = db_fetch_array($res)) {
66 $this->_cached_available_roles[] = $this->getRoleById ($arr['role_id']) ;
69 $groups = $user->getGroups() ;
70 foreach ($groups as $g) {
71 $this->_cached_available_roles[] = $user->getRole($g) ;
77 $params['current_roles'] = $this->_cached_available_roles;
78 $params['new_roles'] = array();
79 plugin_hook_by_reference('get_extra_roles', $params);
80 foreach ($params['new_roles'] as $r) {
81 $this->addAvailableRole($r);
85 $params['current_roles'] = $this->_cached_available_roles;
86 $params['dropped_roles'] = array();
87 plugin_hook_by_reference('restrict_roles', $params);
88 foreach ($params['dropped_roles'] as $r) {
89 $this->dropAvailableRole($r);
92 return $this->_cached_available_roles ;
95 private function addAvailableRole($role) {
97 foreach ($this->_cached_available_roles as $r) {
98 if ($r->getID() == $role->getID()) {
103 $this->_cached_available_roles[] = $role;
107 private function dropAvailableRole($role) {
108 $new_roles = array();
109 foreach ($this->_cached_available_roles as $r) {
110 if ($r->getID() != $role->getID()) {
114 $this->_cached_available_roles = $new_roles;
117 public function getGlobalRoles() {
118 if ($this->_cached_global_roles != NULL) {
119 return $this->_cached_global_roles ;
122 $this->_cached_global_roles = array () ;
125 $res = db_query_params ('SELECT role_id FROM pfo_role WHERE home_group_id IS NULL',
127 while ($arr = db_fetch_array($res)) {
128 $this->_cached_global_roles[] = $this->getRoleById ($arr['role_id']) ;
132 return $this->_cached_global_roles ;
135 public function getPublicRoles() {
136 if ($this->_cached_public_roles != NULL) {
137 return $this->_cached_public_roles ;
140 $this->_cached_public_roles = array () ;
143 $res = db_query_params ('SELECT role_id FROM pfo_role WHERE is_public=$1',
145 while ($arr = db_fetch_array($res)) {
146 $this->_cached_public_roles[] = $this->getRoleById ($arr['role_id']) ;
150 return $this->_cached_public_roles ;
153 public function invalidateRoleCaches () {
154 $this->_cached_available_roles = NULL ;
155 $this->_cached_global_roles = NULL ;
156 $this->_cached_public_roles = NULL ;
159 public function getAvailableRolesForUser($user) {
162 $result[] = RoleAnonymous::getInstance() ;
163 $result[] = RoleLoggedIn::getInstance() ;
166 $res = db_query_params ('SELECT role_id FROM pfo_user_role WHERE user_id=$1',
167 array ($user->getID()));
168 while ($arr = db_fetch_array($res)) {
169 $result[] = $this->getRoleById ($arr['role_id']) ;
172 $res = db_query_params ('SELECT role_id FROM user_group WHERE user_id=$1',
173 array ($user->getID()));
174 while ($arr = db_fetch_array($res)) {
175 $result[] = $this->getRoleById ($arr['role_id']) ;
183 * @see PFO_RBACEngine::isActionAllowed()
185 public function isActionAllowed ($section, $reference, $action = NULL) {
186 $rlist = $this->getAvailableRoles () ;
187 foreach ($rlist as $r) {
188 if ($r->hasPermission ($section, $reference, $action)) {
195 public function isGlobalActionAllowed ($section, $action = NULL) {
196 return $this->isActionAllowed ($section, -1, $action) ;
199 public function isActionAllowedForUser ($user, $section, $reference, $action = NULL) {
200 $rlist = $this->getAvailableRolesForUser ($user) ;
201 foreach ($rlist as $r) {
202 if ($r->hasPermission ($section, $reference, $action)) {
209 public function isGlobalActionAllowedForUser ($user, $section, $action = NULL) {
210 return $this->isActionAllowedForUser ($user, $section, -1, $action) ;
213 public function getRoleById ($role_id) {
214 if (array_key_exists ($role_id, $this->_cached_roles)) {
215 return $this->_cached_roles[$role_id] ;
218 $res = db_query_params ('SELECT c.class_name, r.home_group_id FROM pfo_role r, pfo_role_class c WHERE r.role_class = c.class_id AND r.role_id = $1',
220 if (!$res || !db_numrows($res)) {
224 $class_id = db_result ($res, 0, 'class_name') ;
226 case 'PFO_RoleExplicit':
227 $group_id = db_result ($res, 0, 'home_group_id') ;
228 $group = group_get_object ($group_id) ;
229 $this->_cached_roles[$role_id] = new Role ($group, $role_id) ;
230 return $this->_cached_roles[$role_id] ;
231 case 'PFO_RoleAnonymous':
232 $this->_cached_roles[$role_id] = RoleAnonymous::getInstance() ;
233 return $this->_cached_roles[$role_id] ;
234 case 'PFO_RoleLoggedIn':
235 $this->_cached_roles[$role_id] = RoleLoggedIn::getInstance() ;
236 return $this->_cached_roles[$role_id] ;
238 throw new Exception ("Not implemented") ;
241 $res = db_query_params ('SELECT group_id FROM role r WHERE role_id = $1',
243 if (!$res || !db_numrows($res)) {
246 $group_id = db_result ($res, 0, 'group_id') ;
247 $group = group_get_object ($group_id) ;
248 return new Role ($group, $role_id) ;
252 public function getRolesByAllowedAction ($section, $reference, $action = NULL) {
253 $ids = $this->_getRolesIdByAllowedAction ($section, $reference, $action) ;
255 foreach ($ids as $role_id) {
256 $roles[] = $this->getRoleById ($role_id) ;
262 public function getUsersByAllowedAction ($section, $reference, $action = NULL) {
263 $roles = $this->getRolesByAllowedAction ($section, $reference, $action) ;
264 $user_ids = array () ;
265 foreach ($roles as $role) {
266 foreach ($role->getUsers() as $user) {
267 $user_ids[] = $user->getID() ;
271 $user_ids = array_unique ($user_ids) ;
273 return user_get_objects ($user_ids) ;
276 private function _getRolesIdByAllowedAction ($section, $reference, $action = NULL) {
278 $qpa = db_construct_qpa () ;
279 $qpa = db_construct_qpa ($qpa,
280 'SELECT role_id FROM pfo_role_setting WHERE section_name=$1 AND ref_id=$2 ',
284 // Look for roles that are directly allowed to perform action
289 case 'approve_projects':
291 case 'project_admin':
293 case 'tracker_admin':
296 $qpa = db_construct_qpa ($qpa, 'AND perm_val = 1') ;
301 $qpa = db_construct_qpa ($qpa, 'AND perm_val != 0') ;
304 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 1') ;
307 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 2') ;
314 $qpa = db_construct_qpa ($qpa, 'AND perm_val != 0') ;
317 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 1') ;
320 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 2') ;
327 $qpa = db_construct_qpa ($qpa, 'AND perm_val != 0') ;
330 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 1') ;
333 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 2') ;
336 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 3') ;
339 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 4') ;
346 $qpa = db_construct_qpa ($qpa, 'AND perm_val != 0') ;
349 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 1') ;
352 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 2') ;
355 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 3') ;
362 $qpa = db_construct_qpa ($qpa, 'AND perm_val != 0') ;
365 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 1') ;
368 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 2') ;
370 case 'unmoderated_post':
371 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 3') ;
374 $qpa = db_construct_qpa ($qpa, 'AND perm_val >= 4') ;
382 $qpa = db_construct_qpa ($qpa, 'AND perm_val != 0') ;
385 $qpa = db_construct_qpa ($qpa, 'AND (perm_val & 1) = 1') ;
388 $qpa = db_construct_qpa ($qpa, 'AND (perm_val & 2) = 2') ;
391 $qpa = db_construct_qpa ($qpa, 'AND (perm_val & 4) = 4') ;
396 $hook_params = array ();
397 $hook_params['section'] = $section ;
398 $hook_params['reference'] = $reference ;
399 $hook_params['action'] = $action ;
400 $hook_params['qpa'] = $qpa ;
401 $hook_params['result'] = $result ;
402 plugin_hook_by_reference ("list_roles_by_permission", $hook_params);
403 $qpa = $hook_params['qpa'] ;
407 $res = db_query_qpa ($qpa) ;
409 $this->setError('RBACEngine::getRolesByAllowedAction()::'.db_error());
412 while ($arr = db_fetch_array($res)) {
413 $result[] = $arr['role_id'] ;
416 // Also look for roles that can perform the action because they're more powerful
420 case 'approve_projects':
423 case 'project_admin':
424 $result = array_merge ($result, $this->_getRolesIdByAllowedAction ('forge_admin', -1)) ;
427 case 'tracker_admin':
433 $result = array_merge ($result, $this->_getRolesIdByAllowedAction ('project_admin', $reference)) ;
436 if ($action != 'tech') {
437 $t = artifactType_get_object ($reference) ;
438 $result = array_merge ($result, $this->_getRolesIdByAllowedAction ('tracker_admin', $t->Group->getID())) ;
442 if ($action != 'tech') {
443 $t = projectgroup_get_object ($reference) ;
444 $result = array_merge ($result, $this->_getRolesIdByAllowedAction ('pm_admin', $t->Group->getID())) ;
448 $t = forum_get_object ($reference) ;
449 $result = array_merge ($result, $this->_getRolesIdByAllowedAction ('forum_admin', $t->Group->getID())) ;
452 if ($action != 'tech') {
453 $result = array_merge ($result, $this->_getRolesIdByAllowedAction ('tracker_admin', $reference)) ;
457 if ($action != 'tech') {
458 $result = array_merge ($result, $this->_getRolesIdByAllowedAction ('pm_admin', $reference)) ;
462 $t = forum_get_object ($reference) ;
463 $result = array_merge ($result, $this->_getRolesIdByAllowedAction ('forum_admin', $reference)) ;
467 return array_unique ($result) ;
472 * Check if permission is allowed for an action on a reference in the context of a section
473 * @param string $section
474 * @param unknown_type $reference (group_id, ...)
475 * @param string $action
477 function forge_check_perm ($section, $reference, $action = NULL) {
478 $engine = RBACEngine::getInstance() ;
480 return $engine->isActionAllowed($section, $reference, $action) ;
484 * TODO: Enter description here ...
485 * @param unknown_type $section
486 * @param unknown_type $action
488 function forge_check_global_perm ($section, $action = NULL) {
489 $engine = RBACEngine::getInstance() ;
491 return $engine->isGlobalActionAllowed($section, $action) ;
494 function forge_check_perm_for_user ($user, $section, $reference, $action = NULL) {
495 $engine = RBACEngine::getInstance() ;
497 return $engine->isActionAllowedForUser($user, $section, $reference, $action) ;
500 function forge_check_global_perm_for_user ($user, $section, $action = NULL) {
501 $engine = RBACEngine::getInstance() ;
503 return $engine->isGlobalActionAllowedForUser($user, $section, $action) ;
508 // c-file-style: "bsd"