more internationalization and input validation project
[openemr.git] / library / acl.inc
blob6acb87ce1484a7aede218bb818c6ccb1fc69f37f
1 <?php
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
7 // below.
8  if (isset ($GLOBALS['fileroot'])) {
9   //normal use during OpenEMR
10   $phpgacl_location = $GLOBALS['fileroot'] . "/gacl";
11  }
12  else {  
13   //special case during OpenEMR and php-GACL install/upgrade scripts,
14   //which are run from main OpenEMR directory.
15   $phpgacl_location = "gacl";
16  }
18 // If using an external version of phpGACL, then uncomment the following
19 // line and manually place the path below.  IN THIS CASE YOU MUST ALSO
20 // COMMENT OUT BOTH $phpgacl_location ASSIGNMENTS ABOVE, OR BACKUPS WILL
21 // NOT RESTORE PROPERLY!
23 //$phpgacl_location = "/var/www/gacl";
25 // If you want to turn off php-GACL, then uncomment the following line.
26 // IN THIS CASE YOU MUST ALSO COMMENT OUT ALL $phpgacl_location ASSIGNMENTS
27 // ABOVE, OR BACKUPS WILL NOT RESTORE PROPERLY!
29 //unset($phpgacl_location);
32   // The following Access Control Objects (ACO) are currently supported.
33   // These are the "things to be protected":
34   //
35   // Section "admin" (Administration):
36   //   super       Superuser - can delete patients, encounters, issues
37   //   calendar    Calendar Settings
38   //   database    Database Reporting
39   //   forms       Forms Administration
40   //   practice    Practice Settings
41   //   superbill   Superbill Codes Administration
42   //   users       Users/Groups/Logs Administration
43   //   batchcom    Batch Communication Tool
44   //   language    Language Interface Tool
45   //   drugs       Pharmacy Dispensary
46   //   acl         ACL Administration
47   //
48   // Section "acct" (Accounting):
49   //   bill        Billing (write optional)
50   //   disc        Allowed to discount prices (in Fee Sheet or Checkout form)
51   //   eob         EOB Data Entry
52   //   rep         Financial Reporting - my encounters
53   //   rep_a       Financial Reporting - anything
54   //
55   // Section "patients" (Patient Information):
56   //   appt        Appointments (write optional)
57   //   demo        Demographics (write,addonly optional)
58   //   med         Medical Records and History (write,addonly optional)
59   //   trans       Transactions, e.g. referrals (write optional)
60   //   docs        Documents (write,addonly optional)
61   //   notes       Patient Notes (write,addonly optional)
62   //
63   // Section "encounters" (Encounter Information):
64   //   auth        Authorize - my encounters
65   //   auth_a      Authorize - any encounters
66   //   coding      Coding - my encounters (write,wsome optional)
67   //   coding_a    Coding - any encounters (write,wsome optional)
68   //   notes       Notes - my encounters (write,addonly optional)
69   //   notes_a     Notes - any encounters (write,addonly optional)
70   //   date_a      Fix encounter dates - any encounters
71   //   relaxed     Less-private information (write,addonly optional)
72   //               (e.g. the Sports Fitness encounter form)
73   //
74   // Section "squads" applies to sports team use only:
75   //   acos in this section define the user-specified list of squads
76   //
77   // Section "sensitivities" (Sensitivities):
78   //   normal     Normal
79   //   high       High
80   //
81   // Section "lists" (Lists):
82   //   default    Default List (write,addonly optional)
83   //   state      State List (write,addonly optional)
84   //   country    Country List (write,addonly optional)
85   //   language   Language List (write,addonly optional)
86   //   ethrace    Ethnicity-Race List (write,addonly optional)
87   //
88   // Section "placeholder" (Placeholder):
89   //   filler     Placeholder (Maintains empty ACLs)
90   
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';
97   }
99   // acl_check should return 0 if access is denied.  Otherwise it may
100   // return anything that evaluates to true.  In addition if any of the
101   // following types of access are applicable, then the corresponding value
102   // must be returned if and only if such access is granted (ony one may
103   // be specified):
104   //
105   // * write   - the user may add or modify the ACO
106   // * wsome   - the user has limited add/modify access to the ACO
107   // * addonly - the user may view and add but not modify entries
108   //
109   function acl_check($section, $value, $user = '') {
110     global $gacl_object, $phpgacl_location, $section_aro_value;
111     if (! $user) $user = $_SESSION['authUser'];
113     if ($phpgacl_location) {
114       return $gacl_object->acl_check($section, $value, $section_aro_value, $user);
115     }
117     // If no phpgacl, then apply the old static rules whereby "authorized"
118     // users (providers) can do anything, and other users can do most things.
119     // If you want custom access control but don't want to mess with phpGACL,
120     // then you could customize the code below instead.
122     if ($user == 'admin') return 'write';
123     if ($section == 'admin' && $value == 'super') return 0;
124     if ($_SESSION['userauthorized']) return 'write';
126     if ($section == 'patients') {
127       if ($value == 'med') return 1;
128       return 'write';
129     }
130     else if ($section == 'encounters') {
131       if (strpos($value, 'coding' ) === 0) return 'write';
132       if (strpos($value, 'notes'  ) === 0) return 'write';
133       if ($value == 'relaxed') return 'write';
134     }
135     else if ($section != 'admin') {
136       return 'write';
137     }
139     return 0;
140   }
142   // Get the ACO name/value pairs for a designated section.  Each value
143   // is an array (section_value, value, order_value, name, hidden).
144   //
145   function acl_get_section_acos($section) {
146     global $phpgacl_location;
147     if ($phpgacl_location) {
148       include_once("$phpgacl_location/gacl_api.class.php");
149       $gacl = new gacl_api();
150       $arr1 = $gacl->get_objects($section, 1, 'ACO');
151       $arr = array();
152       if (!empty($arr1[$section])) {
153         foreach ($arr1[$section] as $value) {
154           $odata = $gacl->get_object_data($gacl->get_object_id($section, $value, 'ACO'), 'ACO');
155           $arr[$value] = $odata[0];
156         }
157       }
158       return $arr;
159     }
160     return 0;
161   }
163   // Return an array keyed on squad ACO names.
164   // This is only applicable for sports team use.
165   //
166   function acl_get_squads() {
167     return acl_get_section_acos('squads');
168   }
170   // Return an array keyed on encounter sensitivity level ACO names.
171   // Sensitivities are useful when some encounter notes are not
172   // medically sensitive (e.g. a physical fitness test), and/or if
173   // some will be "for doctor's eyes only" (e.g. STD treatment).
174   //
175   // When a non-blank sensitivity value exists in the new encounter
176   // form, it names an additional ACO required for access to all forms
177   // in the encounter.  If you want some encounters to be non-sensitive,
178   // then you also need some default nonblank sensitivity for normal
179   // encounters, as well as greater encounter notes permissions for
180   // those allowed to view non-sensitive encounters.
181   //
182   function acl_get_sensitivities() {
183     return acl_get_section_acos('sensitivities');
184   }
186   //
187   // Returns true if aco exist
188   // Returns false if aco doesn't exist
189   //    $section_name = name of section (string)
190   //    $aco_name = name of aco (string)
191   //
192   function aco_exist($section_name, $aco_name) {
193    global $phpgacl_location;
194    if (isset ($phpgacl_location)) {
195     include_once("$phpgacl_location/gacl_api.class.php");
196     $gacl = new gacl_api();
197     $aco_id = $gacl->get_object_id($section_name,  $aco_name, 'ACO');
198     if ($aco_id) {
199      return true;
200     }
201    }
202    return false;
203   }
205   //
206   // Returns a sorted array of all available Group Titles.
207   //
208   function acl_get_group_title_list() {
209     global $phpgacl_location;
210     if (isset ($phpgacl_location)) {
211       include_once("$phpgacl_location/gacl_api.class.php");
212       $gacl = new gacl_api();
213       $parent_id = $gacl->get_root_group_id();
214       $arr_group_ids = $gacl->get_group_children($parent_id, 'ARO', 'RECURSE');
215       $arr_group_titles = array();
216       foreach ($arr_group_ids as $value) {
217         $arr_group_data = $gacl->get_group_data($value, 'ARO');
218         $arr_group_titles[$value] = $arr_group_data[3];
219       }
220       sort($arr_group_titles);
221       return $arr_group_titles;
222     }
223     return 0;
224   }
226   //
227   // Returns a sorted array of group Titles that a user belongs to.
228   // Returns 0 if does not belong to any group yet.
229   //   $user_name = Username, which is login name.
230   //
231   function acl_get_group_titles($user_name) {
232     global $phpgacl_location, $section_aro_value;
233     if (isset ($phpgacl_location)) {
234       include_once("$phpgacl_location/gacl_api.class.php");
235       $gacl = new gacl_api();
236       $user_aro_id = $gacl->get_object_id($section_aro_value, $user_name, 'ARO');
237       if ($user_aro_id) {
238         $arr_group_id = $gacl->get_object_groups($user_aro_id, 'ARO', 'NO_RECURSE');
239         if ($arr_group_id) {
240           foreach ($arr_group_id as $key => $value) {
241             $arr_group_data = $gacl->get_group_data($value, 'ARO');
242             $arr_group_titles[$key] =  $arr_group_data[3];
243           }
244         sort($arr_group_titles);
245         return $arr_group_titles;
246         }
247       }
248     }
249     return 0;
250   }
252   //
253   // This will place the user aro object into selected group(s)
254   // It uses the set_user_aro() function
255   //   $username = username (string)
256   //   $group = title of group(s) (string or array)
257   //
258   function add_user_aros($username, $group) {
259    $current_user_groups = acl_get_group_titles($username);
260    if (!$current_user_groups) {
261     $current_user_groups = array();
262    }
263    if (is_array($group)){
264     foreach ($group as $value) {
265        if (!in_array($value, $current_user_groups)) { 
266         array_push($current_user_groups, $value);
267        }
268     }
269    }
270    else {
271     if (!in_array($group, $current_user_groups)) {
272      array_push($current_user_groups, $group);
273     }
274    }
275    $user_data = mysql_fetch_array(sqlStatement("select * from users where username='" .
276     $username . "'"));
277    set_user_aro($current_user_groups, $username, $user_data["fname"],
278     $user_data["mname"], $user_data["lname"]);
279    return;
280   }
282   //
283   // This will remove the user aro object from the selected group(s)
284   // It uses the set_user_aro() function
285   //   $username = username (string)
286   //   $group = title of group(s) (string or array)
287   //
288   function remove_user_aros($username, $group) {
289    $current_user_groups = acl_get_group_titles($username);
290    $new_user_groups = array();
291    if (is_array($group)){
292     foreach ($current_user_groups as $value) {
293      if (!in_array($value, $group)) {
294       array_push($new_user_groups, $value);
295      }
296     }
297    }
298    else {
299     foreach ($current_user_groups as $value) {
300      if ($value != $group) {
301       array_push($new_user_groups, $value);
302      }
303     }
304    }
305    $user_data = mysql_fetch_array(sqlStatement("select * from users where username='" .
306     $username . "'"));
307    set_user_aro($new_user_groups, $username, $user_data["fname"],
308     $user_data["mname"], $user_data["lname"]);
309    return;
310   }
312   //
313   // This will either create or edit a user aro object, and then place it
314   // in the requested groups. It will not allow removal of the 'admin'
315   // user from the 'admin' group.
316   //   $arr_group_titles = titles of the groups that user will be added to.
317   //   $user_name = username, which is login name.
318   //   $first_name = first name
319   //   $middle_name = middle name
320   //   $last_name = last name
321   //
322   function set_user_aro($arr_group_titles, $user_name, $first_name, $middle_name, $last_name) {
323     global $phpgacl_location, $section_aro_value;
325     if (isset ($phpgacl_location)) {
326       include_once("$phpgacl_location/gacl_api.class.php");
327       $gacl = new gacl_api();
329       //get array of all available group ID numbers
330       $parent_id = $gacl->get_root_group_id();
331       $arr_all_group_ids = $gacl->get_group_children($parent_id, 'ARO', 'RECURSE');
333       //Cycle through ID array to find and process each selected group
334       //Create a counter since processing of first hit is unique
335       $counter = 0;
336       foreach ($arr_all_group_ids as $value) {
337         $arr_group_data = $gacl->get_group_data($value, 'ARO');
338         if ((empty($arr_group_titles)) ||
339          (in_array($arr_group_data[3], $arr_group_titles))) {
340           //We have a hit, so need to add group and increment counter
341           // because processing of first hit is unique
342           //This will also deal with an empty $arr_group_titles array
343           // removing user from all groups unless 'admin'
344           $counter = $counter + 1;
345           //create user full name field
346           if ($middle_name) {
347             $full_name = $first_name . " " . $middle_name . " " .  $last_name;
348           }
349           else {
350             if ($last_name) {
351               $full_name = $first_name . " " . $last_name;
352             }
353             else {
354               $full_name = $first_name;
355             }
356           }
358           //If this is not the first group to be added, then will skip below
359           // and will be added. If this is the first group, then need to
360           // go thru several steps before adding the group.
361           if ($counter == 1) {
362             //get ID of user ARO object, if it exist
363             $user_aro_id = $gacl->get_object_id($section_aro_value, $user_name, 'ARO');
364             if ($user_aro_id) {
365               //user ARO object already exist, so will edit it
366               $gacl->edit_object($user_aro_id, $section_aro_value, $full_name, $user_name, 10, 0, 'ARO');
368               //remove all current user ARO object group associations
369               $arr_remove_group_ids = $gacl->get_object_groups($user_aro_id, 'ARO', 'NO_RECURSE');
370               foreach ($arr_remove_group_ids as $value2) {
371                 $gacl->del_group_object($value2, $section_aro_value, $user_name, 'ARO');
372               }
373             }
374             else {
375               //user ARO object does not exist, so will create it
376               $gacl->add_object($section_aro_value, $full_name, $user_name, 10, 0, 'ARO');
377             }
378           }
380           //place the user ARO object in the selected group (if group(s) is selected)
381           if (!empty($arr_group_titles)) {
382             $gacl->add_group_object($value, $section_aro_value, $user_name, 'ARO');
383           }
385           //
386           //Below will not allow 'admin' user to be removed from 'admin' group
387           //
388           if ($user_name == 'admin') {
389             $boolean_admin=0;
390             $admin_id = $gacl->get_object_id($section_aro_value, 'admin', 'ARO');
391             $arr_admin = $gacl->get_object_groups($admin_id, 'ARO', 'NO_RECURSE');
392             foreach ($arr_admin as $value3) {
393               $arr_admin_data = $gacl->get_group_data($value3, 'ARO');
394               if (strcmp($arr_admin_data[2], 'admin') == 0) {
395                 $boolean_admin=1;
396               }
397             }
398             if (!$boolean_admin) {
399               foreach ($arr_all_group_ids as $value4) {
400                 $arr_temp = $gacl->get_group_data($value4, 'ARO');
401                 if ($arr_temp[2] == 'admin') {
402                   $gacl->add_group_object($value4, $section_aro_value, 'admin', 'ARO');
403                 }
404               }
405             }
406           }
407         }
408         //if array of groups was empty, then we are done, and can break from loop
409         if (empty($arr_group_titles)) break;
410       }
411     }
412    return;
413   }
415   //
416   // Returns true if acl exist
417   // Returns false if acl doesn't exist
418   //  EITHER $title or $name is required(send FALSE in variable
419   //  not being used). If both are sent, then only $title will be
420   //  used.
421   //  $return_value is required
422   //    $title = title of acl (string)
423   //    $name = name of acl (string)
424   //    $return_value = return value of acl (string)
425   //
426   function acl_exist($title, $name, $return_value) {
427    global $phpgacl_location;
428    if (isset ($phpgacl_location)) {
429     include_once("$phpgacl_location/gacl_api.class.php");
430     $gacl = new gacl_api();
431     if (!$name) {
432      $acl = $gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $title, FALSE, FALSE, FALSE, $return_value);
433     }
434     else if (!$title) {
435      $group_id = $gacl->get_group_id($name, NULL, 'ARO');
436      if ($group_id) {
437       $group_data = $gacl->get_group_data($group_id, 'ARO');
438       $acl = $gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $group_data[3], FALSE, FALSE, FALSE, $return_value);
439      }
440      else {
441      return false;
442      }
443     }
444     else {
445      $acl = $gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $title, FALSE, FALSE, FALSE, $return_value);
446     }
447     if (!empty($acl)) {
448      return true;
449     }
450     else {
451      return false;
452     }
453    }
454   }
456   //
457   // This will add a new acl and group(if group doesn't yet exist)
458   // with one aco in it.
459   //   $acl_title = title of acl (string)
460   //   $acl_name = name of acl (string)
461   //   $return_value = return value of acl (string)
462   //   $note = description of acl (array)
463   //
464   function acl_add($acl_title, $acl_name, $return_value, $note) {
465    global $phpgacl_location;
466    if (isset ($phpgacl_location)) {
467     include_once("$phpgacl_location/gacl_api.class.php");
468     $gacl = new gacl_api();
469     $group_id = $gacl->get_group_id($acl_name, $acl_title, 'ARO');
470     if ($group_id) {
471      //group already exist, so just create acl
472      $gacl->add_acl(array("placeholder"=>array("filler")),
473       NULL, array($group_id), NULL, NULL, 1, 1, $return_value, $note);
474     }
475     else {
476      //create group, then create acl
477      $parent_id = $gacl->get_root_group_id();
478      $aro_id = $gacl->add_group($acl_name, $acl_title, $parent_id, 'ARO');
479      $gacl->add_acl(array("placeholder"=>array("filler")),
480       NULL, array($aro_id), NULL, NULL, 1, 1, $return_value, $note);
481     }
482     return;
483    }
484    return 0;
485   }
487   //
488   // This will remove acl. It will also remove group(if the group
489   // is no longer associated with any acl's).
490   //   $acl_title = title of acl (string)
491   //   $acl_name = name of acl (string)
492   //   $return_value = return value of acl (string)
493   //   $note = description of acl (array)
494   //
495   function acl_remove($acl_title, $return_value) {
496    global $phpgacl_location;
497    if (isset ($phpgacl_location)) {
498     include_once("$phpgacl_location/gacl_api.class.php");
499     $gacl = new gacl_api();
500     //First, delete the acl
501     $acl_id=$gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $acl_title, FALSE, FALSE, FALSE, $return_value);
502     $gacl->del_acl($acl_id[0]);
503     //Then, remove the group(if no more acl's are remaining)
504     $acl_search=$gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $acl_title, FALSE, FALSE, FALSE, FALSE);
505     if (empty($acl_search)){
506      $group_id=$gacl-> get_group_id(NULL, $acl_title, 'ARO');
507      $gacl->del_group($group_id, TRUE, 'ARO');
508     }
509     return;
510    }
511    return 0;
512   }
514   //
515   // This will place the aco(s) into the selected acl
516   //   $acl_title = title of acl (string)
517   //   $return_value = return value of acl (string)
518   //   $aco_id = id of aco (array)
519   //
520   function acl_add_acos($acl_title, $return_value, $aco_id) {
521    global $phpgacl_location;
522    if (isset ($phpgacl_location)) {
523     include_once("$phpgacl_location/gacl_api.class.php");
524     $gacl = new gacl_api();
525     $acl_id = $gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $acl_title, FALSE, FALSE, FALSE, $return_value);
526     foreach ($aco_id as $value) { 
527      $aco_data = $gacl->get_object_data($value, 'ACO');
528      $aco_section = $aco_data[0][0];
529      $aco_name = $aco_data[0][1];   
530      $gacl->append_acl($acl_id[0], NULL, NULL, NULL, NULL, array($aco_section=>array($aco_name)));
531     }
532     return;
533    }
534    return 0;
535   }
537   //
538   // This will remove the aco(s) from the selected acl
539   //  Note if all aco's are removed, then will place the filler-placeholder
540   //  into the acl to avoid complete removal of the acl.
541   //   $acl_title = title of acl (string)
542   //   $return_value = return value of acl (string)
543   //   $aco_id = id of aco (array)
544   //
545   function acl_remove_acos($acl_title, $return_value, $aco_id) {
546    global $phpgacl_location;
547    if (isset ($phpgacl_location)) {
548     include_once("$phpgacl_location/gacl_api.class.php");
549     $gacl = new gacl_api();
550     $acl_id = $gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $acl_title, FALSE, FALSE, FALSE, $return_value);
552     // Check to see if removing all acos. If removing all acos then will
553     //  ensure the filler-placeholder aco in acl to avoid complete
554     //  removal of the acl.
555     if (count($aco_id) == acl_count_acos($acl_title, $return_value)) {
556      //1-get the filler-placeholder aco id
557      $filler_aco_id = $gacl->get_object_id('placeholder','filler','ACO');     
558      //2-add filler-placeholder aco
559      acl_add_acos($acl_title, $return_value, array($filler_aco_id));
560      //3-ensure filler-placeholder aco is not to be deleted
561      $safeListaco = remove_element($_POST["selection"],$filler_aco_id);
562      //4-prepare to safely delete the acos
563      $aco_id = $safeListaco;
564     }
566     foreach ($aco_id as $value) {
567      $aco_data = $gacl->get_object_data($value, 'ACO');
568      $aco_section = $aco_data[0][0];
569      $aco_name = $aco_data[0][1];
570      $gacl->shift_acl($acl_id[0], NULL, NULL, NULL, NULL, array($aco_section=>array($aco_name)));
571      }
572     return;
573    }
574    return 0;
575   }
577   //
578   // This will return the number of aco objects
579   //  in a specified acl.
580   //   $acl_title = title of acl (string)
581   //   $return_value = return value of acl (string)
582   //
583   function acl_count_acos($acl_title, $return_value) {
584    global $phpgacl_location;
585    if (isset ($phpgacl_location)) {
586     include_once("$phpgacl_location/gacl_api.class.php");
587     $gacl = new gacl_api();
588     $acl_id = $gacl->search_acl(FALSE, FALSE, FALSE, FALSE, $acl_title, FALSE, FALSE, FALSE, $return_value);
589     $acl_data = $gacl->get_acl($acl_id[0]);
590     $aco_count = 0;
591     foreach ($acl_data['aco'] as $key => $value) {
592      $aco_count = $aco_count + count($acl_data['aco'][$key]);
593     }
594     return $aco_count;
595    }
596    return 0;
597   }
599   //
600   // Function to remove an element from an array
601   //
602   function remove_element($arr, $val){
603    $arr2 = array();
604    foreach ($arr as $value){
605     if ($value != $val) {
606      array_push($arr2,$value);
607     }
608    }
609    return $arr2;
610   }