3 * FusionForge role-based access control
5 * Copyright 2004, GForge, LLC
6 * Copyright 2009-2010, Roland Mas
8 * This file is part of FusionForge. FusionForge is free software;
9 * you can redistribute it and/or modify it under the terms of the
10 * GNU General Public License as published by the Free Software
11 * Foundation; either version 2 of the Licence, or (at your option)
14 * FusionForge is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 require "PFO-RBAC.interface.php" ;
26 if (true || file_exists ('/tmp/fusionforge-use-pfo-rbac')) {
27 define ('USE_PFO_RBAC', true) ;
29 define ('USE_PFO_RBAC', false) ;
32 // Code shared between classes
34 abstract class BaseRole extends Error {
39 public function BaseRole() {
41 $this->role_values = array (
42 'forge_admin' => array (0,1),
43 'approve_projects' => array (0,1),
44 'approve_news' => array (0,1),
45 'forge_stats' => array (0,1,2),
47 'project_read' => array (0,1),
48 'project_admin' => array (0,1),
50 'tracker_admin' => array (0,1),
51 'pm_admin' => array (0,1),
52 'forum_admin' => array (0,1),
54 'tracker' => array (0,1,3,5,7),
55 'pm' => array (0,1,3,5,7),
56 'forum' => array (0,1,2,3,4),
58 'new_tracker' => array (0,1,3,5,7),
59 'new_pm' => array (0,1,3,5,7),
60 'new_forum' => array (0,1,2,3,4),
62 'scm' => array (0,1,2),
63 'docman' => array (0,1,2,3,4),
64 'frs' => array (0,1,2,3),
66 'webcal' => array (0,1,2),
69 $this->global_settings = array (
76 $this->defaults = array(
77 'Admin' => array( 'project_admin'=> 1,
90 'Senior Developer' => array( 'project_read' => 1,
102 'Junior Developer' => array( 'project_read' => 1,
111 'Doc Writer' => array( 'project_read' => 1,
119 'Support Tech' => array( 'project_read' => 1,
123 'tracker_admin' => 1,
131 $this->role_values = array(
132 'projectadmin' => array ('0','A'),
133 'frs' => array ('0','1'),
134 'scm' => array ('-1','0','1'),
135 'docman' => array ('0','1'),
136 'forumadmin' => array ('0','2'),
137 'forum' => array ('-1','0','1','2'),
138 'newforum' => array ('-1','0','1','2'),
139 'trackeradmin' => array ('0','2'),
140 'tracker' => array ('-1','0','1','2','3'),
141 'newtracker' => array ('-1','0','1','2','3'),
142 'pmadmin' => array ('0','2'),
143 'pm' => array ('-1','0','1','2','3'),
144 'newpm' => array ('-1','0','1','2','3'),
145 'webcal' => array ('0','1','2'));
147 $this->defaults = array(
148 'Admin' => array( 'projectadmin'=>'A',
162 'Senior Developer'=> array( 'projectadmin'=>'0',
176 'Junior Developer'=> array( 'projectadmin'=>'0',
190 'Doc Writer' => array( 'projectadmin'=>'0',
204 'Support Tech' => array( 'projectadmin'=>'0',
223 public function getUsers() {
226 public function hasUser($user) {
227 throw new Exception ("Not implemented") ;
229 function hasGlobalPermission($section, $action = NULL) {
230 return $this->hasPermission ($section, -1, $action) ;
232 public function getSettings() {
233 throw new Exception ("Not implemented") ;
235 public function setSettings($data) {
236 throw new Exception ("Not implemented") ;
238 public function delete () {
239 throw new Exception ("Not implemented") ;
243 * getLinkedProjects - List of projects referencing that role
245 * Includes the home project (for roles that have one)
247 * @return array Array of Group objects
249 public function getLinkedProjects () {
252 $hp = $this->getHomeProject () ;
254 $ids[] = $hp->getID() ;
257 $res = db_query_params ('SELECT group_id FROM role_project_refs WHERE role_id=$1',
258 array ($this->getID())) ;
260 while ($arr = db_fetch_array ($res)) {
261 $ids[] = $arr['group_id'] ;
265 return group_get_objects (array_unique ($ids)) ;
268 function linkProject ($project) { // From the PFO spec
269 $hp = $this->getHomeProject () ;
270 if ($hp != NULL && $hp->getID() == $project->getID()) {
271 $this->setError ("Can't link to home project") ;
275 $res = db_query_params('SELECT group_id FROM role_project_refs WHERE role_id=$1 AND group_id=$2',
276 array ($this->getID(),
279 if (db_numrows($res)) {
282 $res = db_query_params('INSERT INTO role_project_refs (role_id, group_id) VALUES ($1, $2)',
283 array ($this->getID(),
285 if (!$res || db_affected_rows($res) < 1) {
286 $this->setError('linkProject('.$project->getID().') '.db_error());
293 function unlinkProject ($project) { // From the PFO spec
294 $hp = $this->getHomeProject () ;
295 if ($hp != NULL && $hp->getID() == $project->getID()) {
296 $this->setError ("Can't unlink from home project") ;
300 $res = db_query_params('DELETE FROM role_project_refs WHERE role_id=$1 AND group_id=$2',
301 array ($this->getID(),
304 $this->setError('unlinkProject('.$project->getID().') '.db_error());
308 $this->removeObsoleteSettings () ;
314 * fetchData - May need to refresh database fields.
316 * If an update occurred and you need to access the updated info.
318 * @return boolean success;
320 function fetchData($role_id) {
321 unset($this->data_array);
322 unset($this->setting_array);
323 unset($this->perms_array);
326 $res = db_query_params ('SELECT * FROM pfo_role WHERE role_id=$1',
328 if (!$res || db_numrows($res) < 1) {
329 $this->setError('BaseRole::fetchData()::'.db_error());
332 $this->data_array = db_fetch_array($res);
333 if ($this->data_array['is_public'] == 't') {
334 $this->data_array['is_public'] = true ;
336 $this->data_array['is_public'] = false ;
338 $res = db_query_params ('SELECT section_name, ref_id, perm_val FROM pfo_role_setting WHERE role_id=$1',
341 $this->setError('BaseRole::fetchData()::'.db_error());
344 $this->perms_array=array();
345 while ($arr = db_fetch_array($res)) {
346 $this->perms_array[$arr['section_name']][$arr['ref_id']] = $arr['perm_val'];
349 if ($this instanceof RoleAnonymous) {
350 $res = db_query_params ('SELECT group_id, enable_anonscm FROM groups WHERE is_public=1',
352 while ($arr = db_fetch_array($res)) {
353 $this->perms_array['project_read'][$arr['group_id']] = 1 ;
354 $this->perms_array['frs'][$arr['group_id']] = 1 ;
355 $this->perms_array['scm'][$arr['group_id']] = $arr['enable_anonscm'] ;
358 $res = db_query_params ('SELECT t.group_artifact_id FROM artifact_group_list t, groups g WHERE t.is_public=1 AND t.allow_anon=1 AND g.is_public=1 AND t.group_id = g.group_id',
360 while ($arr = db_fetch_array($res)) {
361 $this->perms_array['tracker'][$arr['group_artifact_id']] = 1 ;
364 $res = db_query_params ('SELECT p.group_project_id FROM project_group_list p, groups g WHERE p.is_public=1 AND g.is_public=1 AND p.group_id = g.group_id',
366 while ($arr = db_fetch_array($res)) {
367 $this->perms_array['pm'][$arr['group_project_id']] = 1 ;
370 $res = db_query_params ('SELECT f.group_forum_id, f.allow_anonymous, f.moderation_level FROM forum_group_list f, groups g WHERE f.is_public=1 AND g.is_public=1 AND f.group_id = g.group_id',
372 while ($arr = db_fetch_array($res)) {
373 if ($arr['allow_anonymous'] == 1) {
374 if ($arr['moderation_level'] == 0) {
375 $this->perms_array['forum'][$arr['group_forum_id']] = 3 ;
377 $this->perms_array['forum'][$arr['group_forum_id']] = 2 ;
380 $this->perms_array['forum'][$arr['group_forum_id']] = 1 ;
383 } elseif ($this instanceof RoleLoggedIn) {
384 $res = db_query_params ('SELECT group_id, enable_anonscm FROM groups WHERE is_public=1',
386 while ($arr = db_fetch_array($res)) {
387 $this->perms_array['project_read'][$arr['group_id']] = 1 ;
388 $this->perms_array['frs'][$arr['group_id']] = 1 ;
389 $this->perms_array['scm'][$arr['group_id']] = $arr['enable_anonscm'] ;
392 $res = db_query_params ('SELECT t.group_artifact_id FROM artifact_group_list t, groups g WHERE t.is_public=1 AND g.is_public=1 AND t.group_id = g.group_id',
394 while ($arr = db_fetch_array($res)) {
395 $this->perms_array['tracker'][$arr['group_artifact_id']] = 1 ;
398 $res = db_query_params ('SELECT p.group_project_id FROM project_group_list p, groups g WHERE p.is_public=1 AND g.is_public=1 AND p.group_id = g.group_id',
400 while ($arr = db_fetch_array($res)) {
401 $this->perms_array['pm'][$arr['group_project_id']] = 1 ;
404 $res = db_query_params ('SELECT f.group_forum_id, f.moderation_level FROM forum_group_list f, groups g WHERE f.is_public=1 AND g.is_public=1 AND f.group_id = g.group_id',
406 while ($arr = db_fetch_array($res)) {
407 if ($arr['moderation_level'] == 0) {
408 $this->perms_array['forum'][$arr['group_forum_id']] = 3 ;
410 $this->perms_array['forum'][$arr['group_forum_id']] = 2 ;
414 $res = db_query_params ('SELECT * FROM role WHERE role_id=$1',
416 if (!$res || db_numrows($res) < 1) {
417 $this->setError('BaseRole::fetchData()::'.db_error());
420 $this->data_array = db_fetch_array($res);
422 // Load pre-PFO RBAC settings...
423 $res = db_query_params ('SELECT * FROM role_setting WHERE role_id=$1',
426 $this->setError('BaseRole::fetchData()::'.db_error());
429 $this->setting_array=array();
430 while ($arr = db_fetch_array($res)) {
431 $this->setting_array[$arr['section_name']][$arr['ref_id']] = $arr['value'];
434 // ...and map section names and values to the new values
436 if ($this->data_array['group_id'] == forge_get_config ('stats_group')) {
437 $this->perms_array['forge_stats'][-1] = 2 ;
440 $this->perms_array=array();
441 $tohandle = array () ;
442 $gid = $this->data_array['group_id'] ;
443 if ($gid == 1 && count ($this->setting_array) == 0) {
444 $tohandle[] = array ('forge_admin', -1) ;
446 foreach ($this->setting_array as $oldsection => $t) {
447 switch ($oldsection) {
449 $tohandle[] = array ('project_admin', $gid) ;
450 if ($this->data_array['group_id'] == 1 && $t[0] == 'A') {
451 $tohandle[] = array ('forge_admin', -1) ;
453 if ($this->data_array['group_id'] == forge_get_config ('news_group') && $t[0] == 'A') {
454 $tohandle[] = array ('approve_news', -1) ;
456 if ($this->data_array['group_id'] == forge_get_config ('stats_group') && $t[0] == 'A') {
457 $tohandle[] = array ('forge_stats', -1) ;
461 $tohandle[] = array ('tracker_admin', $gid) ;
464 $tohandle[] = array ('pm_admin', $gid) ;
467 $tohandle[] = array ('forum_admin', $gid) ;
471 $tohandle[] = array ('new_tracker', $gid) ;
474 $tohandle[] = array ('new_pm', $gid) ;
477 $tohandle[] = array ('new_forum', $gid) ;
481 foreach ($t as $oldreference => $oldvalue) {
482 $tohandle[] = array ($oldsection, $oldreference) ;
488 foreach ($tohandle as $t) {
492 $res = db_query_params ('SELECT pfo_rbac_permissions_from_old($1,$2,$3)',
493 array ($role_id, $nsec, $nref)) ;
495 $arr = db_fetch_array($res) ;
496 $this->perms_array[$nsec][$nref] = $arr[0] ;
499 } // Explicit role (not Anonymous or LoggedIn)
505 function setSetting ($section, $reference, $value) {
506 $role_id = $this->getID () ;
508 $res = db_query_params ('DELETE FROM pfo_role_setting WHERE role_id=$1 AND section_name=$2 AND ref_id=$3',
513 $res = db_query_params ('INSERT INTO pfo_role_setting (role_id, section_name, ref_id, perm_val) VALUES ($1, $2, $3, $4)',
520 function getSettingsForProject ($project) {
522 $group_id = $project->getID() ;
525 $sections = array ('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm') ;
526 foreach ($sections as $section) {
527 $result[$section][$group_id] = $this->getVal ($section, $group_id) ;
530 $sections = array ('projectadmin', 'frs', 'scm', 'docman', 'trackeradmin', 'newtracker', 'forumadmin', 'newforum', 'pmadmin', 'newpm', 'webcal') ;
531 foreach ($sections as $section) {
532 $result[$section][0] = $this->getVal ($section, 0) ;
536 $atf = new ArtifactTypeFactory ($project) ;
537 $tids = $atf->getAllArtifactTypeIds () ;
538 foreach ($tids as $tid) {
539 $result['tracker'][$tid] = $this->getVal ('tracker', $tid) ;
541 $sections[] = 'tracker' ;
543 $ff = new ForumFactory ($project) ;
544 $fids = $ff->getAllForumIds () ;
545 foreach ($fids as $fid) {
546 $result['forum'][$fid] = $this->getVal ('forum', $fid) ;
548 $sections[] = 'forum' ;
550 $pgf = new ProjectGroupFactory ($project) ;
551 $pgids = $pgf->getAllProjectGroupIds () ;
552 foreach ($pgids as $pgid) {
553 $result['pm'][$pgid] = $this->getVal ('pm', $pgid) ;
559 // Add settings not yet listed so far (probably plugins)
560 // Currently handled:
561 // - global settings (ignored here)
562 // - project-wide settings (core and plugins)
563 // - settings for multiple-instance tools coming from the core (trackers/pm/forums)
565 // - settings for multiple-instance tools from plugins
566 foreach (array_keys ($this->perms_array) as $section) {
567 if (!in_array ($section, $sections)) {
568 if (!in_array ($section, $this->global_settings)) {
569 $result[$section][$group_id] = $this->getVal ($section, $group_id) ;
578 function getGlobalSettings () {
581 $sections = array ('forge_admin', 'forge_stats', 'approve_projects', 'approve_news') ;
582 foreach ($sections as $section) {
583 $result[$section][-1] = $this->getVal ($section, -1) ;
585 // Add settings not yet listed so far (probably plugins)
586 foreach (array_keys ($this->perms_array) as $section) {
587 if (!in_array ($section, $sections)) {
588 if (in_array ($section, $this->global_settings)) {
589 $result[$section][-1] = $this->getVal ($section, -1) ;
597 function getSetting($section, $reference) {
598 if (isset ($this->perms_array[$section][$reference])) {
599 $value = $this->perms_array[$section][$reference] ;
612 case 'approve_projects':
614 if ($this->hasGlobalPermission('forge_admin')) {
621 if ($this->hasGlobalPermission('forge_admin')) {
627 case 'project_admin':
628 if ($this->hasGlobalPermission('forge_admin')) {
635 case 'tracker_admin':
638 if ($this->hasPermission('project_admin', $reference)) {
645 if ($this->hasPermission('project_admin', $reference)) {
652 if ($this->hasPermission('project_admin', $reference)) {
659 if ($this->hasPermission('project_admin', $reference)) {
666 if ($this->hasPermission('forum_admin', forum_get_groupid($reference))) {
672 if ($this->hasPermission('forum_admin', $reference)) {
679 if ($this->hasPermission('tracker_admin', artifacttype_get_groupid($reference))) {
685 if ($this->hasPermission('tracker_admin', $reference)) {
692 if ($this->hasPermission('pm_admin', projectgroup_get_groupid($reference))) {
698 if ($this->hasPermission('pm_admin', $reference)) {
704 $hook_params = array ();
705 $hook_params['role'] = $this ;
706 $hook_params['section'] = $section ;
707 $hook_params['reference'] = $reference ;
708 $hook_params['value'] = $value ;
709 $hook_params['result'] = 0 ;
710 plugin_hook_by_reference ("role_get_setting", $hook_params);
711 return $hook_params['result'] ;
717 * getVal - get a value out of the array of settings for this role.
719 * @param string The name of the role.
720 * @param integer The ref_id (ex: group_artifact_id, group_forum_id) for this item.
721 * @return integer The value of this item.
723 function getVal($section,$ref_id) {
724 global $role_default_array;
729 return $this->getSetting ($section, $ref_id) ;
731 if (array_key_exists ($section, $this->setting_array)) {
732 return $this->setting_array[$section][$ref_id];
740 * &getRoleVals - get all the values and language text strings for this section.
742 * @return array Assoc array of values for this section.
744 function &getRoleVals($section) {
745 global $role_vals, $rbac_permission_names;
746 setup_rbac_strings () ;
749 // Optimization - save array so it is only built once per page view
751 if (!isset($role_vals[$section])) {
753 for ($i=0; $i<count($this->role_values[$section]); $i++) {
755 // Build an associative array of these key values + localized description
757 $role_vals[$section][$this->role_values[$section][$i]]=$rbac_permission_names["$section".$this->role_values[$section][$i]];
760 return $role_vals[$section];
763 function hasPermission($section, $reference, $action = NULL) {
766 $value = $this->getSetting ($section, $reference) ;
773 case 'approve_projects':
775 case 'project_admin':
777 case 'tracker_admin':
780 return ($value >= 1) ;
786 return ($value >= 1) ;
789 return ($value >= 2) ;
797 return ($value >= 1) ;
800 return ($value >= 2) ;
808 return ($value >= 1) ;
811 return ($value >= 2) ;
814 return ($value >= 3) ;
817 return ($value >= 4) ;
825 return ($value >= 1) ;
828 return ($value >= 2) ;
831 return ($value >= 3) ;
840 return ($value >= 1) ;
843 return ($value >= 2) ;
845 case 'unmoderated_post':
846 return ($value >= 3) ;
849 return ($value >= 4) ;
858 return (($value & 1) != 0) ;
861 return (($value & 2) != 0) ;
864 return (($value & 4) != 0) ;
873 return (($value & 1) != 0) ;
876 return (($value & 2) != 0) ;
879 return (($value & 4) != 0) ;
884 $hook_params = array ();
885 $hook_params['section'] = $section ;
886 $hook_params['reference'] = $reference ;
887 $hook_params['action'] = $action ;
888 $hook_params['value'] = $value ;
889 $hook_params['result'] = false ;
890 plugin_hook_by_reference ("role_has_permission", $hook_params);
891 return $hook_params['result'] ;
897 * update - update a role in the database.
899 * @param string The name of the role.
900 * @param array A multi-dimensional array of data in this format: $data['section_name']['ref_id']=$val
901 * @param boolean Perform permission checking
902 * @return boolean True on success or false on failure.
904 function update($role_name,$data,$check_perms=true) {
908 if ($this->getHomeProject() == NULL) {
909 if (!forge_check_global_perm ('forge_admin')) {
910 $this->setPermissionDeniedError();
913 } elseif (!forge_check_perm ('project_admin', $this->getHomeProject()->getID())) {
914 $this->setPermissionDeniedError();
919 $perm =& $this->Group->getPermission ();
920 if (!$perm || !is_object($perm) || $perm->isError() || !$perm->isAdmin()) {
921 $this->setPermissionDeniedError();
925 // Cannot update role_id=1
927 if ($this->getID() == 1) {
928 $this->setError('Cannot Update Default Role');
937 if ($role_name != $this->getName()) {
938 $this->setName($role_name) ;
941 foreach ($data as $sect => $refs) {
942 foreach ($refs as $refid => $value) {
943 $this->setSetting ($sect, $refid, $value) ;
945 if ($sect == 'scm') {
946 foreach ($this->getUsers() as $u) {
947 if (!$SYS->sysGroupCheckUser($refid,$u->getID())) {
948 $this->setError($SYS->getErrorMessage());
956 if (! $this->setName($role_name)) {
961 // Delete extra settings
962 db_query_params ('DELETE FROM role_setting WHERE role_id=$1 AND section_name <> ALL ($2)',
963 array ($this->getID(),
964 db_string_array_to_any_clause (array_keys ($this->role_values)))) ;
965 db_query_params ('DELETE FROM role_setting WHERE role_id=$1 AND section_name = $2 AND ref_id <> ALL ($3)',
966 array ($this->getID(),
968 db_int_array_to_any_clause (array_keys ($data['tracker'])))) ;
969 db_query_params ('DELETE FROM role_setting WHERE role_id=$1 AND section_name = $2 AND ref_id <> ALL ($3)',
970 array ($this->getID(),
972 db_int_array_to_any_clause (array_keys ($data['forum'])))) ;
973 db_query_params ('DELETE FROM role_setting WHERE role_id=$1 AND section_name = $2 AND ref_id <> ALL ($3)',
974 array ($this->getID(),
976 db_int_array_to_any_clause (array_keys ($data['pm'])))) ;
987 ////$data['section_name']['ref_id']=$val
988 $arr1 = array_keys($data);
989 for ($i=0; $i<count($arr1); $i++) {
990 // array_values($Report->adjust_days)
991 $arr2 = array_keys($data[$arr1[$i]]);
992 for ($j=0; $j<count($arr2); $j++) {
993 $usection_name=$arr1[$i];
995 $uvalue=$data[$usection_name][$uref_id];
1003 // See if this setting changed. If so, then update it
1005 // if ($this->getVal($usection_name,$uref_id) != $uvalue) {
1006 $res = db_query_params ('UPDATE role_setting SET value=$1 WHERE role_id=$2 AND section_name=$3 AND ref_id=$4',
1011 if (!$res || db_affected_rows($res) < 1) {
1012 $res = db_query_params ('INSERT INTO role_setting (role_id, section_name, ref_id, value) VALUES ($1, $2, $3, $4)',
1013 array ($this->getID(),
1018 $this->setError('update::rolesettinginsert::'.db_error());
1023 if ($usection_name == 'frs') {
1024 $update_usergroup=true;
1025 } elseif ($usection_name == 'scm') {
1026 //$update_usergroup=true;
1028 //iterate all users with this role
1029 $res = db_query_params ('SELECT user_id FROM user_group WHERE role_id=$1',
1030 array ($this->getID())) ;
1031 for ($z=0; $z<db_numrows($res); $z++) {
1033 //TODO - Shell should be separate flag
1034 // If user acquired admin access to CVS,
1035 // one to be given normal shell on CVS machine,
1036 // else - restricted.
1038 $cvs_flags=$data['scm'][0];
1039 $res2 = db_query_params ('UPDATE user_group SET cvs_flags=$1 WHERE user_id=$2',
1041 db_result($res,$z,'user_id')));
1043 $this->setError('update::scm::'.db_error());
1047 // I have doubt the following is usefull
1048 // This is probably buggy if used
1050 if (!$SYS->sysUserSetAttribute(db_result($res,$z,'user_id'),"debGforgeCvsShell","/bin/bash")) {
1051 $this->setError($SYS->getErrorMessage());
1056 if (!$SYS->sysUserSetAttribute(db_result($res,$z,'user_id'),"debGforgeCvsShell","/bin/cvssh")) {
1057 $this->setError($SYS->getErrorMessage());
1064 // If user acquired at least commit access to CVS,
1065 // one to be promoted to CVS group, else, demoted.
1068 if (!$SYS->sysGroupAddUser($this->Group->getID(),db_result($res,$z,'user_id'),1)) {
1069 $this->setError($SYS->getErrorMessage());
1074 if (!$SYS->sysGroupRemoveUser($this->Group->getID(),db_result($res,$z,'user_id'),1)) {
1075 $this->setError($SYS->getErrorMessage());
1083 } elseif ($usection_name == 'docman') {
1084 $update_usergroup=true;
1085 } elseif ($usection_name == 'forumadmin') {
1086 $update_usergroup=true;
1087 } elseif ($usection_name == 'trackeradmin') {
1088 $update_usergroup=true;
1089 } elseif ($usection_name == 'projectadmin') {
1090 $update_usergroup=true;
1091 } elseif ($usection_name == 'pmadmin') {
1092 $update_usergroup=true;
1097 // if ($update_usergroup) {
1098 $keys = array ('forumadmin', 'pmadmin', 'trackeradmin', 'docman', 'scm', 'frs', 'projectadmin') ;
1099 foreach ($keys as $k) {
1100 if (!array_key_exists ($k, $data)) {
1101 $data[$k] = array(0);
1104 $res = db_query_params ('UPDATE user_group
1113 array ($data['projectadmin'][0],
1114 $data['forumadmin'][0],
1115 $data['pmadmin'][0],
1119 $data['trackeradmin'][0],
1122 $this->setError('::update::usergroup::'.db_error());
1131 $hook_params = array ();
1132 $hook_params['role'] =& $this;
1133 $hook_params['role_id'] = $this->getID();
1134 $hook_params['data'] = $data;
1135 plugin_hook ("role_update", $hook_params);
1139 $this->fetchData($this->getID());
1143 function getDisplayableName($group = NULL) {
1144 if ($this->getHomeProject() == NULL) {
1145 return sprintf (_('%s (global role)'),
1146 $this->getName ()) ;
1147 } elseif ($group == NULL
1148 || $this->getHomeProject()->getID() != $group->getID()) {
1149 return sprintf (_('%s (in project %s)'),
1151 $this->getHomeProject()->getPublicName()) ;
1153 return $this->getName () ;
1157 function removeObsoleteSettings () {
1160 // Remove obsolete project-wide settings
1161 $sections = array ('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm', 'webcal') ;
1162 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)',
1163 array ($this->getID(),
1164 db_string_array_to_any_clause($sections))) ;
1167 // Remove obsolete settings for multiple-instance tools
1168 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))',
1169 array ($this->getID(),
1171 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))',
1172 array ($this->getID(),
1174 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))',
1175 array ($this->getID(),
1182 function normalizeDataForSection (&$new_sa, $section) {
1183 if (array_key_exists ($section, $this->setting_array)) {
1184 $new_sa[$section][0] = $this->setting_array[$section][0] ;
1185 } elseif (array_key_exists ($this->data_array['role_name'], $this->defaults)
1186 && array_key_exists ($section, $this->defaults[$this->data_array['role_name']])) {
1187 $new_sa[$section][0] = $this->defaults[$this->data_array['role_name']][$section] ;
1189 $new_sa[$section][0] = 0 ;
1194 function normalizePermsForSection (&$new_pa, $section, $refid) {
1195 if (array_key_exists ($section, $this->perms_array)
1196 && array_key_exists ($refid, $this->perms_array[$section])) {
1197 $new_pa[$section][$refid] = $this->perms_array[$section][$refid] ;
1198 } elseif (array_key_exists ($this->data_array['role_name'], $this->defaults)
1199 && array_key_exists ($section, $this->defaults[$this->data_array['role_name']])) {
1200 $new_pa[$section][$refid] = $this->defaults[$this->data_array['role_name']][$section] ;
1202 $new_pa[$section][$refid] = 0 ;
1207 function normalizeData () { // From the PFO spec
1208 $this->removeObsoleteSettings () ;
1210 $this->fetchData ($this->getID()) ;
1212 $projects = $this->getLinkedProjects() ;
1213 $new_sa = array () ;
1214 $new_pa = array () ;
1216 // Add missing settings
1217 // ...project-wide settings
1219 $arr = array ('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm', 'webcal') ;
1220 foreach ($projects as $p) {
1221 foreach ($arr as $section) {
1222 $this->normalizePermsForSection ($new_pa, $section, $p->getID()) ;
1225 $this->normalizePermsForSection ($new_pa, 'forge_admin', -1) ;
1226 $this->normalizePermsForSection ($new_pa, 'approve_projects', -1) ;
1227 $this->normalizePermsForSection ($new_pa, 'approve_news', -1) ;
1228 $this->normalizePermsForSection ($new_pa, 'forge_stats', -1) ;
1230 $arr = array ('projectadmin', 'frs', 'scm', 'docman', 'forumadmin', 'trackeradmin', 'newtracker', 'pmadmin', 'newpm', 'webcal') ;
1231 foreach ($arr as $section) {
1232 $this->normalizeDataForSection ($new_sa, $section) ;
1236 $hook_params = array ();
1237 $hook_params['role'] =& $this;
1238 $hook_params['new_sa'] =& $new_sa ;
1239 $hook_params['new_pa'] =& $new_pa ;
1240 plugin_hook ("role_normalize", $hook_params);
1242 // ...tracker-related settings
1243 $new_sa['tracker'] = array () ;
1244 $new_pa['tracker'] = array () ;
1245 foreach ($projects as $p) {
1246 $atf = new ArtifactTypeFactory ($p) ;
1247 $trackerids = $atf->getAllArtifactTypeIds () ;
1248 foreach ($trackerids as $tid) {
1250 if (array_key_exists ('tracker', $this->perms_array)
1251 && array_key_exists ($tid, $this->perms_array['tracker']) ) {
1252 $new_pa['tracker'][$tid] = $this->perms_array['tracker'][$tid] ;
1253 } elseif (array_key_exists ('new_tracker', $this->perms_array)
1254 && array_key_exists ($p->getID(), $this->perms_array['new_tracker']) ) {
1255 $new_pa['tracker'][$tid] = $new_pa['new_tracker'][$p->getID()] ;
1258 if (array_key_exists ('tracker', $this->setting_array)
1259 && array_key_exists ($tid, $this->setting_array['tracker']) ) {
1260 $new_sa['tracker'][$tid] = $this->setting_array['tracker'][$tid] ;
1262 $new_sa['tracker'][$tid] = $new_sa['newtracker'][0] ;
1268 // ...forum-related settings
1269 $new_sa['forum'] = array () ;
1270 $new_pa['forum'] = array () ;
1271 foreach ($projects as $p) {
1272 $ff = new ForumFactory ($p) ;
1273 $fids = $ff->getAllForumIds () ;
1274 foreach ($fids as $fid) {
1276 if (array_key_exists ('forum', $this->perms_array)
1277 && array_key_exists ($fid, $this->perms_array['forum']) ) {
1278 $new_pa['forum'][$fid] = $this->perms_array['forum'][$fid] ;
1279 } elseif (array_key_exists ('new_forum', $this->perms_array)
1280 && array_key_exists ($p->getID(), $this->perms_array['new_forum']) ) {
1281 $new_pa['forum'][$fid] = $new_pa['new_forum'][$p->getID()] ;
1284 if (array_key_exists ('forum', $this->setting_array)
1285 && array_key_exists ($fid, $this->setting_array['forum']) ) {
1286 $new_sa['forum'][$fid] = $this->setting_array['forum'][$fid] ;
1288 $new_sa['forum'][$fid] = $new_sa['newforum'][0] ;
1294 // ...pm-related settings
1295 $new_sa['pm'] = array () ;
1296 $new_pa['pm'] = array () ;
1297 foreach ($projects as $p) {
1298 $pgf = new ProjectGroupFactory ($p) ;
1299 $pgids = $pgf->getAllProjectGroupIds () ;
1300 foreach ($pgids as $gid) {
1302 if (array_key_exists ('pm', $this->perms_array)
1303 && array_key_exists ($gid, $this->perms_array['pm']) ) {
1304 $new_pa['pm'][$gid] = $this->perms_array['pm'][$gid] ;
1305 } elseif (array_key_exists ('new_pm', $this->perms_array)
1306 && array_key_exists ($p->getID(), $this->perms_array['new_pm']) ) {
1307 $new_pa['pm'][$gid] = $new_pa['new_pm'][$p->getID()] ;
1310 if (array_key_exists ('pm', $this->setting_array)
1311 && array_key_exists ($gid, $this->setting_array['pm']) ) {
1312 $new_sa['pm'][$gid] = $this->setting_array['pm'][$gid] ;
1314 $new_sa['pm'][$gid] = $new_sa['newpm'][0] ;
1322 $this->update ($this->getName(), $new_pa, false) ;
1324 $this->update ($this->getName(), $new_sa) ;
1332 abstract class RoleExplicit extends BaseRole implements PFO_RoleExplicit {
1333 public function addUsers ($users) {
1337 foreach ($users as $user) {
1338 $ids[] = $user->getID() ;
1341 $already_there = array () ;
1342 $res = db_query_params ('SELECT user_id FROM pfo_user_role WHERE user_id=ANY($1) AND role_id=$2',
1343 array (db_int_array_to_any_clause($ids), $this->getID())) ;
1344 while ($arr = db_fetch_array($res)) {
1345 $already_there[] = $arr['user_id'] ;
1348 foreach ($ids as $id) {
1349 if (!in_array ($id, $already_there)) {
1350 db_query_params ('INSERT INTO pfo_user_role (user_id, role_id) VALUES ($1, $2)',
1356 foreach ($this->getLinkedProjects() as $p) {
1357 foreach ($ids as $uid) {
1358 $SYS->sysGroupCheckUser($p->getID(),$uid) ;
1363 public function addUser ($user) {
1364 return $this->addUsers (array ($user)) ;
1367 public function removeUsers($users) {
1371 foreach ($users as $user) {
1372 $ids[] = $user->getID() ;
1375 $already_there = array () ;
1376 $res = db_query_params ('DELETE FROM pfo_user_role WHERE user_id=ANY($1) AND role_id=$2',
1377 array (db_int_array_to_any_clause($ids), $this->getID())) ;
1379 foreach ($this->getLinkedProjects() as $p) {
1380 foreach ($ids as $uid) {
1381 $SYS->sysGroupCheckUser($p->getID(),$uid) ;
1388 public function removeUser ($user) {
1389 return $this->removeUsers (array ($user)) ;
1392 public function getUsers() {
1393 $result = array () ;
1394 $res = db_query_params ('SELECT user_id FROM pfo_user_role WHERE role_id=$1',
1395 array ($this->getID())) ;
1396 while ($arr = db_fetch_array($res)) {
1397 $result[] = user_get_object ($arr['user_id']) ;
1403 public function hasUser($user) {
1404 $res = db_query_params ('SELECT user_id FROM pfo_user_role WHERE user_id=$1 AND role_id=$2',
1405 array (db_int_array_to_any_clause($user->getID()), $this->getID())) ;
1406 if ($res && $db_numrows($res)) {
1413 function getID() { // From the PFO spec
1414 return $this->data_array['role_id'];
1417 function getName() { // From the PFO spec
1418 return $this->data_array['role_name'];
1422 class RoleAnonymous extends BaseRole implements PFO_RoleAnonymous {
1423 // This role is implemented as a singleton
1424 private static $_instance ;
1426 public static function getInstance() {
1427 if (isset(self::$_instance)) {
1428 return self::$_instance ;
1432 self::$_instance = new $c ;
1434 $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',
1435 array ('PFO_RoleAnonymous')) ;
1436 if (!$res || !db_numrows($res)) {
1437 throw new Exception ("No PFO_RoleAnonymous role in the database") ;
1439 self::$_instance->_role_id = db_result ($res, 0, 'role_id') ;
1441 $hook_params = array ();
1442 $hook_params['role'] =& self::$_instance;
1443 plugin_hook ("role_get", $hook_params);
1445 self::$_instance->fetchData (self::$_instance->_role_id) ;
1447 return self::$_instance ;
1450 public function getID () {
1451 return $this->_role_id ;
1453 public function isPublic () {
1456 public function setPublic ($flag) {
1457 throw new Exception ("Can't setPublic() on RoleAnonymous") ;
1459 public function getHomeProject () {
1462 public function getName () {
1463 return _('Anonymous/not logged in') ;
1465 public function setName ($name) {
1466 throw new Exception ("Can't setName() on RoleAnonymous") ;
1470 class RoleLoggedIn extends BaseRole implements PFO_RoleLoggedIn {
1471 // This role is implemented as a singleton
1472 private static $_instance ;
1474 public static function getInstance() {
1475 if (isset(self::$_instance)) {
1476 return self::$_instance ;
1480 self::$_instance = new $c ;
1482 $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',
1483 array ('PFO_RoleLoggedIn')) ;
1484 if (!$res || !db_numrows($res)) {
1485 throw new Exception ("No PFO_RoleLoggedIn role in the database") ;
1487 self::$_instance->_role_id = db_result ($res, 0, 'role_id') ;
1489 $hook_params = array ();
1490 $hook_params['role'] =& self::$_instance;
1491 plugin_hook ("role_get", $hook_params);
1493 self::$_instance->fetchData (self::$_instance->_role_id) ;
1495 return self::$_instance ;
1498 public function getID () {
1499 return $this->_role_id ;
1501 public function isPublic () {
1504 public function setPublic ($flag) {
1505 throw new Exception ("Can't setPublic() on RoleLoggedIn") ;
1507 public function getHomeProject () {
1510 public function getName () {
1511 return _('Any user logged in') ;
1513 public function setName ($name) {
1514 throw new Exception ("Can't setName() on RoleLoggedIn") ;
1518 abstract class RoleUnion extends BaseRole implements PFO_RoleUnion {
1519 public function addRole ($role) {
1520 throw new Exception ("Not implemented") ;
1522 public function removeRole ($role) {
1523 throw new Exception ("Not implemented") ;
1527 class RoleComparator {
1528 var $criterion = 'composite' ;
1529 var $reference_project = NULL ;
1531 function Compare ($a, $b) {
1532 switch ($this->criterion) {
1534 return strcoll ($a->getName(), $b->getName()) ;
1537 $aid = $a->getID() ;
1538 $bid = $b->getID() ;
1542 return ($a < $b) ? -1 : 1;
1546 if ($this->reference_project == NULL) {
1547 return $this->CompareNoRef ($a, $b) ;
1549 $rpid = $this->reference_project->getID () ;
1550 $ap = $a->getHomeProject() ;
1551 $bp = $b->getHomeProject() ;
1552 $a_is_local = ($ap != NULL && $ap->getID() == $rpid) ; // Local
1553 $b_is_local = ($bp != NULL && $bp->getID() == $rpid) ;
1555 if ($a_is_local && !$b_is_local) {
1557 } elseif (!$a_is_local && $b_is_local) {
1560 return $this->CompareNoRef ($a, $b) ;
1564 function CompareNoRef ($a, $b) {
1565 $ap = $a->getHomeProject() ;
1566 $bp = $b->getHomeProject() ;
1567 if ($ap == NULL && $bp != NULL) {
1569 } elseif ($ap != NULL && $bp == NULL) {
1571 } elseif ($ap == NULL && $bp == NULL) {
1572 $tmp = strcoll ($a->getName(), $b->getName()) ;
1575 $projcmp = new ProjectComparator () ;
1576 $projcmp->criterion = 'name' ;
1577 $tmp = $projcmp->Compare ($ap, $bp) ;
1578 if ($tmp) { /* Different projects, sort accordingly */
1581 return strcoll ($a->getName(), $b->getName()) ;
1586 function sortRoleList (&$list, $relative_to = NULL, $criterion='composite') {
1587 $cmp = new RoleComparator () ;
1588 $cmp->criterion = $criterion ;
1589 $cmp->reference_project = $relative_to ;
1591 return usort ($list, array ($cmp, 'Compare')) ;
1596 // c-file-style: "bsd"