Disallow administration of users having permissions that the logged-i… (#4580)
[openemr.git] / src / Common / Acl / AclExtended.php
blob71143e823a6d65e42e35785c9a10b8e27e269ad3
1 <?php
3 /**
4 * AclExtended class.
6 * Provides Acl functions that are above and beyond the standard acl checks.
8 * Note that it stores a static GaclApi object to improve performance (this avoids doing
9 * separate database connection for every call to GaclApi)
11 * @package OpenEMR
12 * @link https://www.open-emr.org
13 * @author Rod Roark <rod@sunsetsystems.com>
14 * @author Brady Miller <brady.g.miller@gmail.com>
15 * @copyright Copyright (c) 2020 Brady Miller <brady.g.miller@gmail.com>
16 * @copyright Copyright (c) 2021 Rod Roark <rod@sunsetsystems.com>
17 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
20 namespace OpenEMR\Common\Acl;
22 use OpenEMR\Gacl\GaclApi;
23 use OpenEMR\Services\UserService;
24 use OpenEMR\Services\VersionService;
25 use OpenEMR\Common\Acl\AclMain;
27 class AclExtended
29 // Holds the static GaclApi object
30 private static $gaclApiObject;
32 // Collect the stored GaclApi object (create it if it doesn't yet exist)
33 // Sharing one object will prevent opening a database connection for every call to GaclApi.
34 private static function collectGaclApiObject()
36 if (!is_object(self::$gaclApiObject)) {
37 // GaclApi object does not yet exist, so create it
38 self::$gaclApiObject = new GaclApi();
40 return self::$gaclApiObject;
43 // Return an array keyed on squad ACO names.
44 // This is only applicable for sports team use.
46 public static function aclGetSquads()
48 $squads = self::aclGetSectionAcos('squads');
49 uasort($squads, "self::aclSquadCompare");
50 return $squads;
53 // Return an array keyed on encounter sensitivity level ACO names.
54 // Sensitivities are useful when some encounter notes are not
55 // medically sensitive (e.g. a physical fitness test), and/or if
56 // some will be "for doctor's eyes only" (e.g. STD treatment).
58 // When a non-blank sensitivity value exists in the new encounter
59 // form, it names an additional ACO required for access to all forms
60 // in the encounter. If you want some encounters to be non-sensitive,
61 // then you also need some default nonblank sensitivity for normal
62 // encounters, as well as greater encounter notes permissions for
63 // those allowed to view non-sensitive encounters.
65 public static function aclGetSensitivities()
67 return self::aclGetSectionAcos('sensitivities');
70 // Get the ACO name/value pairs for a designated section. Each value
71 // is an array (section_value, value, order_value, name, hidden).
73 private static function aclGetSectionAcos($section)
75 $gacl = self::collectGaclApiObject();
76 $arr1 = $gacl->get_objects($section, 1, 'ACO');
77 $arr = array();
78 if (!empty($arr1[$section])) {
79 foreach ($arr1[$section] as $value) {
80 $odata = $gacl->get_object_data($gacl->get_object_id($section, $value, 'ACO'), 'ACO');
81 $arr[$value] = $odata[0];
84 return $arr;
87 // Sort squads by their order value. Used only by aclGetSquads().
88 private static function aclSquadCompare($a, $b)
90 if ($a[2] == $b[2]) {
91 // If order value is the same, sort by squad name.
92 if ($a[3] == $b[3]) {
93 return 0;
95 return ($a[3] < $b[3]) ? -1 : 1;
97 return ($a[2] < $b[2]) ? -1 : 1;
101 // Returns true if aco exist
102 // Returns false if aco doesn't exist
103 // $section_name = name of section (string)
104 // $aco_name = name of aco (string)
106 public static function acoExist($section_name, $aco_name)
108 $gacl = self::collectGaclApiObject();
109 $aco_id = $gacl->get_object_id($section_name, $aco_name, 'ACO');
110 if ($aco_id) {
111 return true;
116 // Returns a sorted array of all available Group Titles.
118 public static function aclGetGroupTitleList($include_superusers = true)
120 $gacl = self::collectGaclApiObject();
121 $parent_id = $gacl->get_root_group_id();
122 $arr_group_ids = $gacl->get_group_children($parent_id, 'ARO', 'RECURSE');
123 $arr_group_titles = array();
124 foreach ($arr_group_ids as $value) {
125 $arr_group_data = $gacl->get_group_data($value, 'ARO');
126 // add if $include_superusers is true or group not include admin|super rule.
127 if ($include_superusers || !self::isGroupIncludeSuperuser($arr_group_data[3])) {
128 $arr_group_titles[$value] = $arr_group_data[3];
131 sort($arr_group_titles);
132 return $arr_group_titles;
136 // Returns a sorted array of group Titles that a user belongs to.
137 // Returns 0 if does not belong to any group yet.
138 // $user_name = Username, which is login name.
140 public static function aclGetGroupTitles($user_name)
142 $gacl = self::collectGaclApiObject();
143 $user_aro_id = $gacl->get_object_id('users', $user_name, 'ARO');
144 if ($user_aro_id) {
145 $arr_group_id = $gacl->get_object_groups($user_aro_id, 'ARO', 'NO_RECURSE');
146 if ($arr_group_id) {
147 foreach ($arr_group_id as $key => $value) {
148 $arr_group_data = $gacl->get_group_data($value, 'ARO');
149 $arr_group_titles[$key] = $arr_group_data[3];
151 sort($arr_group_titles);
152 return $arr_group_titles;
158 // This will place the user aro object into selected group(s)
159 // It uses the setUserAro() function
160 // $username = username (string)
161 // $group = title of group(s) (string or array)
163 public static function addUserAros($username, $group)
165 $current_user_groups = self::aclGetGroupTitles($username);
166 if (!$current_user_groups) {
167 $current_user_groups = array();
169 if (is_array($group)) {
170 foreach ($group as $value) {
171 if (!in_array($value, $current_user_groups)) {
172 array_push($current_user_groups, $value);
175 } else {
176 if (!in_array($group, $current_user_groups)) {
177 array_push($current_user_groups, $group);
180 $user_data = sqlFetchArray(sqlStatement("SELECT * FROM users WHERE username = ?", array($username)));
181 self::setUserAro(
182 $current_user_groups,
183 $username,
184 $user_data["fname"],
185 $user_data["mname"],
186 $user_data["lname"]
188 return;
192 // This will remove the user aro object from the selected group(s)
193 // It uses the setUserAro() function
194 // $username = username (string)
195 // $group = title of group(s) (string or array)
197 public static function removeUserAros($username, $group)
199 $current_user_groups = self::aclGetGroupTitles($username);
200 $new_user_groups = array();
201 if (is_array($group)) {
202 foreach ($current_user_groups as $value) {
203 if (!in_array($value, $group)) {
204 array_push($new_user_groups, $value);
207 } else {
208 foreach ($current_user_groups as $value) {
209 if ($value != $group) {
210 array_push($new_user_groups, $value);
214 $user_data = sqlFetchArray(sqlStatement("SELECT * FROM users WHERE username = ?", array($username)));
215 self::setUserAro(
216 $new_user_groups,
217 $username,
218 $user_data["fname"],
219 $user_data["mname"],
220 $user_data["lname"]
222 return;
226 // This will either create or edit a user aro object, and then place it
227 // in the requested groups. It will not allow removal of the 'admin'
228 // user or gacl_protected users from the 'admin' group.
229 // $arr_group_titles = titles of the groups that user will be added to.
230 // $user_name = username, which is login name.
231 // $first_name = first name
232 // $middle_name = middle name
233 // $last_name = last name
235 public static function setUserAro($arr_group_titles, $user_name, $first_name, $middle_name, $last_name)
237 $gacl = self::collectGaclApiObject();
239 //see if this user is gacl protected (ie. do not allow
240 //removal from the Administrators group)
241 require_once(dirname(__FILE__) . '/../../../library/user.inc');
243 $userNameToID = (new UserService())->getIdByUsername($user_name);
245 if (checkUserSetting("gacl_protect", "1", $userNameToID) || $user_name == "admin") {
246 $gacl_protect = true;
247 } else {
248 $gacl_protect = false;
251 //get array of all available group ID numbers
252 $parent_id = $gacl->get_root_group_id();
253 $arr_all_group_ids = $gacl->get_group_children($parent_id, 'ARO', 'RECURSE');
255 //Cycle through ID array to find and process each selected group
256 //Create a counter since processing of first hit is unique
257 $counter = 0;
258 foreach ($arr_all_group_ids as $value) {
259 $arr_group_data = $gacl->get_group_data($value, 'ARO');
260 if (
261 (empty($arr_group_titles)) ||
262 (in_array($arr_group_data[3], $arr_group_titles))
264 //We have a hit, so need to add group and increment counter
265 // because processing of first hit is unique
266 //This will also deal with an empty $arr_group_titles array
267 // removing user from all groups unless 'admin'
268 $counter = $counter + 1;
269 //create user full name field
270 if ($middle_name) {
271 $full_name = $first_name . " " . $middle_name . " " . $last_name;
272 } else {
273 if ($last_name) {
274 $full_name = $first_name . " " . $last_name;
275 } else {
276 $full_name = $first_name;
280 //If this is not the first group to be added, then will skip below
281 // and will be added. If this is the first group, then need to
282 // go thru several steps before adding the group.
283 if ($counter == 1) {
284 //get ID of user ARO object, if it exist
285 $user_aro_id = $gacl->get_object_id('users', $user_name, 'ARO');
286 if ($user_aro_id) {
287 //user ARO object already exist, so will edit it
288 $gacl->edit_object($user_aro_id, 'users', $full_name, $user_name, 10, 0, 'ARO');
290 //remove all current user ARO object group associations
291 $arr_remove_group_ids = $gacl->get_object_groups($user_aro_id, 'ARO', 'NO_RECURSE');
292 foreach ($arr_remove_group_ids as $value2) {
293 $gacl->del_group_object($value2, 'users', $user_name, 'ARO');
295 } else {
296 //user ARO object does not exist, so will create it
297 $gacl->add_object('users', $full_name, $user_name, 10, 0, 'ARO');
301 //place the user ARO object in the selected group (if group(s) is selected)
302 if (!empty($arr_group_titles)) {
303 $gacl->add_group_object($value, 'users', $user_name, 'ARO');
307 //Below will not allow 'admin' or gacl_protected user to be removed from 'admin' group
309 if ($gacl_protect) {
310 $boolean_admin = 0;
311 $admin_id = $gacl->get_object_id('users', $user_name, 'ARO');
312 $arr_admin = $gacl->get_object_groups($admin_id, 'ARO', 'NO_RECURSE');
313 if (!empty($arr_admin)) {
314 foreach ($arr_admin as $value3) {
315 $arr_admin_data = $gacl->get_group_data($value3, 'ARO');
316 if (strcmp($arr_admin_data[2], 'admin') == 0) {
317 $boolean_admin = 1;
320 if (!$boolean_admin) {
321 foreach ($arr_all_group_ids as $value4) {
322 $arr_temp = $gacl->get_group_data($value4, 'ARO');
323 if ($arr_temp[2] == 'admin') {
324 $gacl->add_group_object($value4, 'users', $user_name, 'ARO');
331 //if array of groups was empty, then we are done, and can break from loop
332 if (empty($arr_group_titles)) {
333 break;
336 return true;
340 // Returns true if acl exist
341 // Returns false if acl doesn't exist
342 // EITHER $title or $name is required(send FALSE in variable
343 // not being used). If both are sent, then only $title will be
344 // used.
345 // $return_value is required
346 // $title = title of acl (string)
347 // $name = name of acl (string)
348 // $return_value = return value of acl (string)
350 public static function aclExist($title, $name, $return_value)
352 $gacl = self::collectGaclApiObject();
353 if (!$name) {
354 $acl = $gacl->search_acl(false, false, false, false, $title, false, false, false, $return_value);
355 } else {
356 if (!$title) {
357 $group_id = $gacl->get_group_id($name, null, 'ARO');
358 if ($group_id) {
359 $group_data = $gacl->get_group_data($group_id, 'ARO');
360 $acl = $gacl->search_acl(false, false, false, false, $group_data[3], false, false, false, $return_value);
361 } else {
362 return false;
364 } else {
365 $acl = $gacl->search_acl(false, false, false, false, $title, false, false, false, $return_value);
368 if (!empty($acl)) {
369 return true;
370 } else {
371 return false;
376 // This will add a new acl and group(if group doesn't yet exist)
377 // with one aco in it.
378 // $acl_title = title of acl (string)
379 // $acl_name = name of acl (string)
380 // $return_value = return value of acl (string)
381 // $note = description of acl (array)
383 public static function aclAdd($acl_title, $acl_name, $return_value, $note)
385 $gacl = self::collectGaclApiObject();
386 $group_id = $gacl->get_group_id($acl_name, $acl_title, 'ARO');
387 if ($group_id) {
388 //group already exist, so just create acl
389 $gacl->add_acl(
390 array("placeholder" => array("filler")),
391 null,
392 array($group_id),
393 null,
394 null,
397 $return_value,
398 $note
400 } else {
401 //create group, then create acl
402 $parent_id = $gacl->get_root_group_id();
403 $aro_id = $gacl->add_group($acl_name, $acl_title, $parent_id, 'ARO');
404 $gacl->add_acl(
405 array("placeholder" => array("filler")),
406 null,
407 array($aro_id),
408 null,
409 null,
412 $return_value,
413 $note
416 return;
420 // This will remove acl. It will also remove group(if the group
421 // is no longer associated with any acl's).
422 // $acl_title = title of acl (string)
423 // $acl_name = name of acl (string)
424 // $return_value = return value of acl (string)
425 // $note = description of acl (array)
427 public static function aclRemove($acl_title, $return_value)
429 $gacl = self::collectGaclApiObject();
430 //First, delete the acl
431 $acl_id = $gacl->search_acl(false, false, false, false, $acl_title, false, false, false, $return_value);
432 $gacl->del_acl($acl_id[0]);
433 //Then, remove the group(if no more acl's are remaining)
434 $acl_search = $gacl->search_acl(false, false, false, false, $acl_title, false, false, false, false);
435 if (empty($acl_search)) {
436 $group_id = $gacl->get_group_id(null, $acl_title, 'ARO');
437 $gacl->del_group($group_id, true, 'ARO');
439 return;
443 // This will place the aco(s) into the selected acl
444 // $acl_title = title of acl (string)
445 // $return_value = return value of acl (string)
446 // $aco_id = id of aco (array)
448 public static function aclAddAcos($acl_title, $return_value, $aco_id)
450 $gacl = self::collectGaclApiObject();
451 $acl_id = $gacl->search_acl(false, false, false, false, $acl_title, false, false, false, $return_value);
452 foreach ($aco_id as $value) {
453 $aco_data = $gacl->get_object_data($value, 'ACO');
454 $aco_section = $aco_data[0][0];
455 $aco_name = $aco_data[0][1];
456 $gacl->append_acl($acl_id[0], null, null, null, null, array($aco_section => array($aco_name)));
458 return;
462 // This will remove the aco(s) from the selected acl
463 // Note if all aco's are removed, then will place the filler-placeholder
464 // into the acl to avoid complete removal of the acl.
465 // $acl_title = title of acl (string)
466 // $return_value = return value of acl (string)
467 // $aco_id = id of aco (array)
469 public static function aclRemoveAcos($acl_title, $return_value, $aco_id)
471 $gacl = self::collectGaclApiObject();
472 $acl_id = $gacl->search_acl(false, false, false, false, $acl_title, false, false, false, $return_value);
474 // Check to see if removing all acos. If removing all acos then will
475 // ensure the filler-placeholder aco in acl to avoid complete
476 // removal of the acl.
477 if (count($aco_id) == self::aclCountAcos($acl_title, $return_value)) {
478 //1-get the filler-placeholder aco id
479 $filler_aco_id = $gacl->get_object_id('placeholder', 'filler', 'ACO');
480 //2-add filler-placeholder aco
481 self::aclAddAcos($acl_title, $return_value, array($filler_aco_id));
482 //3-ensure filler-placeholder aco is not to be deleted
483 $safeListaco = self::removeElement($_POST["selection"], $filler_aco_id);
484 //4-prepare to safely delete the acos
485 $aco_id = $safeListaco;
488 foreach ($aco_id as $value) {
489 $aco_data = $gacl->get_object_data($value, 'ACO');
490 $aco_section = $aco_data[0][0];
491 $aco_name = $aco_data[0][1];
492 $gacl->shift_acl($acl_id[0], null, null, null, null, array($aco_section => array($aco_name)));
494 return;
498 // This will return the number of aco objects
499 // in a specified acl.
500 // $acl_title = title of acl (string)
501 // $return_value = return value of acl (string)
503 private static function aclCountAcos($acl_title, $return_value)
505 $gacl = self::collectGaclApiObject();
506 $acl_id = $gacl->search_acl(false, false, false, false, $acl_title, false, false, false, $return_value);
507 $acl_data = $gacl->get_acl($acl_id[0]);
508 $aco_count = 0;
509 foreach ($acl_data['aco'] as $key => $value) {
510 $aco_count = $aco_count + count($acl_data['aco'][$key]);
512 return $aco_count;
516 // Function to remove an element from an array
518 private static function removeElement($arr, $val)
520 $arr2 = array();
521 foreach ($arr as $value) {
522 if ($value != $val) {
523 array_push($arr2, $value);
526 return $arr2;
529 // This generates an HTML options list for all ACOs.
530 // The caller inserts this between <select> and </select> tags.
532 public static function genAcoHtmlOptions($default = '')
534 $acoArray = self::genAcoArray();
535 $s = '';
536 foreach ($acoArray as $section => $acos_array) {
537 $s .= "<optgroup label='" . xla($section) . "'>\n";
538 foreach ($acos_array as $aco_array) {
539 $s .= "<option value='" . attr($aco_array['value']) . "'";
540 if ($aco_array['value'] == $default) {
541 $s .= ' selected';
543 $s .= ">" . xlt($aco_array['name']) . "</option>";
545 $s .= "</optgroup>";
547 return $s;
551 // Returns array of all ACOs
552 public static function genAcoArray()
554 $acoArray = array();
555 $gacl = self::collectGaclApiObject();
556 // collect and sort all aco objects
557 $list_aco_objects = $gacl->get_objects(null, 0, 'ACO');
558 ksort($list_aco_objects);
559 foreach ($list_aco_objects as $seckey => $dummy) {
560 if (empty($dummy)) {
561 continue;
563 asort($list_aco_objects[$seckey]);
564 $aco_section_data = $gacl->get_section_data($seckey, 'ACO');
565 $aco_section_title = $aco_section_data[3];
566 foreach ($list_aco_objects[$seckey] as $acokey) {
567 $aco_id = $gacl->get_object_id($seckey, $acokey, 'ACO');
568 $aco_data = $gacl->get_object_data($aco_id, 'ACO');
569 $aco_title = $aco_data[0][3];
570 $optkey = "$seckey|$acokey";
571 $acoArray[$aco_section_title][$aco_id]['name'] = $aco_title;
572 $acoArray[$aco_section_title][$aco_id]['value'] = $optkey;
575 return $acoArray;
578 // check if aro group have superuser rule
579 public static function isGroupIncludeSuperuser($aro_group_name)
581 $gacl = self::collectGaclApiObject();
582 return empty($gacl->search_acl('admin', 'super', false, false, $aro_group_name)) ? false : true;
586 // Returns acl listings(including return value) via xml message.
587 // $err = error strings (array)
589 public static function aclListingsXml($err)
591 $gacl = self::collectGaclApiObject();
593 $message = "<?xml version=\"1.0\"?>\n" .
594 "<response>\n";
595 foreach (self::aclGetGroupTitleList() as $value) {
596 $acl_id = $gacl->search_acl(false, false, false, false, $value, false, false, false, false);
597 foreach ($acl_id as $value2) {
598 $acl = $gacl->get_acl($value2);
599 $ret = $acl["return_value"];
600 $note = $acl["note"];
602 // Modified 6-2009 by BM - Translate gacl group name if applicable
603 // Translate return value
604 // Translate description
605 $message .= "\t<acl>\n" .
606 "\t\t<value>" . $value . "</value>\n" .
607 "\t\t<title>" . xl_gacl_group($value) . "</title>\n" .
608 "\t\t<returnid>" . $ret . "</returnid>\n" .
609 "\t\t<returntitle>" . xl($ret) . "</returntitle>\n" .
610 "\t\t<note>" . xl($note) . "</note>\n" .
611 "\t</acl>\n";
615 if (isset($err)) {
616 foreach ($err as $value) {
617 $message .= "\t<error>" . $value . "</error>\n";
621 $message .= "</response>\n";
622 return $message;
626 // Return aco listings by sections(active and inactive lists)
627 // via xml message.
628 // $group = group title (string)
629 // $return_value = return value (string)
630 // $err = error strings (array)
632 public static function acoListingsXml($group, $return_value, $err)
634 $gacl = self::collectGaclApiObject();
636 //collect and sort all aco objects
637 $list_aco_objects = $gacl->get_objects(null, 0, 'ACO');
638 foreach ($list_aco_objects as $key => $value) {
639 asort($list_aco_objects[$key]);
642 //collect aco objects within the specified acl(already sorted)
643 $acl_id = $gacl->search_acl(false, false, false, false, $group, false, false, false, $return_value);
644 $acl = $gacl->get_acl($acl_id[0]);
645 $active_aco_objects = $acl["aco"];
647 $message = "<?xml version=\"1.0\"?>\n" .
648 "<response>\n" .
649 "\t<inactive>\n";
650 foreach ($list_aco_objects as $key => $value) {
651 $counter = 0;
652 foreach ($list_aco_objects[$key] as $value2) {
653 if (!array_key_exists($key, $active_aco_objects) || !in_array($value2, $active_aco_objects[$key])) {
654 if ($counter == 0) {
655 $counter = $counter + 1;
656 $aco_section_data = $gacl->get_section_data($key, 'ACO');
657 $aco_section_title = $aco_section_data[3];
659 // Modified 6-2009 by BM - Translate gacl aco section name
660 $message .= "\t\t<section>\n" .
661 "\t\t\t<name>" . xl($aco_section_title) . "</name>\n";
664 $aco_id = $gacl->get_object_id($key, $value2, 'ACO');
665 $aco_data = $gacl->get_object_data($aco_id, 'ACO');
666 $aco_title = $aco_data[0][3];
667 $message .= "\t\t\t<aco>\n";
669 // Modified 6-2009 by BM - Translate gacl aco name
670 $message .= "\t\t\t\t<title>" . xl($aco_title) . "</title>\n";
672 $message .= "\t\t\t\t<id>" . $aco_id . "</id>\n";
673 $message .= "\t\t\t</aco>\n";
677 if ($counter != 0) {
678 $message .= "\t\t</section>\n";
682 $message .= "\t</inactive>\n" .
683 "\t<active>\n";
684 foreach ($active_aco_objects as $key => $value) {
685 $aco_section_data = $gacl->get_section_data($key, 'ACO');
686 $aco_section_title = $aco_section_data[3];
688 // Modified 6-2009 by BM - Translate gacl aco section name
689 $message .= "\t\t<section>\n" .
690 "\t\t\t<name>" . xl($aco_section_title) . "</name>\n";
692 foreach ($active_aco_objects[$key] as $value2) {
693 $aco_id = $gacl->get_object_id($key, $value2, 'ACO');
694 $aco_data = $gacl->get_object_data($aco_id, 'ACO');
695 $aco_title = $aco_data[0][3];
696 $message .= "\t\t\t<aco>\n";
698 // Modified 6-2009 by BM - Translate gacl aco name
699 $message .= "\t\t\t\t<title>" . xl($aco_title) . "</title>\n";
701 $message .= "\t\t\t\t<id>" . $aco_id . "</id>\n";
702 $message .= "\t\t\t</aco>\n";
705 $message .= "\t\t</section>\n";
708 $message .= "\t</active>\n";
709 if (isset($err)) {
710 foreach ($err as $value) {
711 $message .= "\t<error>" . $value . "</error>\n";
715 $message .= "</response>\n";
716 return $message;
720 // Returns listing of all possible return values via xml message.
721 // $err = error strings (array)
723 public static function returnValuesXml($err)
725 $gacl = self::collectGaclApiObject();
726 $returns = array();
728 $message = "<?xml version=\"1.0\"?>\n" .
729 "<response>\n";
730 foreach (self::aclGetGroupTitleList() as $value) {
731 $acl_id = $gacl->search_acl(false, false, false, false, $value, false, false, false, false);
732 foreach ($acl_id as $value2) {
733 $acl = $gacl->get_acl($value2);
734 $ret = $acl["return_value"];
735 if (!in_array($ret, $returns)) {
736 // Modified 6-2009 by BM - Translate return value
737 $message .= "\t<return>\n";
738 $message .= "\t\t<returnid>" . $ret . "</returnid>\n";
739 $message .= "\t\t<returntitle>" . xl($ret) . "</returntitle>\n";
740 $message .= "\t</return>\n";
742 array_push($returns, $ret);
747 if (isset($err)) {
748 foreach ($err as $value) {
749 $message .= "\t<error>" . $value . "</error>\n";
753 $message .= "</response>\n";
754 return $message;
758 * Returns the current access control version.
760 * @return integer The current access control version.
762 public static function getAclVersion()
764 $versionService = new VersionService();
765 $version = $versionService->fetch();
766 return $version['v_acl'];
770 * Records the access control version.
772 * @param integer $acl_version access control version
774 public static function setAclVersion($acl_version)
776 $versionService = new VersionService();
777 $version = $versionService->fetch();
778 $version['v_acl'] = $acl_version;
779 $versionService->update($version);
780 return;
784 * Function will return an array that contains the ACL ID number. It will also check to ensure
785 * the ACL exist and is not duplicated.
787 * @param string $title Title of group.
788 * @param string $return_value What the acl returns), usually 'write' or 'addonly'
789 * @return array An array that contains the ACL ID number.
791 public static function getAclIdNumber($title, $return_value)
793 $gacl = self::collectGaclApiObject();
794 $temp_acl_id_array = $gacl->search_acl(false, false, false, false, $title, false, false, false, $return_value);
795 switch (count($temp_acl_id_array)) {
796 case 0:
797 echo "<B>ERROR</B>, '$title' group '$return_value' ACL does not exist.</BR>";
798 break;
799 case 1:
800 echo "'$title' group '$return_value' ACL is present.</BR>";
801 break;
802 default:
803 echo "<B>ERROR</B>, Multiple '$title' group '$return_value' ACLs are present.</BR>";
804 break;
807 return $temp_acl_id_array;
811 * Function will add an ACL (if doesn't already exist).
812 * It will also place the acl in the group, or will CREATE a new group.
813 * It will return the ID number of the acl (created or old)
815 * @param string $title Title of group.
816 * @param string $name name of acl
817 * @param string $return_value What the acl returns, usually 'write' or 'addonly'
818 * @param string $note description of acl
819 * @return array ID number of the acl (created or old)
821 public static function addNewACL($title, $name, $return_value, $note)
823 $gacl = self::collectGaclApiObject();
824 $temp_acl_id_array = $gacl->search_acl(false, false, false, false, $title, false, false, false, $return_value);
825 switch (count($temp_acl_id_array)) {
826 case 0:
827 $group_id = $gacl->get_group_id($name, $title, 'ARO');
828 if ($group_id) {
829 //group already exist, so just create acl
830 $temp_acl_id = $gacl->add_acl(array("placeholder" => array("filler")), null, array($group_id), null, null, 1, 1, $return_value, $note);
831 if ($temp_acl_id) {
832 echo "The '$title' group already exist.</BR>";
833 echo "The '$title' group '$return_value' ACL has been successfully added.</BR>";
834 $temp_acl_id_array = array($temp_acl_id);
835 } else {
836 echo "The '$title' group already exist.</BR>";
837 echo "<B>ERROR</B>, Unable to create the '$title' group '$return_value' ACL.</BR>";
839 } else {
840 //create group, then create acl
841 $parent_id = $gacl->get_root_group_id();
842 $aro_id = $gacl->add_group($name, $title, $parent_id, 'ARO');
843 $temp_acl_id = $gacl->add_acl(array("placeholder" => array("filler")), null, array($aro_id), null, null, 1, 1, $return_value, $note);
844 if ($aro_id) {
845 echo "The '$title' group has been successfully added.</BR>";
846 } else {
847 echo "<B>ERROR</B>, Unable to create the '$title' group.</BR>";
850 if ($temp_acl_id) {
851 echo "The '$title' group '$return_value' ACL has been successfully added.</BR>";
852 $temp_acl_id_array = array($temp_acl_id);
853 } else {
854 echo "<B>ERROR</B>, Unable to create the '$title' group '$return_value' ACL.</BR>";
857 break;
858 case 1:
859 echo "'$title' group '$return_value' ACL already exist.</BR>";
860 break;
862 default:
863 echo "<B>ERROR</B>, Multiple '$title' group '$return_value' ACLs are present.</BR>";
864 break;
867 return $temp_acl_id_array;
871 * Function to add an object section.
872 * It will check to ensure the object section doesn't already exist.
874 * @param string $name identifier of section
875 * @param string $title Title o object.
877 public static function addObjectSectionAcl($name, $title)
879 $gacl = self::collectGaclApiObject();
880 if ($gacl->get_object_section_section_id($title, $name, 'ACO')) {
881 echo "The '$title' object section already exist.</BR>";
882 } else {
883 $tmp_boolean = $gacl->add_object_section($title, $name, 10, 0, 'ACO');
884 if ($tmp_boolean) {
885 echo "The '$title' object section has been successfully added.</BR>";
886 } else {
887 echo "<B>ERROR</B>,unable to create the '$title' object section.</BR>";
891 return;
896 * Function to add an object.
897 * It will check to ensure the object doesn't already exist.
899 * @param string $section_name Identifier of section
900 * @param string $section_title Title of section
901 * @param string $object_name Identifier of object
902 * @param string $object_title Title of object
904 public static function addObjectAcl($section_name, $section_title, $object_name, $object_title)
906 $gacl = self::collectGaclApiObject();
907 if ($gacl->get_object_id($section_name, $object_name, 'ACO')) {
908 echo "The '$object_title' object in the '$section_title' section already exist.</BR>";
909 } else {
910 $tmp_boolean = $gacl->add_object($section_name, $object_title, $object_name, 10, 0, 'ACO');
911 if ($tmp_boolean) {
912 echo "The '$object_title' object in the '$section_title' section has been successfully added.</BR>";
913 } else {
914 echo "<B>ERROR</B>,unable to create the '$object_title' object in the '$section_title' section.</BR>";
918 return;
922 * Function to add an object and set the 'order' variable.
923 * It will check to ensure the object doesn't already exist.
925 * @param string $section_name Identifier of section
926 * @param string $section_title Title of section
927 * @param string $object_name Identifier of object
928 * @param string $object_title Title of object
929 * @param string $order_number number to determine order in list. used in sensitivities to order the choices in openemr
931 public static function addObjectAclWithOrder($section_name, $section_title, $object_name, $object_title, $order_number)
933 $gacl = self::collectGaclApiObject();
934 if ($gacl->get_object_id($section_name, $object_name, 'ACO')) {
935 echo "The '$object_title' object in the '$section_title' section already exist.</BR>";
936 } else {
937 $tmp_boolean = $gacl->add_object($section_name, $object_title, $object_name, $order_number, 0, 'ACO');
938 if ($tmp_boolean) {
939 echo "The '$object_title' object in the '$section_title' section has been successfully added.</BR>";
940 } else {
941 echo "<B>ERROR</B>,unable to create the '$object_title' object in the '$section_title' section.</BR>";
945 return;
949 * Function to edit an object and set the 'order' variable.
950 * It will check to ensure the object doesn't already exist, and hasn't been upgraded yet.
952 * @param string $section_name Identifier of section
953 * @param string $section_title Title of section
954 * @param string $object_name Identifier of object
955 * @param string $object_title Title of object
956 * @param string $order_number number to determine order in list. used in sensitivities to order the choices in openemr
958 public static function editObjectAcl($section_name, $section_title, $object_name, $object_title, $order_number)
960 $gacl = self::collectGaclApiObject();
961 $tmp_objectID = $gacl->get_object_id($section_name, $object_name, 'ACO');
962 if ($tmp_objectID) {
963 $tmp_object = $gacl->get_object_data($tmp_objectID, 'ACO');
964 if (
965 $tmp_object[0][2] == $order_number &&
966 $tmp_object[0][0] == $section_name &&
967 $tmp_object[0][1] == $object_name &&
968 $tmp_object[0][3] == $object_title
970 echo "The '$object_title' object in the '$section_title' section has already been updated.</BR>";
971 } else {
972 $tmp_boolean = $gacl->edit_object($tmp_objectID, $section_name, $object_title, $object_name, $order_number, 0, 'ACO');
973 if ($tmp_boolean) {
974 echo "The '$object_title' object in the '$section_title' section has been successfully updated.</BR>";
975 } else {
976 echo "<B>ERROR</B>,unable to update the '$object_title' object in the '$section_title' section.</BR>";
979 } else {
980 echo "<B>ERROR</B>, the '$object_title' object in the '$section_title' section does not exist.</BR>";
983 return;
987 * Update the ACL.
988 * It will check to ensure the ACL hasn't already been updated.
990 * @param array $array_acl_id_number Array containing hopefully one element, which is an integer, and is identifier of acl to be updated.
991 * @param string $group_title Title of group.
992 * @param string $object_section_name Identifier of section
993 * @param string $object_section_title Title of section
994 * @param string $object_name Identifier of object
995 * @param string $object_title Title of object
996 * @param string $acl_return_value What the acl returns (string), usually 'write', 'addonly', 'wsome' or 'view'
998 public static function updateAcl($array_acl_id_number, $group_title, $section_name, $section_title, $object_name, $object_title, $return_value)
1000 $gacl = self::collectGaclApiObject();
1001 $tmp_array = $gacl->search_acl($section_name, $object_name, false, false, $group_title, false, false, false, $return_value);
1002 switch (count($tmp_array)) {
1003 case 0:
1004 $tmp_boolean = @$gacl->append_acl($array_acl_id_number[0], null, null, null, null, array($section_name => array($object_name)));
1005 if ($tmp_boolean) {
1006 echo "Successfully placed the '$object_title' object of the '$section_title' section into the '$group_title' group '$return_value' ACL.</BR>";
1007 } else {
1008 echo "<B>ERROR</B>,unable to place the '$object_title' object of the '$section_title' section into the '$group_title' group '$return_value' ACL.</BR>";
1010 break;
1011 case 1:
1012 echo "The '$object_title' object of the '$section_title' section is already found in the '$group_title' group '$return_value' ACL.</BR>";
1013 break;
1014 default:
1015 echo "<B>ERROR</B>, Multiple '$group_title' group '$return_value' ACLs with the '$object_title' object of the '$section_title' section are present.</BR>";
1016 break;
1019 return;
1023 * Update the provided array of ACOs that the designated group has permission for.
1024 * This is an array keyed on ACO section ID with values that are arrays keyed on ACO ID
1025 * with values that are arrays keyed on return value.
1027 * @param string $group_name Name of group
1028 * @param array $perms The array to update
1030 public static function getGroupPermissions($group_name, &$perms)
1032 $gacl = self::collectGaclApiObject();
1033 $acl_ids = $gacl->search_acl(false, false, false, false, $group_name, false, false, false, false);
1034 foreach ($acl_ids as $acl_id) {
1035 $acl = $gacl->get_acl($acl_id);
1036 $ret = $acl['return_value'];
1037 foreach ($acl['aco'] as $sectionid => $acos) {
1038 if ($sectionid != 'placeholder') {
1039 foreach ($acos as $aco) {
1040 $perms[$sectionid][$aco][$ret] = 1;
1048 * Return an array of all ACOs that the designated user has permission for.
1049 * This is an array keyed on ACO section ID with values that are arrays keyed on ACO ID
1050 * with values that are arrays keyed on return value.
1052 * @param string $username Name of user
1053 * @return array The array of ACOs
1055 public static function getUserPermissions($username = '')
1057 if (!$username) {
1058 $username = $_SESSION['authUser'];
1060 $gacl = self::collectGaclApiObject();
1061 $perms = array();
1062 $username_acl_groups = self::aclGetGroupTitles($username); // array of roles for the user
1063 if ($username_acl_groups) {
1064 foreach ($username_acl_groups as $group_name) {
1065 self::getGroupPermissions($group_name, $perms);
1068 return $perms;
1072 * Test if the logged-in user has all of the permissions of the specified user.
1074 * @param string $username Name of user
1075 * @return boolean
1077 public static function iHavePermissionsOf($username)
1079 $perms = self::getUserPermissions($username);
1080 $myperms = self::getUserPermissions();
1081 foreach ($perms as $sectionid => $acos) {
1082 foreach ($acos as $aco => $rets) {
1083 foreach ($rets as $ret => $dummy) {
1084 // Next test is just to speed things up.
1085 if (empty($myperms[$sectionid][$aco][$ret]) && empty($myperms[$sectionid][$aco]['write'])) {
1086 if (!aclMain::aclCheckCore($sectionid, $aco, '', $ret)) {
1087 return false;
1093 return true;
1097 * Test if the logged-in user has all of the permissions of the specified group.
1099 * @param string $group_name Name of group
1100 * @return boolean
1102 public static function iHaveGroupPermissions($group_name)
1104 $perms = array();
1105 self::getGroupPermissions($group_name, $perms);
1106 $myperms = self::getUserPermissions();
1107 foreach ($perms as $sectionid => $acos) {
1108 foreach ($acos as $aco => $rets) {
1109 foreach ($rets as $ret => $dummy) {
1110 // Next test is just to speed things up.
1111 if (empty($myperms[$sectionid][$aco][$ret]) && empty($myperms[$sectionid][$aco]['write'])) {
1112 if (!aclMain::aclCheckCore($sectionid, $aco, '', $ret)) {
1113 return false;
1119 return true;