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