3 * FusionForge role-based access control
5 * Copyright 2004, GForge, LLC
6 * Copyright 2009-2010, Roland Mas
8 * This file is part of FusionForge.
10 * FusionForge is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published
12 * by the Free Software Foundation; either version 2 of the License,
13 * or (at your option) any later version.
15 * FusionForge is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with FusionForge; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 require "PFO-RBAC.interface.php" ;
28 if (file_exists ('/tmp/fusionforge-use-pfo-rbac')) {
29 define ('USE_PFO_RBAC', true) ;
31 define ('USE_PFO_RBAC', false) ;
34 // Code shared between classes
36 abstract class BaseRole extends Error {
41 public function BaseRole() {
43 $this->role_values = array (
44 'forge_admin' => array (0,1),
45 'approve_projects' => array (0,1),
46 'approve_news' => array (0,1),
47 'forge_stats' => array (0,1,2),
49 'project_read' => array (0,1),
50 'project_admin' => array (0,1),
52 'tracker_admin' => array (0,1),
53 'pm_admin' => array (0,1),
54 'forum_admin' => array (0,1),
56 'tracker' => array (0,1,3,5,7),
57 'pm' => array (0,1,3,5,7),
58 'forum' => array (0,1,2,3,4),
60 'new_tracker' => array (0,1,3,5,7),
61 'new_pm' => array (0,1,3,5,7),
62 'new_forum' => array (0,1,2,3,4),
64 'scm' => array (0,1,2),
65 'docman' => array (0,1,2,3,4),
66 'frs' => array (0,1,2,3),
68 'webcal' => array (0,1,2),
71 $this->global_settings = array (
78 $this->defaults = array(
79 'Admin' => array( 'project_admin'=> 1,
92 'Senior Developer' => array( 'project_read' => 1,
104 'Junior Developer' => array( 'project_read' => 1,
113 'Doc Writer' => array( 'project_read' => 1,
121 'Support Tech' => array( 'project_read' => 1,
125 'tracker_admin' => 1,
133 $this->role_values = array(
134 'projectadmin' => array ('0','A'),
135 'frs' => array ('0','1'),
136 'scm' => array ('-1','0','1'),
137 'docman' => array ('0','1'),
138 'forumadmin' => array ('0','2'),
139 'forum' => array ('-1','0','1','2'),
140 'newforum' => array ('-1','0','1','2'),
141 'trackeradmin' => array ('0','2'),
142 'tracker' => array ('-1','0','1','2','3'),
143 'newtracker' => array ('-1','0','1','2','3'),
144 'pmadmin' => array ('0','2'),
145 'pm' => array ('-1','0','1','2','3'),
146 'newpm' => array ('-1','0','1','2','3'),
147 'webcal' => array ('0','1','2'));
149 $this->defaults = array(
150 'Admin' => array( 'projectadmin'=>'A',
164 'Senior Developer'=> array( 'projectadmin'=>'0',
178 'Junior Developer'=> array( 'projectadmin'=>'0',
192 'Doc Writer' => array( 'projectadmin'=>'0',
206 'Support Tech' => array( 'projectadmin'=>'0',
225 public function getUsers() {
228 public function hasUser($user) {
229 throw new Exception ("Not implemented") ;
231 function hasGlobalPermission($section, $action = NULL) {
232 return $this->hasPermission ($section, -1, $action) ;
234 public function getSettings() {
235 throw new Exception ("Not implemented") ;
237 public function setSettings($data) {
238 throw new Exception ("Not implemented") ;
240 public function delete () {
241 throw new Exception ("Not implemented") ;
245 * getLinkedProjects - List of projects referencing that role
247 * Includes the home project (for roles that have one)
249 * @return array Array of Group objects
251 public function getLinkedProjects () {
254 $hp = $this->getHomeProject () ;
256 $ids[] = $hp->getID() ;
259 $res = db_query_params ('SELECT group_id FROM role_project_refs WHERE role_id=$1',
260 array ($this->getID())) ;
262 while ($arr = db_fetch_array ($res)) {
263 $ids[] = $arr['group_id'] ;
267 return group_get_objects (array_unique ($ids)) ;
270 function linkProject ($project) { // From the PFO spec
271 $hp = $this->getHomeProject () ;
272 if ($hp != NULL && $hp->getID() == $project->getID()) {
273 $this->setError ("Can't link to home project") ;
277 $res = db_query_params('SELECT group_id FROM role_project_refs WHERE role_id=$1 AND group_id=$2',
278 array ($this->getID(),
281 if (db_numrows($res)) {
284 $res = db_query_params('INSERT INTO role_project_refs (role_id, group_id) VALUES ($1, $2)',
285 array ($this->getID(),
287 if (!$res || db_affected_rows($res) < 1) {
288 $this->setError('linkProject('.$project->getID().') '.db_error());
295 function unlinkProject ($project) { // From the PFO spec
296 $hp = $this->getHomeProject () ;
297 if ($hp != NULL && $hp->getID() == $project->getID()) {
298 $this->setError ("Can't unlink from home project") ;
302 $res = db_query_params('DELETE FROM role_project_refs WHERE role_id=$1 AND group_id=$2',
303 array ($this->getID(),
306 $this->setError('unlinkProject('.$project->getID().') '.db_error());
310 $this->removeObsoleteSettings () ;
316 * fetchData - May need to refresh database fields.
318 * If an update occurred and you need to access the updated info.
320 * @return boolean success;
322 function fetchData($role_id) {
323 unset($this->data_array);
324 unset($this->setting_array);
325 unset($this->perms_array);
328 $res = db_query_params ('SELECT * FROM pfo_role WHERE role_id=$1',
330 if (!$res || db_numrows($res) < 1) {
331 $this->setError('BaseRole::fetchData()::'.db_error());
334 $this->data_array = db_fetch_array($res);
335 if ($this->data_array['is_public'] == 't') {
336 $this->data_array['is_public'] = true ;
338 $this->data_array['is_public'] = false ;
340 $res = db_query_params ('SELECT section_name, ref_id, perm_val FROM pfo_role_setting WHERE role_id=$1',
343 $this->setError('BaseRole::fetchData()::'.db_error());
346 $this->perms_array=array();
347 while ($arr = db_fetch_array($res)) {
348 $this->perms_array[$arr['section_name']][$arr['ref_id']] = $arr['perm_val'];
351 if ($this instanceof RoleAnonymous) {
352 $res = db_query_params ('SELECT group_id, enable_anonscm FROM groups WHERE is_public=1',
354 while ($arr = db_fetch_array($res)) {
355 $this->perms_array['project_read'][$arr['group_id']] = 1 ;
356 $this->perms_array['frs'][$arr['group_id']] = 1 ;
357 $this->perms_array['scm'][$arr['group_id']] = $arr['enable_anonscm'] ;
360 $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',
362 while ($arr = db_fetch_array($res)) {
363 $this->perms_array['tracker'][$arr['group_artifact_id']] = 1 ;
366 $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',
368 while ($arr = db_fetch_array($res)) {
369 $this->perms_array['pm'][$arr['group_project_id']] = 1 ;
372 $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',
374 while ($arr = db_fetch_array($res)) {
375 if ($arr['allow_anonymous'] == 1) {
376 if ($arr['moderation_level'] == 0) {
377 $this->perms_array['forum'][$arr['group_forum_id']] = 3 ;
379 $this->perms_array['forum'][$arr['group_forum_id']] = 2 ;
382 $this->perms_array['forum'][$arr['group_forum_id']] = 1 ;
385 } elseif ($this instanceof RoleLoggedIn) {
386 $res = db_query_params ('SELECT group_id, enable_anonscm FROM groups WHERE is_public=1',
388 while ($arr = db_fetch_array($res)) {
389 $this->perms_array['project_read'][$arr['group_id']] = 1 ;
390 $this->perms_array['frs'][$arr['group_id']] = 1 ;
391 $this->perms_array['scm'][$arr['group_id']] = $arr['enable_anonscm'] ;
394 $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',
396 while ($arr = db_fetch_array($res)) {
397 $this->perms_array['tracker'][$arr['group_artifact_id']] = 1 ;
400 $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',
402 while ($arr = db_fetch_array($res)) {
403 $this->perms_array['pm'][$arr['group_project_id']] = 1 ;
406 $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',
408 while ($arr = db_fetch_array($res)) {
409 if ($arr['moderation_level'] == 0) {
410 $this->perms_array['forum'][$arr['group_forum_id']] = 3 ;
412 $this->perms_array['forum'][$arr['group_forum_id']] = 2 ;
416 $res = db_query_params ('SELECT * FROM role WHERE role_id=$1',
418 if (!$res || db_numrows($res) < 1) {
419 $this->setError('BaseRole::fetchData()::'.db_error());
422 $this->data_array = db_fetch_array($res);
424 // Load pre-PFO RBAC settings...
425 $res = db_query_params ('SELECT * FROM role_setting WHERE role_id=$1',
428 $this->setError('BaseRole::fetchData()::'.db_error());
431 $this->setting_array=array();
432 while ($arr = db_fetch_array($res)) {
433 $this->setting_array[$arr['section_name']][$arr['ref_id']] = $arr['value'];
436 // ...and map section names and values to the new values
438 if ($this->data_array['group_id'] == forge_get_config ('stats_group')) {
439 $this->perms_array['forge_stats'][-1] = 2 ;
442 $this->perms_array=array();
443 $tohandle = array () ;
444 $gid = $this->data_array['group_id'] ;
445 if ($gid == 1 && count ($this->setting_array) == 0) {
446 $tohandle[] = array ('forge_admin', -1) ;
448 foreach ($this->setting_array as $oldsection => $t) {
449 switch ($oldsection) {
451 $tohandle[] = array ('project_admin', $gid) ;
452 if ($this->data_array['group_id'] == 1 && $t[0] == 'A') {
453 $tohandle[] = array ('forge_admin', -1) ;
455 if ($this->data_array['group_id'] == forge_get_config ('news_group') && $t[0] == 'A') {
456 $tohandle[] = array ('approve_news', -1) ;
458 if ($this->data_array['group_id'] == forge_get_config ('stats_group') && $t[0] == 'A') {
459 $tohandle[] = array ('forge_stats', -1) ;
463 $tohandle[] = array ('tracker_admin', $gid) ;
466 $tohandle[] = array ('pm_admin', $gid) ;
469 $tohandle[] = array ('forum_admin', $gid) ;
473 $tohandle[] = array ('new_tracker', $gid) ;
476 $tohandle[] = array ('new_pm', $gid) ;
479 $tohandle[] = array ('new_forum', $gid) ;
483 foreach ($t as $oldreference => $oldvalue) {
484 $tohandle[] = array ($oldsection, $oldreference) ;
490 foreach ($tohandle as $t) {
494 $res = db_query_params ('SELECT pfo_rbac_permissions_from_old($1,$2,$3)',
495 array ($role_id, $nsec, $nref)) ;
497 $arr = db_fetch_array($res) ;
498 $this->perms_array[$nsec][$nref] = $arr[0] ;
501 } // Explicit role (not Anonymous or LoggedIn)
507 function setSetting ($section, $reference, $value) {
508 $role_id = $this->getID () ;
510 $res = db_query_params ('DELETE FROM pfo_role_setting WHERE role_id=$1 AND section_name=$2 AND ref_id=$3',
515 $res = db_query_params ('INSERT INTO pfo_role_setting (role_id, section_name, ref_id, perm_val) VALUES ($1, $2, $3, $4)',
522 function getSettingsForProject ($project) {
524 $group_id = $project->getID() ;
527 $sections = array ('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm', 'webcal') ;
528 foreach ($sections as $section) {
529 $result[$section][$group_id] = $this->getVal ($section, $group_id) ;
532 $sections = array ('projectadmin', 'frs', 'scm', 'docman', 'trackeradmin', 'newtracker', 'forumadmin', 'newforum', 'pmadmin', 'newpm', 'webcal') ;
533 foreach ($sections as $section) {
534 $result[$section][0] = $this->getVal ($section, 0) ;
538 $atf = new ArtifactTypeFactory ($project) ;
539 $tids = $atf->getAllArtifactTypeIds () ;
540 foreach ($tids as $tid) {
541 $result['tracker'][$tid] = $this->getVal ('tracker', $tid) ;
544 $ff = new ForumFactory ($project) ;
545 $fids = $ff->getAllForumIds () ;
546 foreach ($fids as $fid) {
547 $result['forum'][$fid] = $this->getVal ('forum', $fid) ;
550 $pgf = new ProjectGroupFactory ($project) ;
551 $pgids = $pgf->getAllProjectGroupIds () ;
552 foreach ($pgids as $pgid) {
553 $result['pm'][$pgid] = $this->getVal ('pm', $pgid) ;
558 // Add settings not yet listed so far (probably plugins)
559 // Currently handled:
560 // - global settings (ignored here)
561 // - project-wide settings (core and plugins)
562 // - settings for multiple-instance tools coming from the core (trackers/pm/forums)
564 // - settings for multiple-instance tools from plugins
565 foreach (array_keys ($this->perms_array) as $section) {
566 if (!in_array ($section, $sections)) {
567 if (!in_array ($section, $this->global_settings)) {
568 $result[$section][$group_id] = $this->getVal ($section, $group_id) ;
577 function getGlobalSettings () {
580 $sections = array ('forge_admin', 'forge_stats', 'approve_projects', 'approve_news') ;
581 foreach ($sections as $section) {
582 $result[$section][-1] = $this->getVal ($section, -1) ;
584 // Add settings not yet listed so far (probably plugins)
585 foreach (array_keys ($this->perms_array) as $section) {
586 if (!in_array ($section, $sections)) {
587 if (in_array ($section, $this->global_settings)) {
588 $result[$section][-1] = $this->getVal ($section, -1) ;
596 function getSetting($section, $reference) {
597 if (isset ($this->perms_array[$section][$reference])) {
598 $value = $this->perms_array[$section][$reference] ;
611 case 'approve_projects':
613 if ($this->hasGlobalPermission('forge_admin')) {
620 if ($this->hasGlobalPermission('forge_admin')) {
626 case 'project_admin':
627 if ($this->hasGlobalPermission('forge_admin')) {
634 case 'tracker_admin':
637 if ($this->hasPermission('project_admin', $reference)) {
644 if ($this->hasPermission('project_admin', $reference)) {
651 if ($this->hasPermission('project_admin', $reference)) {
658 if ($this->hasPermission('project_admin', $reference)) {
665 if ($this->hasPermission('forum_admin', forum_get_groupid($reference))) {
671 if ($this->hasPermission('forum_admin', $reference)) {
678 if ($this->hasPermission('tracker_admin', artifacttype_get_groupid($reference))) {
684 if ($this->hasPermission('tracker_admin', $reference)) {
691 if ($this->hasPermission('pm_admin', projectgroup_get_groupid($reference))) {
697 if ($this->hasPermission('pm_admin', $reference)) {
706 * getVal - get a value out of the array of settings for this role.
708 * @param string The name of the role.
709 * @param integer The ref_id (ex: group_artifact_id, group_forum_id) for this item.
710 * @return integer The value of this item.
712 function getVal($section,$ref_id) {
713 global $role_default_array;
718 return $this->getSetting ($section, $ref_id) ;
720 if (array_key_exists ($section, $this->setting_array)) {
721 return $this->setting_array[$section][$ref_id];
728 function setVal($section, $ref_id, $value) {
729 $this->setting_array[$section][$ref_id] = $value;
730 return $this->update( $this->getName(), $this->setting_array);
734 * &getRoleVals - get all the values and language text strings for this section.
736 * @return array Assoc array of values for this section.
738 function &getRoleVals($section) {
739 global $role_vals, $rbac_permission_names;
740 setup_rbac_strings () ;
743 // Optimization - save array so it is only built once per page view
745 if (!isset($role_vals[$section])) {
747 for ($i=0; $i<count($this->role_values[$section]); $i++) {
749 // Build an associative array of these key values + localized description
751 $role_vals[$section][$this->role_values[$section][$i]]=$rbac_permission_names["$section".$this->role_values[$section][$i]];
754 return $role_vals[$section];
757 function hasPermission($section, $reference, $action = NULL) {
760 $value = $this->getSetting ($section, $reference) ;
767 case 'approve_projects':
769 case 'project_admin':
771 case 'tracker_admin':
774 return ($value >= 1) ;
780 return ($value >= 1) ;
783 return ($value >= 2) ;
791 return ($value >= 1) ;
794 return ($value >= 2) ;
802 return ($value >= 1) ;
805 return ($value >= 2) ;
808 return ($value >= 3) ;
811 return ($value >= 4) ;
819 return ($value >= 1) ;
822 return ($value >= 2) ;
825 return ($value >= 3) ;
834 return ($value >= 1) ;
837 return ($value >= 2) ;
839 case 'unmoderated_post':
840 return ($value >= 3) ;
843 return ($value >= 4) ;
852 return (($value & 1) != 0) ;
855 return (($value & 2) != 0) ;
858 return (($value & 4) != 0) ;
867 return (($value & 1) != 0) ;
870 return (($value & 2) != 0) ;
873 return (($value & 4) != 0) ;
881 * update - update a role in the database.
883 * @param string The name of the role.
884 * @param array A multi-dimensional array of data in this format: $data['section_name']['ref_id']=$val
885 * @return boolean True on success or false on failure.
887 function update($role_name,$data) {
890 if ($this->getHomeProject() == NULL) {
891 if (!forge_check_global_perm ('forge_admin')) {
892 $this->setPermissionDeniedError();
895 } elseif (!forge_check_perm ('project_admin', $this->getHomeProject()->getID())) {
896 $this->setPermissionDeniedError();
900 $perm =& $this->Group->getPermission ();
901 if (!$perm || !is_object($perm) || $perm->isError() || !$perm->isAdmin()) {
902 $this->setPermissionDeniedError();
906 // Cannot update role_id=1
908 if ($this->getID() == 1) {
909 $this->setError('Cannot Update Default Role');
918 if ($role_name != $this->getName()) {
919 $this->setName($role_name) ;
922 foreach ($data as $sect => $refs) {
923 foreach ($refs as $refid => $value) {
924 $this->setSetting ($sect, $refid, $value) ;
926 if ($sect == 'scm') {
927 foreach ($this->getUsers() as $u) {
928 if (!$SYS->sysGroupCheckUser($refid,$u->getID())) {
929 $this->setError($SYS->getErrorMessage());
937 if (! $this->setName($role_name)) {
942 // Delete extra settings
943 db_query_params ('DELETE FROM role_setting WHERE role_id=$1 AND section_name <> ALL ($2)',
944 array ($this->getID(),
945 db_string_array_to_any_clause (array_keys ($this->role_values)))) ;
946 db_query_params ('DELETE FROM role_setting WHERE role_id=$1 AND section_name = $2 AND ref_id <> ALL ($3)',
947 array ($this->getID(),
949 db_int_array_to_any_clause (array_keys ($data['tracker'])))) ;
950 db_query_params ('DELETE FROM role_setting WHERE role_id=$1 AND section_name = $2 AND ref_id <> ALL ($3)',
951 array ($this->getID(),
953 db_int_array_to_any_clause (array_keys ($data['forum'])))) ;
954 db_query_params ('DELETE FROM role_setting WHERE role_id=$1 AND section_name = $2 AND ref_id <> ALL ($3)',
955 array ($this->getID(),
957 db_int_array_to_any_clause (array_keys ($data['pm'])))) ;
968 ////$data['section_name']['ref_id']=$val
969 $arr1 = array_keys($data);
970 for ($i=0; $i<count($arr1); $i++) {
971 // array_values($Report->adjust_days)
972 $arr2 = array_keys($data[$arr1[$i]]);
973 for ($j=0; $j<count($arr2); $j++) {
974 $usection_name=$arr1[$i];
976 $uvalue=$data[$usection_name][$uref_id];
984 // See if this setting changed. If so, then update it
986 // if ($this->getVal($usection_name,$uref_id) != $uvalue) {
987 $res = db_query_params ('UPDATE role_setting SET value=$1 WHERE role_id=$2 AND section_name=$3 AND ref_id=$4',
992 if (!$res || db_affected_rows($res) < 1) {
993 $res = db_query_params ('INSERT INTO role_setting (role_id, section_name, ref_id, value) VALUES ($1, $2, $3, $4)',
994 array ($this->getID(),
999 $this->setError('update::rolesettinginsert::'.db_error());
1004 if ($usection_name == 'frs') {
1005 $update_usergroup=true;
1006 } elseif ($usection_name == 'scm') {
1007 //$update_usergroup=true;
1009 //iterate all users with this role
1010 $res = db_query_params ('SELECT user_id FROM user_group WHERE role_id=$1',
1011 array ($this->getID())) ;
1012 for ($z=0; $z<db_numrows($res); $z++) {
1014 //TODO - Shell should be separate flag
1015 // If user acquired admin access to CVS,
1016 // one to be given normal shell on CVS machine,
1017 // else - restricted.
1019 $cvs_flags=$data['scm'][0];
1020 $res2 = db_query_params ('UPDATE user_group SET cvs_flags=$1 WHERE user_id=$2',
1022 db_result($res,$z,'user_id')));
1024 $this->setError('update::scm::'.db_error());
1028 // I have doubt the following is usefull
1029 // This is probably buggy if used
1031 if (!$SYS->sysUserSetAttribute(db_result($res,$z,'user_id'),"debGforgeCvsShell","/bin/bash")) {
1032 $this->setError($SYS->getErrorMessage());
1037 if (!$SYS->sysUserSetAttribute(db_result($res,$z,'user_id'),"debGforgeCvsShell","/bin/cvssh")) {
1038 $this->setError($SYS->getErrorMessage());
1045 // If user acquired at least commit access to CVS,
1046 // one to be promoted to CVS group, else, demoted.
1049 if (!$SYS->sysGroupAddUser($this->Group->getID(),db_result($res,$z,'user_id'),1)) {
1050 $this->setError($SYS->getErrorMessage());
1055 if (!$SYS->sysGroupRemoveUser($this->Group->getID(),db_result($res,$z,'user_id'),1)) {
1056 $this->setError($SYS->getErrorMessage());
1064 } elseif ($usection_name == 'docman') {
1065 $update_usergroup=true;
1066 } elseif ($usection_name == 'forumadmin') {
1067 $update_usergroup=true;
1068 } elseif ($usection_name == 'trackeradmin') {
1069 $update_usergroup=true;
1070 } elseif ($usection_name == 'projectadmin') {
1071 $update_usergroup=true;
1072 } elseif ($usection_name == 'pmadmin') {
1073 $update_usergroup=true;
1078 // if ($update_usergroup) {
1079 $keys = array ('forumadmin', 'pmadmin', 'trackeradmin', 'docman', 'scm', 'frs', 'projectadmin') ;
1080 foreach ($keys as $k) {
1081 if (!array_key_exists ($k, $data)) {
1082 $data[$k] = array(0);
1085 $res = db_query_params ('UPDATE user_group
1094 array ($data['projectadmin'][0],
1095 $data['forumadmin'][0],
1096 $data['pmadmin'][0],
1100 $data['trackeradmin'][0],
1103 $this->setError('::update::usergroup::'.db_error());
1112 $hook_params = array ();
1113 $hook_params['role'] =& $this;
1114 $hook_params['role_id'] = $this->getID();
1115 $hook_params['data'] = $data;
1116 plugin_hook ("role_update", $hook_params);
1120 $this->fetchData($this->getID());
1124 function getDisplayableName($group = NULL) {
1125 if ($this->getHomeProject() == NULL) {
1126 return sprintf (_('%s (global role)'),
1127 $this->getName ()) ;
1128 } elseif ($group == NULL
1129 || $this->getHomeProject()->getID() != $group->getID()) {
1130 return sprintf (_('%s (in project %s)'),
1132 $this->getHomeProject()->getPublicName()) ;
1134 return $this->getName () ;
1138 function removeObsoleteSettings () {
1141 // Remove obsolete project-wide settings
1142 $sections = array ('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm', 'webcal') ;
1143 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 UNION SELECT group_id from role_project_refs WHERE role_id=$1)',
1144 array ($this->getID(),
1145 db_string_array_to_any_clause($sections))) ;
1148 // Remove obsolete settings for multiple-instance tools
1149 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 UNION SELECT group_id from role_project_refs WHERE role_id=$1))',
1150 array ($this->getID(),
1152 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 UNION SELECT group_id from role_project_refs WHERE role_id=$1))',
1153 array ($this->getID(),
1155 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 UNION SELECT group_id from role_project_refs WHERE role_id=$1))',
1156 array ($this->getID(),
1163 function normalizeDataForSection (&$new_sa, $section) {
1164 if (array_key_exists ($section, $this->setting_array)) {
1165 $new_sa[$section][0] = $this->setting_array[$section][0] ;
1166 } elseif (array_key_exists ($this->data_array['role_name'], $this->defaults)
1167 && array_key_exists ($section, $this->defaults[$this->data_array['role_name']])) {
1168 $new_sa[$section][0] = $this->defaults[$this->data_array['role_name']][$section] ;
1170 $new_sa[$section][0] = 0 ;
1175 function normalizePermsForSection (&$new_pa, $section, $refid) {
1176 if (array_key_exists ($section, $this->perms_array)
1177 && array_key_exists ($refid, $this->perms_array[$section])) {
1178 $new_pa[$section][$refid] = $this->perms_array[$section][$refid] ;
1179 } elseif (array_key_exists ($this->data_array['role_name'], $this->defaults)
1180 && array_key_exists ($section, $this->defaults[$this->data_array['role_name']])) {
1181 $new_pa[$section][$refid] = $this->defaults[$this->data_array['role_name']][$section] ;
1183 $new_pa[$section][$refid] = 0 ;
1188 function normalizeData () { // From the PFO spec
1189 $this->removeObsoleteSettings () ;
1191 $this->fetchData ($this->getID()) ;
1193 $projects = $this->getLinkedProjects() ;
1194 $new_sa = array () ;
1195 $new_pa = array () ;
1197 // Add missing settings
1198 // ...project-wide settings
1200 $arr = array ('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm', 'webcal') ;
1201 foreach ($projects as $p) {
1202 foreach ($arr as $section) {
1203 $this->normalizePermsForSection ($new_pa, $section, $p->getID()) ;
1206 $this->normalizePermsForSection ($new_pa, 'forge_admin', -1) ;
1207 $this->normalizePermsForSection ($new_pa, 'approve_projects', -1) ;
1208 $this->normalizePermsForSection ($new_pa, 'approve_news', -1) ;
1209 $this->normalizePermsForSection ($new_pa, 'forge_stats', -1) ;
1211 $arr = array ('projectadmin', 'frs', 'scm', 'docman', 'forumadmin', 'trackeradmin', 'newtracker', 'pmadmin', 'newpm', 'webcal') ;
1212 foreach ($arr as $section) {
1213 $this->normalizeDataForSection ($new_sa, $section) ;
1217 $hook_params = array ();
1218 $hook_params['role'] =& $this;
1219 $hook_params['new_sa'] =& $new_sa ;
1220 $hook_params['new_pa'] =& $new_pa ;
1221 plugin_hook ("role_normalize", $hook_params);
1223 // ...tracker-related settings
1224 $new_sa['tracker'] = array () ;
1225 $new_pa['tracker'] = array () ;
1226 foreach ($projects as $p) {
1227 $atf = new ArtifactTypeFactory ($p) ;
1228 $trackers = $atf->getArtifactTypes () ;
1229 foreach ($trackers as $t) {
1231 if (array_key_exists ('tracker', $this->perms_array)
1232 && array_key_exists ($t->getID(), $this->perms_array['tracker']) ) {
1233 $new_pa['tracker'][$t->getID()] = $this->perms_array['tracker'][$t->getID()] ;
1234 } elseif (array_key_exists ('new_tracker', $this->perms_array)
1235 && array_key_exists ($p->getID(), $this->perms_array['new_tracker']) ) {
1236 $new_pa['tracker'][$t->getID()] = $new_pa['new_tracker'][$p->getID()] ;
1239 if (array_key_exists ('tracker', $this->setting_array)
1240 && array_key_exists ($t->getID(), $this->setting_array['tracker']) ) {
1241 $new_sa['tracker'][$t->getID()] = $this->setting_array['tracker'][$t->getID()] ;
1243 $new_sa['tracker'][$t->getID()] = $new_sa['newtracker'][0] ;
1249 // ...forum-related settings
1250 $new_sa['forum'] = array () ;
1251 $new_pa['forum'] = array () ;
1252 foreach ($projects as $p) {
1253 $ff = new ForumFactory ($p) ;
1254 $forums = $ff->getForums () ;
1255 foreach ($forums as $f) {
1257 if (array_key_exists ('forum', $this->perms_array)
1258 && array_key_exists ($f->getID(), $this->perms_array['forum']) ) {
1259 $new_pa['forum'][$f->getID()] = $this->perms_array['forum'][$f->getID()] ;
1260 } elseif (array_key_exists ('new_forum', $this->perms_array)
1261 && array_key_exists ($p->getID(), $this->perms_array['new_forum']) ) {
1262 $new_pa['forum'][$f->getID()] = $new_pa['new_forum'][$p->getID()] ;
1265 if (array_key_exists ('forum', $this->setting_array)
1266 && array_key_exists ($f->getID(), $this->setting_array['forum']) ) {
1267 $new_sa['forum'][$f->getID()] = $this->setting_array['forum'][$f->getID()] ;
1269 $new_sa['forum'][$f->getID()] = $new_sa['newforum'][0] ;
1275 // ...pm-related settings
1276 $new_sa['pm'] = array () ;
1277 $new_pa['pm'] = array () ;
1278 foreach ($projects as $p) {
1279 $pgf = new ProjectGroupFactory ($p) ;
1280 $pgs = $pgf->getProjectGroups () ;
1281 foreach ($pgs as $g) {
1283 if (array_key_exists ('pm', $this->perms_array)
1284 && array_key_exists ($g->getID(), $this->perms_array['pm']) ) {
1285 $new_pa['pm'][$g->getID()] = $this->perms_array['pm'][$g->getID()] ;
1286 } elseif (array_key_exists ('new_pm', $this->perms_array)
1287 && array_key_exists ($p->getID(), $this->perms_array['new_pm']) ) {
1288 $new_pa['pm'][$g->getID()] = $new_pa['new_pm'][$p->getID()] ;
1291 if (array_key_exists ('pm', $this->setting_array)
1292 && array_key_exists ($g->getID(), $this->setting_array['pm']) ) {
1293 $new_sa['pm'][$g->getID()] = $this->setting_array['pm'][$g->getID()] ;
1295 $new_sa['pm'][$g->getID()] = $new_sa['newpm'][0] ;
1303 $this->update ($this->getName(), $new_pa) ;
1305 $this->update ($this->getName(), $new_sa) ;
1313 abstract class RoleExplicit extends BaseRole implements PFO_RoleExplicit {
1314 public function addUsers ($users) {
1318 foreach ($users as $user) {
1319 $ids[] = $user->getID() ;
1322 $already_there = array () ;
1323 $res = db_query_params ('SELECT user_id FROM pfo_user_role WHERE user_id=ANY($1) AND role_id=$2',
1324 array (db_int_array_to_any_clause($ids), $this->getID())) ;
1325 while ($arr =& db_fetch_array($res)) {
1326 $already_there[] = $arr['user_id'] ;
1329 foreach ($ids as $id) {
1330 if (!in_array ($id, $already_there)) {
1331 db_query_params ('INSERT INTO pfo_user_role (user_id, role_id) VALUES ($1, $2)',
1337 foreach ($this->getLinkedProjects() as $p) {
1338 foreach ($ids as $uid) {
1339 $SYS->sysGroupCheckUser($p->getID(),$uid) ;
1344 public function addUser ($user) {
1345 return $this->addUsers (array ($user)) ;
1348 public function removeUsers($users) {
1352 foreach ($users as $user) {
1353 $ids[] = $user->getID() ;
1356 $already_there = array () ;
1357 $res = db_query_params ('DELETE FROM pfo_user_role WHERE user_id=ANY($1) AND role_id=$2',
1358 array (db_int_array_to_any_clause($ids), $this->getID())) ;
1360 foreach ($this->getLinkedProjects() as $p) {
1361 foreach ($ids as $uid) {
1362 $SYS->sysGroupCheckUser($p->getID(),$uid) ;
1369 public function removeUser ($user) {
1370 return $this->removeUsers (array ($user)) ;
1373 public function getUsers() {
1374 $result = array () ;
1375 $res = db_query_params ('SELECT user_id FROM pfo_user_role WHERE role_id=$1',
1376 array ($this->getID())) ;
1377 while ($arr = db_fetch_array($res)) {
1378 $result[] = user_get_object ($arr['user_id']) ;
1384 public function hasUser($user) {
1385 $res = db_query_params ('SELECT user_id FROM pfo_user_role WHERE user_id=$1 AND role_id=$2',
1386 array (db_int_array_to_any_clause($user->getID()), $this->getID())) ;
1387 if ($res && $db_numrows($res)) {
1394 function getID() { // From the PFO spec
1395 return $this->data_array['role_id'];
1398 function getName() { // From the PFO spec
1399 return $this->data_array['role_name'];
1403 class RoleAnonymous extends BaseRole implements PFO_RoleAnonymous {
1404 // This role is implemented as a singleton
1405 private static $_instance ;
1407 public static function getInstance() {
1408 if (isset(self::$_instance)) {
1409 return self::$_instance ;
1413 self::$_instance = new $c ;
1415 $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',
1416 array ('PFO_RoleAnonymous')) ;
1417 if (!$res || !db_numrows($res)) {
1418 throw new Exception ("No PFO_RoleAnonymous role in the database") ;
1420 self::$_instance->_role_id = db_result ($res, 0, 'role_id') ;
1421 self::$_instance->fetchData (self::$_instance->_role_id) ;
1423 return self::$_instance ;
1426 public function getID () {
1427 return $this->_role_id ;
1429 public function isPublic () {
1432 public function setPublic ($flag) {
1433 throw new Exception ("Can't setPublic() on RoleAnonymous") ;
1435 public function getHomeProject () {
1438 public function getName () {
1439 return _('Anonymous/not logged in') ;
1441 public function setName ($name) {
1442 throw new Exception ("Can't setName() on RoleAnonymous") ;
1446 class RoleLoggedIn extends BaseRole implements PFO_RoleLoggedIn {
1447 // This role is implemented as a singleton
1448 private static $_instance ;
1450 public static function getInstance() {
1451 if (isset(self::$_instance)) {
1452 return self::$_instance ;
1456 self::$_instance = new $c ;
1458 $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',
1459 array ('PFO_RoleLoggedIn')) ;
1460 if (!$res || !db_numrows($res)) {
1461 throw new Exception ("No PFO_RoleLoggedIn role in the database") ;
1463 self::$_instance->_role_id = db_result ($res, 0, 'role_id') ;
1464 self::$_instance->fetchData (self::$_instance->_role_id) ;
1466 return self::$_instance ;
1469 public function getID () {
1470 return $this->_role_id ;
1472 public function isPublic () {
1475 public function setPublic ($flag) {
1476 throw new Exception ("Can't setPublic() on RoleLoggedIn") ;
1478 public function getHomeProject () {
1481 public function getName () {
1482 return _('Any user logged in') ;
1484 public function setName ($name) {
1485 throw new Exception ("Can't setName() on RoleLoggedIn") ;
1489 abstract class RoleUnion extends BaseRole implements PFO_RoleUnion {
1490 public function addRole ($role) {
1491 throw new Exception ("Not implemented") ;
1493 public function removeRole ($role) {
1494 throw new Exception ("Not implemented") ;
1498 class RoleComparator {
1499 var $criterion = 'composite' ;
1500 var $reference_project = NULL ;
1502 function Compare ($a, $b) {
1503 switch ($this->criterion) {
1505 return strcoll ($a->getName(), $b->getName()) ;
1508 $aid = $a->getID() ;
1509 $bid = $b->getID() ;
1513 return ($a < $b) ? -1 : 1;
1517 if ($this->reference_project == NULL) {
1518 return $this->CompareNoRef ($a, $b) ;
1520 $rpid = $this->reference_project->getID () ;
1521 $ap = $a->getHomeProject() ;
1522 $bp = $b->getHomeProject() ;
1523 $a_is_local = ($ap != NULL && $ap->getID() == $rpid) ; // Local
1524 $b_is_local = ($bp != NULL && $bp->getID() == $rpid) ;
1526 if ($a_is_local && !$b_is_local) {
1528 } elseif (!$a_is_local && $b_is_local) {
1531 return $this->CompareNoRef ($a, $b) ;
1535 function CompareNoRef ($a, $b) {
1536 $ap = $a->getHomeProject() ;
1537 $bp = $b->getHomeProject() ;
1538 if ($ap == NULL && $bp != NULL) {
1540 } elseif ($ap != NULL && $bp == NULL) {
1542 } elseif ($ap == NULL && $bp == NULL) {
1543 $tmp = strcoll ($a->getName(), $b->getName()) ;
1546 $projcmp = new ProjectComparator () ;
1547 $projcmp->criterion = 'name' ;
1548 $tmp = $projcmp->Compare ($ap, $bp) ;
1549 if ($tmp) { /* Different projects, sort accordingly */
1552 return strcoll ($a->getName(), $b->getName()) ;
1557 function sortRoleList (&$list, $relative_to = NULL, $criterion='composite') {
1558 $cmp = new RoleComparator () ;
1559 $cmp->criterion = $criterion ;
1560 $cmp->reference_project = $relative_to ;
1562 return usort ($list, array ($cmp, 'Compare')) ;
1567 // c-file-style: "bsd"