3 * FusionForge role-based access control
5 * Copyright 2004, GForge, LLC
6 * Copyright 2009-2010, Roland Mas
7 * Copyright 2012-2014, Franck Villaume - TrivialDev
8 * Copyright 2012, Thorsten “mirabilos” Glaser <t.glaser@tarent.de>
9 * Copyright 2013, French Ministry of National Education
10 * Copyright 2014, Inria (Sylvain Beucler)
11 * http://fusionforge.org
13 * This file is part of FusionForge. FusionForge is free software;
14 * you can redistribute it and/or modify it under the terms of the
15 * GNU General Public License as published by the Free Software
16 * Foundation; either version 2 of the Licence, or (at your option)
19 * FusionForge is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 require $gfcommon.'include/PFO-RBAC.interface.php';
30 require_once $gfcommon.'frs/FRSPackageFactory.class.php';
31 require_once $gfcommon.'include/SysTasksQ.class.php';
33 // Code shared between classes
36 * TODO: RBAC::BaseRole Enter description here ...
39 abstract class BaseRole extends FFError {
41 * TODO: Enter description here ...
46 * TODO: Enter description here ...
51 * TODO: Enter description here ...
57 // var $setting_array;
59 public function __construct() {
60 // TODO: document these tables
61 // $gfcommon.'include/rbac_texts.php' may provide some hints...
62 $this->role_values = array(
63 'forge_admin' => array(0, 1),
64 'approve_projects' => array(0, 1),
65 'approve_news' => array(0, 1),
66 'forge_stats' => array(0, 1, 2),
68 'project_read' => array(0, 1),
69 'project_admin' => array(0, 1),
71 'tracker_admin' => array(0, 1),
72 'pm_admin' => array(0, 1),
73 'forum_admin' => array(0, 1),
74 'frs_admin' => array(0, 1, 2),
76 'tracker' => array(0, 1, 9, 11, 13, 15, 25, 27, 29, 31),
77 'pm' => array(0, 1, 3, 5, 7),
78 'forum' => array(0, 1, 2, 3, 4),
79 'frs' => array(0, 1, 2, 3, 4),
81 'new_tracker' => array(0, 1, 9, 11, 13, 15, 25, 27, 29, 31),
82 'new_pm' => array(0, 1, 3, 5, 7),
83 'new_forum' => array(0, 1, 2, 3, 4),
84 'new_frs' => array(0, 1, 2, 3, 4),
86 'scm' => array (0, 1, 2),
87 'docman' => array (0, 1, 2, 3, 4),
91 $this->global_settings = array(
92 'forge_admin', // “God mode”: all actions allowed
93 'approve_projects', // Ability to approve pending projects
94 'approve_news', // Ability to approve news bits to the forge front page
98 // TODO: document these (Project-related permissions ?)
99 $this->defaults = array(
100 'Admin' => array( 'project_admin'=> 1,
108 'tracker_admin' => 1,
113 'Senior Developer' => array( 'project_read' => 1,
120 'tracker_admin' => 1,
125 'Junior Developer' => array( 'project_read' => 1,
134 'Doc Writer' => array( 'project_read' => 1,
142 'Support Tech' => array( 'project_read' => 1,
147 'tracker_admin' => 1,
155 public function getUsers() {
158 public function hasUser($user) {
159 throw new Exception ("Not implemented");
161 function hasGlobalPermission($section, $action = NULL) {
162 return $this->hasPermission ($section, -1, $action);
164 public function getSettings() {
165 throw new Exception ("Not implemented");
167 public function setSettings($data) {
168 throw new Exception ("Not implemented");
170 public function delete() {
171 throw new Exception ("Not implemented");
175 * getLinkedProjects - List of projects referencing that role
177 * Includes the home project (for roles that have one)
179 * @return array Array of Group objects
181 public function getLinkedProjects() {
184 $hp = $this->getHomeProject();
186 $ids[] = $hp->getID();
189 $res = db_query_params('SELECT group_id FROM role_project_refs WHERE role_id=$1',
190 array($this->getID()));
192 while ($arr = db_fetch_array ($res)) {
193 $ids[] = $arr['group_id'];
197 return group_get_objects(array_unique($ids));
200 function linkProject($project) { // From the PFO spec
202 $hp = $this->getHomeProject();
203 if ($hp != NULL && $hp->getID() == $project->getID()) {
204 $this->setError(_("Cannot link to home project"));
208 $res = db_query_params('SELECT group_id FROM role_project_refs WHERE role_id=$1 AND group_id=$2',
209 array($this->getID(),
212 if (db_numrows($res)) {
215 $res = db_query_params('INSERT INTO role_project_refs (role_id, group_id) VALUES ($1, $2)',
216 array($this->getID(),
218 if (!$res || db_affected_rows($res) < 1) {
219 $this->setError('linkProject('.$project->getID().') '.db_error());
223 $this->normalizeData();
225 foreach ($this->getUsers() as $u) {
226 if (!$SYS->sysCheckCreateUser($u->getID())) {
227 $this->setError($SYS->getErrorMessage());
235 function unlinkProject($project) { // From the PFO spec
237 $hp = $this->getHomeProject();
238 if ($hp != NULL && $hp->getID() == $project->getID()) {
239 $this->setError (_("Cannot unlink from home project"));
243 $res = db_query_params('DELETE FROM role_project_refs WHERE role_id=$1 AND group_id=$2',
244 array($this->getID(),
247 $this->setError('unlinkProject('.$project->getID().') '.db_error());
251 $this->removeObsoleteSettings();
253 foreach ($this->getUsers() as $u) {
254 if (!$SYS->sysCheckCreateUser($u->getID())) {
255 $this->setError($SYS->getErrorMessage());
260 $hook_params = array();
261 $hook_params['role'] =& $this;
262 $hook_params['project'] =& $project;
263 plugin_hook ("role_unlink_project", $hook_params);
265 # Change repo permissions when we change anonymous access
266 # Not done in SetSetting() because we used batch-mode removeObsoleteSettings()
267 $anon = RoleAnonymous::getInstance();
268 if ($this->getID() == $anon->getID()) {
269 $systasksq = new SysTasksQ();
270 $systasksq->add(SYSTASK_CORE, 'SCM_REPO', $project->getID());
277 * fetchData - May need to refresh database fields.
279 * If an update occurred and you need to access the updated info.
281 * @param int $role_id
282 * @return bool success
284 function fetchData($role_id) {
285 unset($this->data_array);
286 unset($this->setting_array);
287 unset($this->perms_array);
289 $res = db_query_params('SELECT * FROM pfo_role WHERE role_id=$1',
291 if (!$res || db_numrows($res) < 1) {
292 $this->setError('BaseRole::fetchData()::'.db_error());
295 $this->data_array = db_fetch_array($res);
296 if ($this->data_array['is_public'] == 't') {
297 $this->data_array['is_public'] = true;
299 $this->data_array['is_public'] = false;
301 $res = db_query_params('SELECT section_name, ref_id, perm_val FROM pfo_role_setting WHERE role_id=$1',
304 $this->setError('BaseRole::fetchData()::'.db_error());
307 // TODO: document perms_array
308 $this->perms_array=array();
309 while ($arr = db_fetch_array($res)) {
310 $this->perms_array[$arr['section_name']][$arr['ref_id']] = intval($arr['perm_val']);
316 function setSetting ($section, $reference, $value) {
317 $cur = $this->getSettingRaw($section, $reference);
318 if (($value == $cur) && ($cur != NULL)) {
322 $role_id = $this->getID();
324 db_query_params ('DELETE FROM pfo_role_setting WHERE role_id=$1 AND section_name=$2 AND ref_id=$3',
329 db_query_params ('INSERT INTO pfo_role_setting (role_id, section_name, ref_id, perm_val) VALUES ($1, $2, $3, $4)',
334 $this->perms_array[$section][$reference] = $value;
336 # Change repo permissions when we change anonymous access
337 $anon = RoleAnonymous::getInstance();
338 if ($section == 'scm' && $this->getID() == $anon->getID()) {
339 $systasksq = new SysTasksQ();
340 $systasksq->add(SYSTASK_CORE, 'SCM_REPO', $reference);
344 function getSettingsForProject ($project) {
346 $group_id = $project->getID();
348 $sections = array ('project_read', 'project_admin', 'scm', 'docman', 'tracker_admin', 'new_tracker');
349 foreach ($sections as $section) {
350 $result[$section][$group_id] = $this->getVal ($section, $group_id);
353 if ($project->usesTracker()) {
354 $atf = new ArtifactTypeFactory ($project);
355 if (!$atf->isError()) {
356 $tids = $atf->getAllArtifactTypeIds();
357 foreach ($tids as $tid) {
358 $result['tracker'][$tid] = $this->getVal ('tracker', $tid);
361 array_push ($sections,'tracker');
363 $sections_frs = array('frs_admin', 'new_frs');
364 foreach ($sections_frs as $section_frs) {
365 $result[$section_frs][$group_id] = $this->getVal($section_frs, $group_id);
367 $sections = array_merge($sections, $sections_frs);
369 if ($project->usesFRS()) {
370 $frspf = new FRSPackageFactory($project);
371 if (!$frspf->isError()) {
372 $pkgids = $frspf->getAllPackagesIds();
373 foreach ($pkgids as $pkgid) {
374 $result['frs'][$pkgid] = $this->getVal ('frs',$pkgid);
377 array_push($sections,'frs');
379 $sections = array_merge($sections, $sections_frs);
381 /*XXX merge from Branch_5_1: maybe this also only if usesForum? */
382 $sections_forum = array('forum_admin', 'new_forum');
383 foreach ($sections_forum as $section_forum) {
384 $result[$section_forum][$group_id] = $this->getVal ($section_forum, $group_id);
386 $sections = array_merge($sections, $sections_forum);
388 if ($project->usesForum()) {
389 $ff = new ForumFactory ($project);
390 if (!$ff->isError()) {
391 $fids = $ff->getAllForumIdsWithNews();
392 foreach ($fids as $fid) {
393 $result['forum'][$fid] = $this->getVal ('forum', $fid);
396 array_push ($sections,'forum');
399 /*XXX see above, maybe only if usesPM? */
400 $sections_pm = array('pm_admin', 'new_pm');
401 foreach ($sections_pm as $section_pm) {
402 $result[$section_pm][$group_id] = $this->getVal ($section_pm, $group_id);
404 $sections = array_merge($sections, $sections_pm);
406 if ($project->usesPM()) {
407 $pgf = new ProjectGroupFactory ($project);
408 if (!$pgf->isError()) {
409 $pgids = $pgf->getAllProjectGroupIds();
410 foreach ($pgids as $pgid) {
411 $result['pm'][$pgid] = $this->getVal ('pm', $pgid);
414 array_push ($sections,'pm');
417 // Add settings not yet listed so far (probably plugins)
418 // Currently handled:
419 // - global settings (ignored here)
420 // - project-wide settings (core and plugins)
421 // - settings for multiple-instance tools coming from the core (trackers/pm/forums)
423 // - settings for multiple-instance tools from plugins
424 foreach (array_keys ($this->perms_array) as $section) {
425 if (!in_array ($section, $sections)) {
426 if (!in_array ($section, $this->global_settings)) {
427 $result[$section][$group_id] = $this->getVal ($section, $group_id);
436 * getGlobalSettings - get the permissions for global settings
438 * The following sections are global : forge_admin, forge_stats, approve_projects, approve_news
440 * @return array array of permission for global settings
442 function getGlobalSettings() {
445 $sections = array ('forge_admin', 'forge_stats', 'approve_projects', 'approve_news');
446 foreach ($sections as $section) {
447 $result[$section][-1] = $this->getVal($section, -1);
449 // Add settings not yet listed so far (probably plugins)
450 foreach (array_keys ($this->perms_array) as $section) {
451 if (!in_array ($section, $sections)) {
452 if (in_array ($section, $this->global_settings)) {
453 $result[$section][-1] = $this->getVal ($section, -1);
462 * getSetting - TODO: Enter description here ...
464 * @param string $section
465 * @param unknown_type $reference
466 * @return number|bool
468 function getSetting($section, $reference) {
469 $value = $this->getSettingRaw($section, $reference);
470 if ($value == NULL) {
483 case 'approve_projects':
485 case 'project_admin':
486 if ($this->hasGlobalPermission('forge_admin')) {
493 if ($this->hasGlobalPermission('forge_admin')) {
500 if ($this->hasPermission('project_admin', $reference)) {
505 case 'tracker_admin':
508 if ($this->hasPermission('project_admin', $reference)) {
510 } elseif (!$this->hasPermission('project_read', $reference)) {
517 if ($this->hasPermission('project_admin', $reference)) {
519 } elseif (!$this->hasPermission('project_read', $reference)) {
526 if ($this->hasPermission('project_admin', $reference)) {
528 } elseif (!$this->hasPermission('project_read', $reference)) {
535 if ($this->hasPermission('frs_admin', frspackage_get_groupid($reference), 'admin')) {
537 } elseif (!$this->hasPermission('project_read', frspackage_get_groupid($reference))) {
543 if ($this->hasPermission('frs_admin', $reference, 'admin')) {
545 } elseif (!$this->hasPermission('project_read', $reference)) {
552 if ($this->hasPermission('forum_admin', forum_get_groupid($reference))) {
554 } elseif (!$this->hasPermission('project_read', forum_get_groupid($reference))) {
560 if ($this->hasPermission('forum_admin', $reference)) {
562 } elseif (!$this->hasPermission('project_read', $reference)) {
569 if ($this->hasPermission('tracker_admin', artifacttype_get_groupid($reference))) {
571 } elseif (!$this->hasPermission('project_read', artifacttype_get_groupid($reference))) {
577 if ($this->hasPermission('tracker_admin', $reference)) {
579 } elseif (!$this->hasPermission('project_read', $reference)) {
586 if ($this->hasPermission('pm_admin', projectgroup_get_groupid($reference))) {
588 } elseif (!$this->hasPermission('project_read', projectgroup_get_groupid($reference))) {
594 if ($this->hasPermission('pm_admin', $reference)) {
596 } elseif (!$this->hasPermission('project_read', $reference)) {
602 $hook_params = array();
603 $hook_params['role'] = $this;
604 $hook_params['section'] = $section;
605 $hook_params['reference'] = $reference;
606 $hook_params['value'] = $value;
607 $hook_params['result'] = NULL;
608 plugin_hook_by_reference ("role_get_setting", $hook_params);
609 return $hook_params['result'];
614 function getSettingRaw($section, $reference) {
615 if (isset ($this->perms_array[$section][$reference])) {
616 return $this->perms_array[$section][$reference];
621 * getVal - get a value out of the array of settings for this role.
623 * @param string $section The name of the role.
624 * @param int $ref_id The ref_id (ex: group_artifact_id, group_forum_id) for this item.
625 * @return int The value of this item.
627 function getVal($section, $ref_id) {
631 return $this->getSetting($section, $ref_id);
635 * &getRoleVals - get all the values and language text strings for this section.
637 * @param string $section
638 * @return array Assoc array of values for this section.
640 function &getRoleVals($section) {
641 global $role_vals, $rbac_permission_names;
642 setup_rbac_strings();
645 // Optimization - save array so it is only built once per page view
647 if (!isset($role_vals[$section])) {
649 for ($i=0; $i<count($this->role_values[$section]); $i++) {
651 // Build an associative array of these key values + localized description
653 $role_vals[$section][$this->role_values[$section][$i]] =
654 util_ifsetor($rbac_permission_names["$section".$this->role_values[$section][$i]],
655 _('UNKNOWN (internal error, report bug to FusionForge)'));
658 return $role_vals[$section];
661 function hasPermission($section, $reference, $action = NULL) {
663 $value = $this->getSetting ($section, $reference);
670 case 'approve_projects':
672 case 'project_admin':
674 case 'tracker_admin':
677 return ($value >= 1);
684 return ($value >= 1);
687 return ($value >= 2);
695 return ($value >= 1);
698 return ($value >= 2);
706 return ($value >= 1);
709 return ($value >= 2);
712 return ($value >= 3);
715 return ($value >= 4);
724 return ($value >= 1);
727 return ($value >= 2);
730 return ($value >= 3);
733 return ($value >= 4);
742 return ($value >= 1);
745 return ($value >= 2);
747 case 'unmoderated_post':
748 return ($value >= 3);
751 return ($value >= 4);
760 return (($value & 1) != 0);
763 return (($value & 2) != 0);
766 return (($value & 4) != 0);
769 return (($value & 8) != 0);
772 return (($value & 16) != 0);
781 return (($value & 1) != 0);
784 return (($value & 2) != 0);
787 return (($value & 4) != 0);
793 $hook_params = array();
794 $hook_params['section'] = $section;
795 $hook_params['reference'] = $reference;
796 $hook_params['action'] = $action;
797 $hook_params['value'] = $value;
798 $hook_params['result'] = false;
799 plugin_hook_by_reference ("role_has_permission", $hook_params);
800 return $hook_params['result'];
806 * update - update a role in the database.
808 * @param string $role_name The name of the role.
809 * @param array $data A multi-dimensional array of data in this format: $data['section_name']['ref_id']=$val
810 * @param bool $check_perms Perform permission checking
811 * @param bool $update_sys Update system users & groups membership
812 * @return bool True on success or false on failure.
814 function update($role_name,$data,$check_perms=true,$update_sys=true) {
817 if ($this->getHomeProject() == NULL) {
818 if (!forge_check_global_perm ('forge_admin')) {
819 $this->setPermissionDeniedError();
822 } elseif (!forge_check_perm ('project_admin', $this->getHomeProject()->getID())) {
823 $this->setPermissionDeniedError();
830 $role_id = $this->getID();
832 if ($role_name != $this->getName()) {
833 $this->setName($role_name);
836 db_prepare ('INSERT INTO pfo_role_setting (role_id, section_name, ref_id, perm_val) VALUES ($1, $2, $3, $4)',
837 'insert_into_pfo_role_setting');
838 db_prepare ('DELETE FROM pfo_role_setting WHERE role_id=$1 AND section_name=$2 AND ref_id=$3',
839 'delete_from_pfo_role_setting');
840 db_prepare ('UPDATE pfo_role_setting SET perm_val=$4 WHERE role_id=$1 AND section_name=$2 AND ref_id=$3',
841 'update_pfo_role_setting');
843 // Don't remove unknown permissions (e.g. forums permissions while forums are currently disabled)
844 //foreach ($this->perms_array as $sect => &$refs)
845 // foreach ($refs as $refid => $value)
846 // if (!isset($data[$sect][$refid]) or $data[$sect][$refid] != $value)
847 // db_execute('delete_from_pfo_role_setting', array($role_id, $sect, $refid));
849 // Insert new/changed permissions
850 $anon = RoleAnonymous::getInstance();
851 foreach ($data as $sect => &$refs) {
852 foreach ($refs as $refid => $value) {
853 if (!isset($this->perms_array[$sect][$refid])) {
855 db_execute('insert_into_pfo_role_setting',
856 array($role_id, $sect, $refid, $value));
857 } elseif ($this->perms_array[$sect][$refid] != $value) {
858 // changed permission
859 db_execute('update_pfo_role_setting',
860 array($role_id, $sect, $refid, $value));
864 # Change repo permissions when we edit anonymous access for a single project
865 if ($sect == 'scm' && $this->getID() == $anon->getID() && count($refs) == 1) {
866 $systasksq = new SysTasksQ();
867 $systasksq->add(SYSTASK_CORE, 'SCM_REPO', $refid);
871 db_unprepare ('insert_into_pfo_role_setting');
872 db_unprepare ('delete_from_pfo_role_setting');
873 db_unprepare ('update_pfo_role_setting');
875 $hook_params = array();
876 $hook_params['role'] =& $this;
877 $hook_params['role_id'] = $this->getID();
878 $hook_params['data'] = $data;
879 plugin_hook ("role_update", $hook_params);
882 $this->fetchData($this->getID());
885 foreach ($this->getUsers() as $u) {
886 if (!$SYS->sysCheckCreateUser($u->getID())) {
887 $this->setError($SYS->getErrorMessage());
896 function getDisplayableName($group = NULL) {
897 if ($this->getHomeProject() == NULL) {
898 return sprintf (_('%s (global role)'),
900 } elseif ($group == NULL
901 || $this->getHomeProject()->getID() != $group->getID()) {
902 return sprintf (_('%s (in project %s)'),
904 $this->getHomeProject()->getPublicName());
906 return $this->getName();
910 function removeObsoleteSettings() {
913 // Remove obsolete project-wide settings
914 $sections = array ('project_read', 'project_admin', 'frs_admin', 'new_frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm');
915 db_query_params ('DELETE FROM pfo_role_setting where role_id=$1 AND section_name=ANY($2) and ref_id NOT IN (SELECT home_group_id FROM pfo_role WHERE role_id=$1 AND home_group_id IS NOT NULL UNION SELECT group_id from role_project_refs WHERE role_id=$1)',
916 array ($this->getID(),
917 db_string_array_to_any_clause($sections)));
919 // Remove obsolete settings for multiple-instance tools
920 db_query_params ('DELETE FROM pfo_role_setting where role_id=$1 AND section_name=$2 and ref_id NOT IN (SELECT group_artifact_id FROM artifact_group_list WHERE group_id IN (SELECT home_group_id FROM pfo_role WHERE role_id=$1 AND home_group_id IS NOT NULL UNION SELECT group_id from role_project_refs WHERE role_id=$1))',
921 array ($this->getID(),
923 db_query_params ('DELETE FROM pfo_role_setting where role_id=$1 AND section_name=$2 and ref_id NOT IN (SELECT group_project_id FROM project_group_list WHERE group_id IN (SELECT home_group_id FROM pfo_role WHERE role_id=$1 AND home_group_id IS NOT NULL UNION SELECT group_id from role_project_refs WHERE role_id=$1))',
924 array ($this->getID(),
926 db_query_params ('DELETE FROM pfo_role_setting where role_id=$1 AND section_name=$2 and ref_id NOT IN (SELECT group_forum_id FROM forum_group_list WHERE group_id IN (SELECT home_group_id FROM pfo_role WHERE role_id=$1 AND home_group_id IS NOT NULL UNION SELECT group_id from role_project_refs WHERE role_id=$1))',
927 array ($this->getID(),
929 db_query_params ('DELETE FROM pfo_role_setting where role_id=$1 AND section_name=$2 and ref_id NOT IN (SELECT package_id FROM frs_package WHERE group_id IN (SELECT home_group_id FROM pfo_role WHERE role_id=$1 AND home_group_id IS NOT NULL UNION SELECT group_id from role_project_refs WHERE role_id=$1))',
930 array ($this->getID(), 'frs'));
933 $this->fetchData($this->getID());
937 function normalizePermsForSection (&$new_pa, $section, $refid) {
938 if (array_key_exists ($section, $this->perms_array)
939 && array_key_exists ($refid, $this->perms_array[$section])) {
940 $new_pa[$section][$refid] = $this->perms_array[$section][$refid];
941 } elseif (array_key_exists ($this->data_array['role_name'], $this->defaults)
942 && array_key_exists ($section, $this->defaults[$this->data_array['role_name']])) {
943 $new_pa[$section][$refid] = $this->defaults[$this->data_array['role_name']][$section];
945 $new_pa[$section][$refid] = 0;
950 function normalizeData() { // From the PFO spec
951 $this->removeObsoleteSettings();
953 $this->fetchData ($this->getID());
955 $projects = $this->getLinkedProjects();
958 // Add missing settings
959 // ...project-wide settings
960 $arr = array ('project_read', 'project_admin', 'scm', 'docman', 'frs_admin', 'new_frs', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm');
961 foreach ($projects as $p) {
962 foreach ($arr as $section) {
963 $this->normalizePermsForSection ($new_pa, $section, $p->getID());
966 $this->normalizePermsForSection($new_pa, 'forge_admin', -1);
967 $this->normalizePermsForSection($new_pa, 'approve_projects', -1);
968 $this->normalizePermsForSection($new_pa, 'approve_news', -1);
969 $this->normalizePermsForSection($new_pa, 'forge_stats', -1);
971 $hook_params = array();
972 $hook_params['role'] =& $this;
973 $hook_params['new_pa'] =& $new_pa;
974 plugin_hook ("role_normalize", $hook_params);
976 // ...tracker-related settings
977 $new_pa['tracker'] = array();
978 // Direct query to avoid querying each project - especially for global roles
979 $project_ids = array();
980 foreach ($projects as $p) {
981 $project_ids[] = $p->getID();
983 $res = db_query_params('SELECT group_artifact_id FROM artifact_group_list JOIN groups USING (group_id) WHERE use_tracker = 1 AND group_id = ANY($1)',
984 array(db_int_array_to_any_clause($project_ids)));
985 while ($row = db_fetch_array($res)) {
986 $tid = $row['group_artifact_id'];
987 if (array_key_exists ('tracker', $this->perms_array)
988 && array_key_exists ($tid, $this->perms_array['tracker']) ) {
989 $new_pa['tracker'][$tid] = $this->perms_array['tracker'][$tid];
990 } elseif (array_key_exists ('new_tracker', $this->perms_array)
991 && array_key_exists ($p->getID(), $this->perms_array['new_tracker']) ) {
992 $new_pa['tracker'][$tid] = $new_pa['new_tracker'][$p->getID()];
996 // ...forum-related settings
997 $new_pa['forum'] = array();
998 foreach ($projects as $p) {
999 if (!$p->usesForum()) {
1002 $ff = new ForumFactory ($p);
1003 if (!$ff->isError()) {
1004 $fids = $ff->getAllForumIdsWithNews();
1005 foreach ($fids as $fid) {
1006 if (array_key_exists ('forum', $this->perms_array)
1007 && array_key_exists ($fid, $this->perms_array['forum']) ) {
1008 $new_pa['forum'][$fid] = $this->perms_array['forum'][$fid];
1009 } elseif (array_key_exists ('new_forum', $this->perms_array)
1010 && array_key_exists ($p->getID(), $this->perms_array['new_forum']) ) {
1011 $new_pa['forum'][$fid] = $new_pa['new_forum'][$p->getID()];
1017 // ...pm-related settings
1018 $new_pa['pm'] = array();
1019 foreach ($projects as $p) {
1020 if (!$p->usesPM()) {
1023 $pgf = new ProjectGroupFactory ($p);
1024 if (!$pgf->isError()) {
1025 $pgids = $pgf->getAllProjectGroupIds();
1026 foreach ($pgids as $gid) {
1027 if (array_key_exists ('pm', $this->perms_array)
1028 && array_key_exists ($gid, $this->perms_array['pm']) ) {
1029 $new_pa['pm'][$gid] = $this->perms_array['pm'][$gid];
1030 } elseif (array_key_exists ('new_pm', $this->perms_array)
1031 && array_key_exists ($p->getID(), $this->perms_array['new_pm']) ) {
1032 $new_pa['pm'][$gid] = $new_pa['new_pm'][$p->getID()];
1038 // ...frs-related settings
1039 $new_pa['frs'] = array();
1040 foreach ($projects as $p) {
1041 if (!$p->usesFRS()) {
1044 $frspf = new FRSPackageFactory($p);
1045 if (!$frspf->isError()) {
1046 $frspids = $frspf->getAllPackagesIds();
1047 foreach ($frspids as $frspid) {
1048 if (array_key_exists('frs', $this->perms_array) && array_key_exists($frspid, $this->perms_array['frs'])) {
1049 $new_pa['frs'][$frspid] = $this->perms_array['frs'][$frspid];
1050 } elseif (array_key_exists('new_frs', $this->perms_array) && array_key_exists($p->getID(), $this->perms_array['new_frs']) ) {
1051 $new_pa['frs'][$frspid] = $new_pa['new_frs'][$p->getID()];
1057 $this->update($this->getName(), $new_pa, false, false);
1065 * TODO: RBAC::RoleExplicit Enter description here ...
1068 abstract class RoleExplicit extends BaseRole implements PFO_RoleExplicit {
1069 public function addUsers($users) {
1073 foreach ($users as $user) {
1074 $ids[] = $user->getID();
1077 $already_there = array();
1078 $res = db_query_params('SELECT user_id FROM pfo_user_role WHERE user_id=ANY($1) AND role_id=$2',
1079 array(db_int_array_to_any_clause($ids), $this->getID()));
1083 while ($arr = db_fetch_array($res)) {
1084 $already_there[] = $arr['user_id'];
1087 foreach ($ids as $id) {
1088 if (!in_array($id, $already_there)) {
1089 $res = db_query_params('INSERT INTO pfo_user_role (user_id, role_id) VALUES ($1, $2)',
1090 array ($id, $this->getID()));
1097 foreach ($this->getLinkedProjects() as $p) {
1098 foreach ($ids as $uid) {
1099 if (!$SYS->sysGroupCheckUser($p->getID(),$uid)) {
1108 public function addUser($user) {
1109 if (!$this->addUsers(array($user))) {
1112 $hook_params['user'] = $user;
1113 $hook_params['role'] = $this;
1114 plugin_hook ("role_adduser", $hook_params);
1119 public function removeUsers($users) {
1123 foreach ($users as $user) {
1124 $ids[] = $user->getID();
1127 $res = db_query_params('DELETE FROM pfo_user_role WHERE user_id = ANY($1) AND role_id = $2',
1128 array(db_int_array_to_any_clause($ids), $this->getID()));
1130 foreach ($this->getLinkedProjects() as $p) {
1131 foreach ($ids as $uid) {
1132 $SYS->sysGroupCheckUser($p->getID(), $uid);
1139 public function removeUser($user) {
1140 if (!$this->removeUsers(array($user))) {
1143 $hook_params['user'] = $user;
1144 $hook_params['role'] = $this;
1145 plugin_hook("role_removeuser", $hook_params);
1150 public function getUsers() {
1152 $res = db_query_params('SELECT user_id FROM pfo_user_role WHERE role_id=$1',
1153 array($this->getID()));
1154 while ($arr = db_fetch_array($res)) {
1155 $result[] = user_get_object($arr['user_id']);
1161 public function hasUser($user) {
1162 $res = db_query_params('SELECT user_id FROM pfo_user_role WHERE user_id=$1 AND role_id=$2',
1163 array($user->getID(), $this->getID()));
1164 if ($res && db_numrows($res)) {
1171 function getID() { // From the PFO spec
1172 return $this->data_array['role_id'];
1175 function getName() { // From the PFO spec
1176 return $this->data_array['role_name'];
1180 class RoleAnonymous extends BaseRole implements PFO_RoleAnonymous {
1181 // This role is implemented as a singleton
1182 private static $_instance;
1184 public static function getInstance() {
1185 if (isset(self::$_instance)) {
1186 return self::$_instance;
1190 self::$_instance = new $c;
1192 /* drop vote rights from RoleAnonymous */
1194 foreach (array('tracker', 'new_tracker') as $x) {
1196 foreach (self::$_instance->role_values[$x] as $z) {
1197 if (($z & 16) != 0) {
1202 self::$_instance->role_values[$x] = $y;
1205 $res = db_query_params('SELECT r.role_id FROM pfo_role r, pfo_role_class c WHERE r.role_class = c.class_id AND c.class_name = $1',
1206 array('PFO_RoleAnonymous'));
1207 if (!$res || !db_numrows($res)) {
1208 throw new Exception(_('No PFO_RoleAnonymous role in the database'));
1210 self::$_instance->_role_id = db_result ($res, 0, 'role_id');
1212 $hook_params = array();
1213 $hook_params['role'] =& self::$_instance;
1214 plugin_hook ('role_get', $hook_params);
1216 self::$_instance->fetchData(self::$_instance->_role_id);
1218 return self::$_instance;
1221 public function getID() {
1222 return $this->_role_id;
1224 public function isPublic() {
1227 public function setPublic($flag) {
1228 throw new Exception(_('Cannot setPublic() on RoleAnonymous'));
1230 public function getHomeProject() {
1233 public function getName() {
1234 return _('Anonymous/not logged in');
1236 public function setName($name) {
1237 throw new Exception(_('Cannot setName() on RoleAnonymous'));
1241 class RoleLoggedIn extends BaseRole implements PFO_RoleLoggedin {
1242 // This role is implemented as a singleton
1243 private static $_instance;
1245 public static function getInstance() {
1246 if (isset(self::$_instance)) {
1247 return self::$_instance;
1251 self::$_instance = new $c;
1253 $res = db_query_params('SELECT r.role_id FROM pfo_role r, pfo_role_class c WHERE r.role_class = c.class_id AND c.class_name = $1',
1254 array ('PFO_RoleLoggedIn'));
1255 if (!$res || !db_numrows($res)) {
1256 throw new Exception(_('No PFO_RoleLoggedIn role in the database'));
1258 self::$_instance->_role_id = db_result ($res, 0, 'role_id');
1260 $hook_params = array();
1261 $hook_params['role'] =& self::$_instance;
1262 plugin_hook ('role_get', $hook_params);
1264 self::$_instance->fetchData (self::$_instance->_role_id);
1266 return self::$_instance;
1269 public function getID() {
1270 return $this->_role_id;
1272 public function isPublic() {
1275 public function setPublic ($flag) {
1276 throw new Exception(_('Cannot setPublic() on RoleLoggedIn'));
1278 public function getHomeProject() {
1281 public function getName() {
1282 return _('Any user logged in');
1284 public function setName($name) {
1285 throw new Exception(_('Cannot setName() on RoleLoggedIn'));
1289 abstract class RoleUnion extends BaseRole implements PFO_RoleUnion {
1290 public function addRole($role) {
1291 throw new Exception(_('Not implemented'));
1293 public function removeRole($role) {
1294 throw new Exception(_('Not implemented'));
1299 * TODO: Enter description here ...
1302 class RoleComparator {
1303 var $criterion = 'composite';
1304 var $reference_project = NULL;
1306 function Compare ($a, $b) {
1307 switch ($this->criterion) {
1309 return strcoll ($a->getName(), $b->getName());
1317 return ($a < $b) ? -1 : 1;
1321 if ($this->reference_project == NULL) {
1322 return $this->CompareNoRef ($a, $b);
1324 $rpid = $this->reference_project->getID();
1325 $ap = $a->getHomeProject();
1326 $bp = $b->getHomeProject();
1327 $a_is_local = ($ap != NULL && $ap->getID() == $rpid); // Local
1328 $b_is_local = ($bp != NULL && $bp->getID() == $rpid);
1330 if ($a_is_local && !$b_is_local) {
1332 } elseif (!$a_is_local && $b_is_local) {
1335 return $this->CompareNoRef ($a, $b);
1340 * CompareNoRef - TODO: Enter description here ...
1345 function CompareNoRef ($a, $b) {
1346 $ap = $a->getHomeProject();
1347 $bp = $b->getHomeProject();
1348 if ($ap == NULL && $bp != NULL) {
1350 } elseif ($ap != NULL && $bp == NULL) {
1352 } elseif ($ap == NULL && $bp == NULL) {
1353 $tmp = strcoll ($a->getName(), $b->getName());
1356 $projcmp = new ProjectComparator();
1357 $projcmp->criterion = 'name';
1358 $tmp = $projcmp->Compare ($ap, $bp);
1359 if ($tmp) { /* Different projects, sort accordingly */
1362 return strcoll ($a->getName(), $b->getName());
1367 function sortRoleList (&$list, $relative_to = NULL, $criterion='composite') {
1368 $cmp = new RoleComparator();
1369 $cmp->criterion = $criterion;
1370 $cmp->reference_project = $relative_to;
1372 return usort ($list, array ($cmp, 'Compare'));
1377 // c-file-style: "bsd"