2 // php-GACL access controls are included in OpenEMR. The below
3 // function will automatically create the path where gacl.class.php
4 // can be found. Note that this path can be manually set below
5 // for users who are using an external version of php-GACL.
6 // Also note that php-GACL access controls can be turned off
9 $phpgacl_location = dirname(__FILE__).'/../gacl';
12 // If using an external version of phpGACL, then uncomment the following
13 // line and manually place the path below. IN THIS CASE YOU MUST ALSO
14 // COMMENT OUT ABOVE $phpgacl_location ASSIGNMENT ABOVE, OR BACKUPS WILL
15 // NOT RESTORE PROPERLY!
17 //$phpgacl_location = "/var/www/gacl";
19 // If you want to turn off php-GACL, then uncomment the following line.
20 // IN THIS CASE YOU MUST ALSO COMMENT OUT ABOVE $phpgacl_location ASSIGNMENT(S)
21 // ABOVE, OR BACKUPS WILL NOT RESTORE PROPERLY!
23 //unset($phpgacl_location);
26 // The following Access Control Objects (ACO) are currently supported.
27 // These are the "things to be protected":
29 // Section "admin" (Administration):
30 // super Superuser - can delete patients, encounters, issues
31 // calendar Calendar Settings
32 // database Database Reporting
33 // forms Forms Administration
34 // practice Practice Settings
35 // superbill Superbill Codes Administration
36 // users Users/Groups/Logs Administration
37 // batchcom Batch Communication Tool
38 // language Language Interface Tool
39 // drugs Pharmacy Dispensary
40 // acl ACL Administration
42 // Section "acct" (Accounting):
43 // bill Billing (write optional)
44 // disc Allowed to discount prices (in Fee Sheet or Checkout form)
46 // rep Financial Reporting - my encounters
47 // rep_a Financial Reporting - anything
49 // Section "patients" (Patient Information):
50 // appt Appointments (write,wsome optional)
51 // demo Demographics (write,addonly optional)
52 // med Medical Records and History (write,addonly optional)
53 // trans Transactions, e.g. referrals (write optional)
54 // docs Documents (write,addonly optional)
55 // notes Patient Notes (write,addonly optional)
56 // sign Sign Lab Results (write,addonly optional)
58 // Section "encounters" (Encounter Information):
59 // auth Authorize - my encounters
60 // auth_a Authorize - any encounters
61 // coding Coding - my encounters (write,wsome optional)
62 // coding_a Coding - any encounters (write,wsome optional)
63 // notes Notes - my encounters (write,addonly optional)
64 // notes_a Notes - any encounters (write,addonly optional)
65 // date_a Fix encounter dates - any encounters
66 // relaxed Less-private information (write,addonly optional)
67 // (e.g. the Sports Fitness encounter form)
69 // Section "squads" applies to sports team use only:
70 // acos in this section define the user-specified list of squads
72 // Section "sensitivities" (Sensitivities):
76 // Section "lists" (Lists):
77 // default Default List (write,addonly optional)
78 // state State List (write,addonly optional)
79 // country Country List (write,addonly optional)
80 // language Language List (write,addonly optional)
81 // ethrace Ethnicity-Race List (write,addonly optional)
83 // Section "placeholder" (Placeholder):
84 // filler Placeholder (Maintains empty ACLs)
86 // Section "nationnotes" (Nation Notes):
87 // nn_configure Nation Notes
89 // Section "patientportal" (Patient Portal):
90 // portal Patient Portal
92 if (isset ($phpgacl_location)) {
93 include_once("$phpgacl_location/gacl.class.php");
94 $gacl_object = new gacl();
95 //DO NOT CHANGE BELOW VARIABLE
96 $section_aro_value = 'users';
99 // acl_check should return FALSE if access is denied and will return TRUE
100 // if access is allowed. The following return_value(s) can be evaluated
101 // for by placing in the $return_value parameter; it can be placed
102 // as a single value or can send as an array of values (so $return_value
103 // can be a string or an array).
105 // * view - the user may view but not add or modify entries
106 // * write - the user may add or modify the ACO
107 // * wsome - the user has limited add/modify access to the ACO
108 // * addonly - the user may view and add but not modify entries
110 function acl_check($section, $value, $user = '', $return_value = '') {
111 global $gacl_object, $phpgacl_location, $section_aro_value;
112 if (! $user) $user = $_SESSION['authUser'];
114 if ($phpgacl_location) {
115 // This will return all pertinent ACL's (including return_values and whether allow/deny)
116 // Walk through them to assess for access
117 $acl_results = $gacl_object->acl_query($section, $value, $section_aro_value, $user,NULL,NULL,NULL,NULL,NULL,TRUE);
118 if (empty($acl_results)) {
119 return FALSE; //deny access
121 $access=FALSE; //flag
123 foreach ($acl_results as $acl_result) {
124 if (empty($acl_result['acl_id'])) return FALSE; //deny access, since this happens if no pertinent ACL's are returned
125 if (is_array($return_value)) {
126 foreach ($return_value as $single_return_value) {
127 if (empty($single_return_value)) {
128 // deal with case if not looking for specific return value
129 if ($acl_result['allow']) {
136 else { //!empty($single_return_value)
137 // deal with case if looking for specific return value
138 if ($acl_result['return_value'] == $single_return_value) {
139 if ($acl_result['allow']) {
149 else { // $return_value is not an array (either empty or with one value)
150 if (empty($return_value)) {
151 // deal with case if not looking for specific return value
152 if ($acl_result['allow']) {
159 else { //!empty($return_value)
160 // deal with case if looking for specific return value
161 if ($acl_result['return_value'] == $return_value) {
162 if ($acl_result['allow']) {
173 // Now decide whether user has access
174 // (Note a denial takes precedence)
175 if ($deny) return FALSE;
176 if ($access) return TRUE;
181 // If no phpgacl, then apply the old static rules whereby "authorized"
182 // users (providers) can do anything, and other users can do most things.
183 // If you want custom access control but don't want to mess with phpGACL,
184 // then you could customize the code below instead.
186 if ($user == 'admin') return 'write';
187 if ($section == 'admin' && $value == 'super') return 0;
188 if ($_SESSION['userauthorized']) return 'write';
190 if ($section == 'patients') {
191 if ($value == 'med') return 1;
194 else if ($section == 'encounters') {
195 if (strpos($value, 'coding' ) === 0) return 'write';
196 if (strpos($value, 'notes' ) === 0) return 'write';
197 if ($value == 'relaxed') return 'write';
199 else if ($section != 'admin') {
206 // Get the ACO name/value pairs for a designated section. Each value
207 // is an array (section_value, value, order_value, name, hidden).
209 function acl_get_section_acos($section) {
210 global $phpgacl_location;
211 if ($phpgacl_location) {
212 include_once("$phpgacl_location/gacl_api.class.php");
213 $gacl = new gacl_api();
214 $arr1 = $gacl->get_objects($section, 1, 'ACO');
216 if (!empty($arr1[$section])) {
217 foreach ($arr1[$section] as $value) {
218 $odata = $gacl->get_object_data($gacl->get_object_id($section, $value, 'ACO'), 'ACO');
219 $arr[$value] = $odata[0];
227 // Sort squads by their order value. Used only by acl_get_squads().
228 function _acl_squad_compare($a, $b) {
229 if ($a[2] == $b[2]) {
230 // If order value is the same, sort by squad name.
231 if ($a[3] == $b[3]) return 0;
232 return ($a[3] < $b[3]) ? -1 : 1;
234 return ($a[2] < $b[2]) ? -1 : 1;
237 // Return an array keyed on squad ACO names.
238 // This is only applicable for sports team use.
240 function acl_get_squads() {
241 $squads = acl_get_section_acos('squads');
242 uasort($squads, "_acl_squad_compare");
246 // Return an array keyed on encounter sensitivity level ACO names.
247 // Sensitivities are useful when some encounter notes are not
248 // medically sensitive (e.g. a physical fitness test), and/or if
249 // some will be "for doctor's eyes only" (e.g. STD treatment).
251 // When a non-blank sensitivity value exists in the new encounter
252 // form, it names an additional ACO required for access to all forms
253 // in the encounter. If you want some encounters to be non-sensitive,
254 // then you also need some default nonblank sensitivity for normal
255 // encounters, as well as greater encounter notes permissions for
256 // those allowed to view non-sensitive encounters.
258 function acl_get_sensitivities() {
259 return acl_get_section_acos('sensitivities');
263 // Returns true if aco exist
264 // Returns false if aco doesn't exist
265 // $section_name = name of section (string)
266 // $aco_name = name of aco (string)
268 function aco_exist($section_name, $aco_name) {
269 global $phpgacl_location;
270 if (isset ($phpgacl_location)) {
271 include_once("$phpgacl_location/gacl_api.class.php");
272 $gacl = new gacl_api();
273 $aco_id = $gacl->get_object_id($section_name, $aco_name, 'ACO');
282 // Returns a sorted array of all available Group Titles.
284 function acl_get_group_title_list() {
285 global $phpgacl_location;
286 if (isset ($phpgacl_location)) {
287 include_once("$phpgacl_location/gacl_api.class.php");
288 $gacl = new gacl_api();
289 $parent_id = $gacl->get_root_group_id();
290 $arr_group_ids = $gacl->get_group_children($parent_id, 'ARO', 'RECURSE');
291 $arr_group_titles = array();
292 foreach ($arr_group_ids as $value) {
293 $arr_group_data = $gacl->get_group_data($value, 'ARO');
294 $arr_group_titles[$value] = $arr_group_data[3];
296 sort($arr_group_titles);
297 return $arr_group_titles;
303 // Returns a sorted array of group Titles that a user belongs to.
304 // Returns 0 if does not belong to any group yet.
305 // $user_name = Username, which is login name.
307 function acl_get_group_titles($user_name) {
308 global $phpgacl_location, $section_aro_value;
309 if (isset ($phpgacl_location)) {
310 include_once("$phpgacl_location/gacl_api.class.php");
311 $gacl = new gacl_api();
312 $user_aro_id = $gacl->get_object_id($section_aro_value, $user_name, 'ARO');
314 $arr_group_id = $gacl->get_object_groups($user_aro_id, 'ARO', 'NO_RECURSE');
316 foreach ($arr_group_id as $key => $value) {
317 $arr_group_data = $gacl->get_group_data($value, 'ARO');
318 $arr_group_titles[$key] = $arr_group_data[3];
320 sort($arr_group_titles);
321 return $arr_group_titles;
329 // This will place the user aro object into selected group(s)
330 // It uses the set_user_aro() function
331 // $username = username (string)
332 // $group = title of group(s) (string or array)
334 function add_user_aros($username, $group) {
335 $current_user_groups = acl_get_group_titles($username);
336 if (!$current_user_groups) {
337 $current_user_groups = array();
339 if (is_array($group)){
340 foreach ($group as $value) {
341 if (!in_array($value, $current_user_groups)) {
342 array_push($current_user_groups, $value);
347 if (!in_array($group, $current_user_groups)) {
348 array_push($current_user_groups, $group);
351 $user_data = mysql_fetch_array(sqlStatement("select * from users where username='" .
353 set_user_aro($current_user_groups, $username, $user_data["fname"],
354 $user_data["mname"], $user_data["lname"]);
359 // This will remove the user aro object from the selected group(s)
360 // It uses the set_user_aro() function
361 // $username = username (string)
362 // $group = title of group(s) (string or array)
364 function remove_user_aros($username, $group) {
365 $current_user_groups = acl_get_group_titles($username);
366 $new_user_groups = array();
367 if (is_array($group)){
368 foreach ($current_user_groups as $value) {
369 if (!in_array($value, $group)) {
370 array_push($new_user_groups, $value);
375 foreach ($current_user_groups as $value) {
376 if ($value != $group) {
377 array_push($new_user_groups, $value);
381 $user_data = mysql_fetch_array(sqlStatement("select * from users where username='" .
383 set_user_aro($new_user_groups, $username, $user_data["fname"],
384 $user_data["mname"], $user_data["lname"]);
389 // This will either create or edit a user aro object, and then place it
390 // in the requested groups. It will not allow removal of the 'admin'
391 // user or gacl_protected users from the 'admin' group.
392 // $arr_group_titles = titles of the groups that user will be added to.
393 // $user_name = username, which is login name.
394 // $first_name = first name
395 // $middle_name = middle name
396 // $last_name = last name
398 function set_user_aro($arr_group_titles, $user_name, $first_name, $middle_name, $last_name) {
399 global $phpgacl_location, $section_aro_value;
401 if (isset ($phpgacl_location)) {
402 include_once("$phpgacl_location/gacl_api.class.php");
403 $gacl = new gacl_api();
405 //see if this user is gacl protected (ie. do not allow
406 //removal from the Administrators group)
407 require_once(dirname(__FILE__).'/user.inc');
408 require_once(dirname(__FILE__).'/calendar.inc');
409 $userNametoID = getIDfromUser($user_name);
410 if (checkUserSetting("gacl_protect","1",$userNametoID) || $user_name == "admin") {
411 $gacl_protect = true;
414 $gacl_protect = false;
417 //get array of all available group ID numbers
418 $parent_id = $gacl->get_root_group_id();
419 $arr_all_group_ids = $gacl->get_group_children($parent_id, 'ARO', 'RECURSE');
421 //Cycle through ID array to find and process each selected group
422 //Create a counter since processing of first hit is unique
424 foreach ($arr_all_group_ids as $value) {
425 $arr_group_data = $gacl->get_group_data($value, 'ARO');
426 if ((empty($arr_group_titles)) ||
427 (in_array($arr_group_data[3], $arr_group_titles))) {
428 //We have a hit, so need to add group and increment counter
429 // because processing of first hit is unique
430 //This will also deal with an empty $arr_group_titles array
431 // removing user from all groups unless 'admin'
432 $counter = $counter + 1;
433 //create user full name field
435 $full_name = $first_name . " " . $middle_name . " " . $last_name;
439 $full_name = $first_name . " " . $last_name;
442 $full_name = $first_name;
446 //If this is not the first group to be added, then will skip below
447 // and will be added. If this is the first group, then need to
448 // go thru several steps before adding the group.
450 //get ID of user ARO object, if it exist
451 $user_aro_id = $gacl->get_object_id($section_aro_value, $user_name, 'ARO');
453 //user ARO object already exist, so will edit it
454 $gacl->edit_object($user_aro_id, $section_aro_value, $full_name, $user_name, 10, 0, 'ARO');
456 //remove all current user ARO object group associations
457 $arr_remove_group_ids = $gacl->get_object_groups($user_aro_id, 'ARO', 'NO_RECURSE');
458 foreach ($arr_remove_group_ids as $value2) {
459 $gacl->del_group_object($value2, $section_aro_value, $user_name, 'ARO');
463 //user ARO object does not exist, so will create it
464 $gacl->add_object($section_aro_value, $full_name, $user_name, 10, 0, 'ARO');
468 //place the user ARO object in the selected group (if group(s) is selected)
469 if (!empty($arr_group_titles)) {
470 $gacl->add_group_object($value, $section_aro_value, $user_name, 'ARO');
474 //Below will not allow 'admin' or gacl_protected user to be removed from 'admin' group
478 $admin_id = $gacl->get_object_id($section_aro_value, $user_name, 'ARO');
479 $arr_admin = $gacl->get_object_groups($admin_id, 'ARO', 'NO_RECURSE');
480 foreach ($arr_admin as $value3) {
481 $arr_admin_data = $gacl->get_group_data($value3, 'ARO');
482 if (strcmp($arr_admin_data[2], 'admin') == 0) {
486 if (!$boolean_admin) {
487 foreach ($arr_all_group_ids as $value4) {
488 $arr_temp = $gacl->get_group_data($value4, 'ARO');
489 if ($arr_temp[2] == 'admin') {
490 $gacl->add_group_object($value4, $section_aro_value, $user_name, 'ARO');
496 //if array of groups was empty, then we are done, and can break from loop
497 if (empty($arr_group_titles)) break;
505 // Returns true if acl exist
506 // Returns false if acl doesn't exist
507 // EITHER $title or $name is required(send FALSE in variable
508 // not being used). If both are sent, then only $title will be
510 // $return_value is required
511 // $title = title of acl (string)
512 // $name = name of acl (string)
513 // $return_value = return value of acl (string)
515 function acl_exist($title, $name, $return_value) {
516 global $phpgacl_location;
517 if (isset ($phpgacl_location)) {
518 include_once("$phpgacl_location/gacl_api.class.php");
519 $gacl = new gacl_api();
521 $acl = $gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $title, FALSE, FALSE, FALSE, $return_value);
524 $group_id = $gacl->get_group_id($name, NULL, 'ARO');
526 $group_data = $gacl->get_group_data($group_id, 'ARO');
527 $acl = $gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $group_data[3], FALSE, FALSE, FALSE, $return_value);
534 $acl = $gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $title, FALSE, FALSE, FALSE, $return_value);
546 // This will add a new acl and group(if group doesn't yet exist)
547 // with one aco in it.
548 // $acl_title = title of acl (string)
549 // $acl_name = name of acl (string)
550 // $return_value = return value of acl (string)
551 // $note = description of acl (array)
553 function acl_add($acl_title, $acl_name, $return_value, $note) {
554 global $phpgacl_location;
555 if (isset ($phpgacl_location)) {
556 include_once("$phpgacl_location/gacl_api.class.php");
557 $gacl = new gacl_api();
558 $group_id = $gacl->get_group_id($acl_name, $acl_title, 'ARO');
560 //group already exist, so just create acl
561 $gacl->add_acl(array("placeholder"=>array("filler")),
562 NULL, array($group_id), NULL, NULL, 1, 1, $return_value, $note);
565 //create group, then create acl
566 $parent_id = $gacl->get_root_group_id();
567 $aro_id = $gacl->add_group($acl_name, $acl_title, $parent_id, 'ARO');
568 $gacl->add_acl(array("placeholder"=>array("filler")),
569 NULL, array($aro_id), NULL, NULL, 1, 1, $return_value, $note);
577 // This will remove acl. It will also remove group(if the group
578 // is no longer associated with any acl's).
579 // $acl_title = title of acl (string)
580 // $acl_name = name of acl (string)
581 // $return_value = return value of acl (string)
582 // $note = description of acl (array)
584 function acl_remove($acl_title, $return_value) {
585 global $phpgacl_location;
586 if (isset ($phpgacl_location)) {
587 include_once("$phpgacl_location/gacl_api.class.php");
588 $gacl = new gacl_api();
589 //First, delete the acl
590 $acl_id=$gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $acl_title, FALSE, FALSE, FALSE, $return_value);
591 $gacl->del_acl($acl_id[0]);
592 //Then, remove the group(if no more acl's are remaining)
593 $acl_search=$gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $acl_title, FALSE, FALSE, FALSE, FALSE);
594 if (empty($acl_search)){
595 $group_id=$gacl-> get_group_id(NULL, $acl_title, 'ARO');
596 $gacl->del_group($group_id, TRUE, 'ARO');
604 // This will place the aco(s) into the selected acl
605 // $acl_title = title of acl (string)
606 // $return_value = return value of acl (string)
607 // $aco_id = id of aco (array)
609 function acl_add_acos($acl_title, $return_value, $aco_id) {
610 global $phpgacl_location;
611 if (isset ($phpgacl_location)) {
612 include_once("$phpgacl_location/gacl_api.class.php");
613 $gacl = new gacl_api();
614 $acl_id = $gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $acl_title, FALSE, FALSE, FALSE, $return_value);
615 foreach ($aco_id as $value) {
616 $aco_data = $gacl->get_object_data($value, 'ACO');
617 $aco_section = $aco_data[0][0];
618 $aco_name = $aco_data[0][1];
619 $gacl->append_acl($acl_id[0], NULL, NULL, NULL, NULL, array($aco_section=>array($aco_name)));
627 // This will remove the aco(s) from the selected acl
628 // Note if all aco's are removed, then will place the filler-placeholder
629 // into the acl to avoid complete removal of the acl.
630 // $acl_title = title of acl (string)
631 // $return_value = return value of acl (string)
632 // $aco_id = id of aco (array)
634 function acl_remove_acos($acl_title, $return_value, $aco_id) {
635 global $phpgacl_location;
636 if (isset ($phpgacl_location)) {
637 include_once("$phpgacl_location/gacl_api.class.php");
638 $gacl = new gacl_api();
639 $acl_id = $gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $acl_title, FALSE, FALSE, FALSE, $return_value);
641 // Check to see if removing all acos. If removing all acos then will
642 // ensure the filler-placeholder aco in acl to avoid complete
643 // removal of the acl.
644 if (count($aco_id) == acl_count_acos($acl_title, $return_value)) {
645 //1-get the filler-placeholder aco id
646 $filler_aco_id = $gacl->get_object_id('placeholder','filler','ACO');
647 //2-add filler-placeholder aco
648 acl_add_acos($acl_title, $return_value, array($filler_aco_id));
649 //3-ensure filler-placeholder aco is not to be deleted
650 $safeListaco = remove_element($_POST["selection"],$filler_aco_id);
651 //4-prepare to safely delete the acos
652 $aco_id = $safeListaco;
655 foreach ($aco_id as $value) {
656 $aco_data = $gacl->get_object_data($value, 'ACO');
657 $aco_section = $aco_data[0][0];
658 $aco_name = $aco_data[0][1];
659 $gacl->shift_acl($acl_id[0], NULL, NULL, NULL, NULL, array($aco_section=>array($aco_name)));
667 // This will return the number of aco objects
668 // in a specified acl.
669 // $acl_title = title of acl (string)
670 // $return_value = return value of acl (string)
672 function acl_count_acos($acl_title, $return_value) {
673 global $phpgacl_location;
674 if (isset ($phpgacl_location)) {
675 include_once("$phpgacl_location/gacl_api.class.php");
676 $gacl = new gacl_api();
677 $acl_id = $gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $acl_title, FALSE, FALSE, FALSE, $return_value);
678 $acl_data = $gacl->get_acl($acl_id[0]);
680 foreach ($acl_data['aco'] as $key => $value) {
681 $aco_count = $aco_count + count($acl_data['aco'][$key]);
689 // Function to remove an element from an array
691 function remove_element($arr, $val){
693 foreach ($arr as $value){
694 if ($value != $val) {
695 array_push($arr2,$value);