PageRenderTime 57ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/library/log.inc

https://bitbucket.org/astawiarski/openemr
PHP | 767 lines | 726 code | 14 blank | 27 comment | 57 complexity | c350f4796a1972df3df2b5995bfdb591 MD5 | raw file
Possible License(s): LGPL-2.1, AGPL-1.0, GPL-2.0, MPL-2.0
  1. <?php
  2. #require_once("{$GLOBALS['srcdir']}/sql.inc");
  3. require_once(dirname(__FILE__). "/sql.inc");
  4. require_once(dirname(__FILE__). "/formdata.inc.php");
  5. function newEvent($event, $user, $groupname, $success, $comments="") {
  6. $adodb = $GLOBALS['adodb']['db'];
  7. $crt_user=isset($_SERVER['SSL_CLIENT_S_DN_CN']) ? $_SERVER['SSL_CLIENT_S_DN_CN'] : null;
  8. /* More details added to the log */
  9. $sql = "insert into log ( date, event, user, groupname, success, comments, crt_user) " .
  10. "values ( NOW(), " . $adodb->qstr($event) . "," . $adodb->qstr($user) .
  11. "," . $adodb->qstr($groupname) . "," . $adodb->qstr($success) . "," .
  12. $adodb->qstr($comments) ."," .
  13. $adodb->qstr($crt_user) . ")";
  14. $ret = sqlInsertClean_audit($sql);
  15. send_atna_audit_msg($user, $groupname, $event, 0, $success, $comments);
  16. }
  17. function getEventByDate($date, $user="", $cols="DISTINCT date, event, user, groupname, patient_id, success, comments, checksum")
  18. {
  19. $sql = "SELECT $cols FROM log WHERE date >= '$date 00:00:00' AND date <= '$date 23:59:59'";
  20. if ($user) $sql .= " AND user LIKE '$user'";
  21. $sql .= " ORDER BY date DESC LIMIT 5000";
  22. $res = sqlStatement($sql);
  23. for($iter=0; $row=sqlFetchArray($res); $iter++) {
  24. $all[$iter] = $row;
  25. }
  26. return $all;
  27. }
  28. /******************
  29. * Get records from the LOG and Extended_Log table
  30. * using the optional parameters:
  31. * date : a specific date (defaults to today)
  32. * user : a specific user (defaults to none)
  33. * cols : gather specific columns (defaults to date,event,user,groupname,comments)
  34. * sortby : sort the results by (defaults to none)
  35. * RETURNS:
  36. * array of results
  37. ******************/
  38. function getEvents($params)
  39. {
  40. // parse the parameters
  41. $cols = "DISTINCT date, event, user, groupname, patient_id, success, comments,checksum,crt_user";
  42. if (isset($params['cols']) && $params['cols'] != "") $cols = $params['cols'];
  43. $date1 = date("Y-m-d", time());
  44. if (isset($params['sdate']) && $params['sdate'] != "") $date1= $params['sdate'];
  45. $date2 = date("Y-m-d", time());
  46. if (isset($params['edate']) && $params['edate'] != "") $date2= $params['edate'];
  47. $user = "";
  48. if (isset($params['user']) && $params['user'] != "") $user= $params['user'];
  49. //VicarePlus :: For Generating log with patient id.
  50. $patient = "";
  51. if (isset($params['patient']) && $params['patient'] != "") $patient= $params['patient'];
  52. $sortby = "";
  53. if (isset($params['sortby']) && $params['sortby'] != "") $sortby = $params['sortby'];
  54. $levent = "";
  55. if (isset($params['levent']) && $params['levent'] != "") $levent = $params['levent'];
  56. $tevent = "";
  57. if (isset($params['tevent']) && $params['tevent'] != "") $tevent = $params['tevent'];
  58. $event = "";
  59. if (isset($params['event']) && $params['event'] != "") $event = $params['event'];
  60. if ($event!=""){
  61. if ($sortby == "comments") $sortby = "description";
  62. if ($sortby == "groupname") $sortby = ""; //VicarePlus :: since there is no groupname in extended_log
  63. if ($sortby == "success") $sortby = ""; //VicarePlus :: since there is no success field in extended_log
  64. if ($sortby == "checksum") $sortby = ""; //VicarePlus :: since there is no checksum field in extended_log
  65. $columns = "DISTINCT date, event, user, recipient,patient_id,description";
  66. $sql = "SELECT $columns FROM extended_log WHERE date >= '$date1 00:00:00' AND date <= '$date2 23:59:59'";
  67. if ($user != "") $sql .= " AND user LIKE '$user'";
  68. if ($patient != "") $sql .= " AND patient_id LIKE '$patient'";
  69. if ($levent != "") $sql .= " AND event LIKE '$levent%'";
  70. if ($sortby != "") $sql .= " ORDER BY ".$sortby." DESC "; // descending order
  71. $sql .= " LIMIT 5000";
  72. }
  73. else
  74. {
  75. // do the query
  76. $sql = "SELECT $cols FROM log WHERE date >= '$date1 00:00:00' AND date <= '$date2 23:59:59'";
  77. if ($user != "") $sql .= " AND user LIKE '$user'";
  78. if ($patient != "") $sql .= " AND patient_id LIKE '$patient'";
  79. if ($levent != "") $sql .= " AND event LIKE '$levent%'";
  80. if ($tevent != "") $sql .= " AND event LIKE '%$tevent'";
  81. if ($sortby != "") $sql .= " ORDER BY ".$sortby." DESC "; // descending order
  82. $sql .= " LIMIT 5000";
  83. }
  84. $res = sqlStatement($sql);
  85. for($iter=0; $row=sqlFetchArray($res); $iter++) {
  86. $all[$iter] = $row;
  87. }
  88. return $all;
  89. }
  90. /* Given an SQL insert/update that was just performeds:
  91. * - Find the table and primary id of the row that was created/modified
  92. * - Calculate the SHA1 checksum of that row (with all the
  93. * column values concatenated together).
  94. * - Return the SHA1 checksum as a 40 char hex string.
  95. * If this is not an insert/update query, return "".
  96. * If multiple rows were modified, return "".
  97. * If we're unable to determine the row modified, return "".
  98. *
  99. * TODO: May need to incorporate the binded stuff (still analyzing)
  100. *
  101. */
  102. function sql_checksum_of_modified_row($statement)
  103. {
  104. $table = "";
  105. $rid = "";
  106. $tokens = preg_split("/[\s,(\'\"]+/", $statement);
  107. /* Identifying the id for insert/replace statements for calculating the checksum */
  108. if((strcasecmp($tokens[0],"INSERT")==0) || (strcasecmp($tokens[0],"REPLACE")==0)){
  109. $table = $tokens[2];
  110. $rid = mysql_insert_id($GLOBALS['dbh']);
  111. /* For handling the table that doesn't have auto-increment column */
  112. if ($rid === 0 || $rid === FALSE) {
  113. if($table == "gacl_aco_map" || $table == "gacl_aro_groups_map" || $table == "gacl_aro_map" || $table == "gacl_axo_groups_map" || $table == "gacl_axo_map")
  114. $id="acl_id";
  115. else if($table == "gacl_groups_aro_map" || $table == "gacl_groups_axo_map")
  116. $id="group_id";
  117. else
  118. $id="id";
  119. /* To handle insert statements */
  120. if($tokens[3] == $id){
  121. for($i=4;$i<count($tokens);$i++){
  122. if(strcasecmp($tokens[$i],"VALUES")==0){
  123. $rid=$tokens[$i+1];
  124. break;
  125. }// if close
  126. }//for close
  127. }//if close
  128. /* To handle replace statements */
  129. else if(strcasecmp($tokens[3],"SET")==0){
  130. if((strcasecmp($tokens[4],"ID")==0) || (strcasecmp($tokens[4],"`ID`")==0)){
  131. $rid=$tokens[6];
  132. }// if close
  133. }
  134. else {
  135. return "";
  136. }
  137. }
  138. }
  139. /* Identifying the id for update statements for calculating the checksum */
  140. else if(strcasecmp($tokens[0],"UPDATE")==0){
  141. $table = $tokens[1];
  142. $offset = 3;
  143. $total = count($tokens);
  144. /* Identifying the primary key column for the updated record */
  145. if ($table == "form_physical_exam") {
  146. $id = "forms_id";
  147. }
  148. else if ($table == "claims"){
  149. $id = "patient_id";
  150. }
  151. else if ($table == "openemr_postcalendar_events") {
  152. $id = "pc_eid";
  153. }
  154. else if ($table == "lang_languages"){
  155. $id = "lang_id";
  156. }
  157. else if ($table == "openemr_postcalendar_categories" || $table == "openemr_postcalendar_topics"){
  158. $id = "pc_catid";
  159. }
  160. else if ($table == "openemr_postcalendar_limits"){
  161. $id = "pc_limitid";
  162. }
  163. else if($table == "gacl_aco_map" || $table == "gacl_aro_groups_map" || $table == "gacl_aro_map" || $table == "gacl_axo_groups_map" || $table == "gacl_axo_map"){
  164. $id="acl_id";
  165. }
  166. else if($table == "gacl_groups_aro_map" || $table == "gacl_groups_axo_map"){
  167. $id="group_id";
  168. }
  169. else {
  170. $id = "id";
  171. }
  172. /* Identifying the primary key value for the updated record */
  173. while ($offset < $total) {
  174. /* There are 4 possible ways that the id=123 can be parsed:
  175. * ('id', '=', '123')
  176. * ('id=', '123')
  177. * ('id=123')
  178. * ('id', '=123')
  179. */
  180. $rid = "";
  181. /*id=', '123'*/
  182. if (($tokens[$offset] == "$id=") && ($offset + 1 < $total)) {
  183. $rid = $tokens[$offset+1];
  184. break;
  185. }
  186. /* 'id', '=', '123' */
  187. else if ($tokens[$offset] == "$id" && $tokens[$offset+1] == "=" && ($offset+2 < $total)) {
  188. $rid = $tokens[$offset+2];
  189. break;
  190. }
  191. /*id=123*/
  192. else if (strpos($tokens[$offset], "$id=") === 0) {
  193. $tid = substr($tokens[$offset], strlen($id)+1);
  194. if(is_numeric($tid))
  195. $rid=$tid;
  196. break;
  197. }
  198. /*'id', '=123' */
  199. else if($tokens[$offset] == "$id") {
  200. $tid = substr($tokens[$offset+1],1);
  201. if(is_numeric($tid))
  202. $rid=$tid;
  203. break;
  204. }
  205. $offset += 1;
  206. }//while ($offset < $total)
  207. }// else if ($tokens[0] == 'update' || $tokens[0] == 'UPDATE' )
  208. if ($table == "" || $rid == "") {
  209. return "";
  210. }
  211. /* Framing sql statements for calculating checksum */
  212. if ($table == "form_physical_exam") {
  213. $sql = "select * from $table where forms_id = $rid";
  214. }
  215. else if ($table == "claims"){
  216. $sql = "select * from $table where patient_id = $rid";
  217. }
  218. else if ($table == "openemr_postcalendar_events") {
  219. $sql = "select * from $table where pc_eid = $rid";
  220. }
  221. else if ($table == "lang_languages") {
  222. $sql = "select * from $table where lang_id = $rid";
  223. }
  224. else if ($table == "openemr_postcalendar_categories" || $table == "openemr_postcalendar_topics"){
  225. $sql = "select * from $table where pc_catid = $rid";
  226. }
  227. else if ($table == "openemr_postcalendar_limits"){
  228. $sql = "select * from $table where pc_limitid = $rid";
  229. }
  230. else if ($table == "gacl_aco_map" || $table == "gacl_aro_groups_map" || $table == "gacl_aro_map" || $table == "gacl_axo_groups_map" || $table == "gacl_axo_map"){
  231. $sql = "select * from $table where acl_id = $rid";
  232. }
  233. else if($table == "gacl_groups_aro_map" || $table == "gacl_groups_axo_map"){
  234. $sql = "select * from $table where group_id = $rid";
  235. }
  236. else {
  237. $sql = "select * from $table where id = $rid";
  238. }
  239. // When this function is working perfectly, can then shift to the
  240. // sqlQueryNoLog() function.
  241. $results = sqlQueryNoLogIgnoreError($sql);
  242. $column_values = "";
  243. /* Concatenating the column values for the row inserted/updated */
  244. if (is_array($results)) {
  245. foreach ($results as $field_name => $field) {
  246. $column_values .= $field;
  247. }
  248. }
  249. // ViCarePlus: As per NIST standard, the encryption algorithm SHA1 is used
  250. //error_log("COLUMN_VALUES: ".$column_values,0);
  251. return sha1($column_values);
  252. }
  253. /* Create an XML audit record corresponding to RFC 3881.
  254. * The parameters passed are the column values (from table 'log')
  255. * for a single audit record.
  256. */
  257. function create_rfc3881_msg($user, $group, $event, $patient_id, $outcome, $comments)
  258. {
  259. /* Event action codes indicate whether the event is read/write.
  260. * C = create, R = read, U = update, D = delete, E = execute
  261. */
  262. $eventActionCode = 'E';
  263. if (substr($event, -7) == "-create") {
  264. $eventActionCode = 'C';
  265. }
  266. else if (substr($event, -7) == "-insert") {
  267. $eventActionCode = 'C';
  268. }
  269. else if (substr($event, -7) == "-select") {
  270. $eventActionCode = 'R';
  271. }
  272. else if (substr($event, -7) == "-update") {
  273. $eventActionCode = 'U';
  274. }
  275. else if (substr($event, -7) == "-delete") {
  276. $eventActionCode = 'D';
  277. }
  278. $date_obj = new DateTime();
  279. $eventDateTime = $date_obj->format(DATE_ATOM);
  280. /* For EventOutcomeIndicator, 0 = success and 4 = minor error */
  281. $eventOutcome = ($outcome === 1) ? 0 : 4;
  282. /* The choice of event codes is up to OpenEMR.
  283. * We're using the same event codes as
  284. * https://iheprofiles.projects.openhealthtools.org/
  285. */
  286. $eventIDcodeSystemName = "DCM";
  287. $eventIDcode = 0;
  288. $eventIDdisplayName = $event;
  289. if (strpos($event, 'patient-record') !== FALSE) {
  290. $eventIDcode = 110110;
  291. $eventIDdisplayName = 'Patient Record';
  292. }
  293. else if (strpos($event, 'view') !== FALSE) {
  294. $eventIDCode = 110110;
  295. $eventIDdisplayName = 'Patient Record';
  296. }
  297. else if (strpos($event, 'login') !== FALSE) {
  298. $eventIDcode = 110122;
  299. $eventIDdisplayName = 'Login';
  300. }
  301. else if (strpos($event, 'logout') !== FALSE) {
  302. $eventIDcode = 110123;
  303. $eventIDdisplayName = 'Logout';
  304. }
  305. else if (strpos($event, 'scheduling') !== FALSE) {
  306. $eventIDcode = 110111;
  307. $eventIDdisplayName = 'Patient Care Assignment';
  308. }
  309. else if (strpos($event, 'security-administration') !== FALSE) {
  310. $eventIDcode = 110129;
  311. $eventIDdisplayName = 'Security Administration';
  312. }
  313. /* Variables used in ActiveParticipant section, which identifies
  314. * the IP address and application of the source and destination.
  315. */
  316. $srcUserID = $_SERVER['SERVER_NAME'] . '|OpenEMR';
  317. $srcNetwork = $_SERVER['SERVER_ADDR'];
  318. $destUserID = $GLOBALS['atna_audit_host'];
  319. $destNetwork = $GLOBALS['atna_audit_host'];
  320. $userID = $user;
  321. $userTypeCode = 1;
  322. $userRole = 6;
  323. $userCode = 11;
  324. $userDisplayName = 'User Identifier';
  325. $patientID = "";
  326. $patientTypeCode = "";
  327. $patientRole = "";
  328. $patientCode = "";
  329. $patientDisplayName = "";
  330. if ($eventIdDisplayName == 'Patient Record') {
  331. $patientID = $patient_id;
  332. $pattientTypeCode = 1;
  333. $patientRole = 1;
  334. $patientCode = 2;
  335. $patientDisplayName = 'Patient Number';
  336. }
  337. /* Construct the XML audit message, and save to $msg */
  338. $msg = '<?xml version="1.0" encoding="ASCII"?>';
  339. $msg .= '<AuditMessage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ';
  340. $msg .= 'xsi:noNamespaceSchemaLocation="healthcare-security-audit.xsd">';
  341. /* Indicate the event code, text name, read/write type, and date/time */
  342. $msg .= "<EventIdentification EventActionCode=\"$eventActionCode\" ";
  343. $msg .= "EventDateTime=\"$eventDateTime\" ";
  344. $msg .= "EventOutcomeIndicator=\"$eventOutcome\">";
  345. $msg .= "<EventID code=\"eventIDcode\" displayName=\"$eventIDdisplayName\" ";
  346. $msg .= "codeSystemName=\"DCM\" />";
  347. $msg .= "</EventIdentification>";
  348. /* Indicate the IP address and application of the source and destination */
  349. $msg .= "<ActiveParticipant UserID=\"$srcUserID\" UserIsRequestor=\"true\" ";
  350. $msg .= "NetworkAccessPointID=\"$srcNetwork\" NetworkAccessPointTypeCode=\"2\" >";
  351. $msg .= "<RoleIDCode code=\"110153\" displayName=\"Source\" codeSystemName=\"DCM\" />";
  352. $msg .= "</ActiveParticipant>";
  353. $msg .= "<ActiveParticipant UserID=\"$destUserID\" UserIsRequestor=\"false\" ";
  354. $msg .= "NetworkAccessPointID=\"$destNetwork\" NetworkAccessPointTypeCode=\"2\" >";
  355. $msg .= "<RoleIDCode code=\"110152\" displayName=\"Destination\" codeSystemName=\"DCM\" />";
  356. $msg .= "</ActiveParticipant>";
  357. $msg .= "<AuditSourceIdentification AuditSourceID=\"$srcUserID\" />";
  358. /* Indicate the username who generated this audit record */
  359. $msg .= "<ParticipantObjectIdentification ParticipantObjectID=\"$user\" ";
  360. $msg .= "ParticipantObjectTypeCode=\"1\" ";
  361. $msg .= "ParticipantObjectTypeCodeRole=\"6\" >";
  362. $msg .= "<ParticipantObjectIDTypeCode code=\"11\" ";
  363. $msg .= "displayName=\"User Identifier\" ";
  364. $msg .= "codeSystemName=\"RFC-3881\" /></ParticipantObjectIdentification>";
  365. if ($eventIDdisplayName == 'Patient Record' && $patient_id != 0) {
  366. $msg .= "<ParticipantObjectIdentification ParticipantObjectID=\"$patient_id\" ";
  367. $msg .= "ParticipantObjectTypeCode=\"1\" ";
  368. $msg .= "ParticipantObjectTypeCodeRole=\"1\" >";
  369. $msg .= "<ParticipantObjectIDTypeCode code=\"2\" ";
  370. $msg .= "displayName=\"Patient Number\" ";
  371. $msg .= "codeSystemName=\"RFC-3881\" /></ParticipantObjectIdentification>";
  372. }
  373. $msg .= "</AuditMessage>";
  374. /* Add the syslog header */
  375. $date_obj = new DateTime($date);
  376. $datestr= $date_obj->format(DATE_ATOM);
  377. $msg = "<13> " . $datestr . " " . $_SERVER['SERVER_NAME'] . " " . $msg;
  378. return $msg;
  379. }
  380. /* Create a TLS (SSLv3) connection to the given host/port.
  381. * $localcert is the path to a PEM file with a client certificate and private key.
  382. * $cafile is the path to the CA certificate file, for
  383. * authenticating the remote machine's certificate.
  384. * If $cafile is "", the remote machine's certificate is not verified.
  385. * If $localcert is "", we don't pass a client certificate in the connection.
  386. *
  387. * Return a stream resource that can be used with fwrite(), fread(), etc.
  388. * Returns FALSE on error.
  389. */
  390. function create_tls_conn($host, $port, $localcert, $cafile) {
  391. $sslopts = array();
  392. if ($cafile !== null && $cafile != "") {
  393. $sslopts['cafile'] = $cafile;
  394. $sslopts['verify_peer'] = TRUE;
  395. $sslopts['verify_depth'] = 10;
  396. }
  397. if ($localcert !== null && $localcert != "") {
  398. $sslopts['local_cert'] = $localcert;
  399. }
  400. $opts = array('tls' => $sslopts, 'ssl' => $sslopts);
  401. $ctx = stream_context_create($opts);
  402. $timeout = 60;
  403. $flags = STREAM_CLIENT_CONNECT;
  404. $olderr = error_reporting(0);
  405. $conn = stream_socket_client('tls://' . $host . ":" . $port, $errno, $errstr,
  406. $timeout, $flags, $ctx);
  407. error_reporting($olderr);
  408. return $conn;
  409. }
  410. /* This function is used to send audit records to an Audit Repository Server,
  411. * as described in the Audit Trail and Node Authentication (ATNA) standard.
  412. * Given the fields in a single audit record:
  413. * - Create an XML audit message according to RFC 3881, including the RFC5425 syslog header.
  414. * - Create a TLS connection that performs bi-directions certificate authentication,
  415. * according to RFC 5425.
  416. * - Send the XML message on the TLS connection.
  417. */
  418. function send_atna_audit_msg($user, $group, $event, $patient_id, $outcome, $comments)
  419. {
  420. /* If no ATNA repository server is configured, return */
  421. if ($GLOBALS['atna_audit_host'] === null || $GLOBALS['atna_audit_host'] == "" || !($GLOBALS['enable_atna_audit'])) {
  422. return;
  423. }
  424. $host = $GLOBALS['atna_audit_host'];
  425. $port = $GLOBALS['atna_audit_port'];
  426. $localcert = $GLOBALS['atna_audit_localcert'];
  427. $cacert = $GLOBALS['atna_audit_cacert'];
  428. $conn = create_tls_conn($host, $port, $localcert, $cacert);
  429. if ($conn !== FALSE) {
  430. $msg = create_rfc3881_msg($user, $group, $event, $patient_id, $outcome, $comments);
  431. $len = strlen($msg);
  432. fwrite($conn, $msg);
  433. fclose($conn);
  434. }
  435. }
  436. /* Add an entry into the audit log table, indicating that an
  437. * SQL query was performed. $outcome is true if the statement
  438. * successfully completed. Determine the event type based on
  439. * the tables present in the SQL query.
  440. */
  441. function auditSQLEvent($statement, $outcome, $binds=NULL)
  442. {
  443. $user = isset($_SESSION['authUser']) ? $_SESSION['authUser'] : "";
  444. /* Don't log anything if the audit logging is not enabled. Exception for "emergency" users */
  445. if (!isset($GLOBALS['enable_auditlog']) || !($GLOBALS['enable_auditlog']))
  446. {
  447. if ((soundex($user) != soundex("emergency")) && (soundex($user) != soundex("breakglass")))
  448. return;
  449. }
  450. $statement = trim($statement);
  451. /* Don't audit SQL statements done to the audit log,
  452. * or we'll have an infinite loop.
  453. */
  454. if ((stripos($statement, "insert into log") !== FALSE) ||
  455. (stripos($statement, "FROM log ") !== FALSE) ) {
  456. return;
  457. }
  458. $group = isset($_SESSION['authGroup']) ? $_SESSION['authGroup'] : "";
  459. $comments = $statement;
  460. $processed_binds = "";
  461. if (is_array($binds)) {
  462. // Need to include the binded variable elements in the logging
  463. $first_loop=true;
  464. foreach ($binds as $value_bind) {
  465. if ($first_loop) {
  466. //no comma
  467. $processed_binds .= "'" . add_escape_custom($value_bind) . "'";
  468. $first_loop=false;
  469. }
  470. else {
  471. //add a comma
  472. $processed_binds .= ",'" . add_escape_custom($value_bind) . "'";
  473. }
  474. }
  475. if (!empty($processed_binds)) {
  476. $processed_binds = "(" . $processed_binds . ")";
  477. $comments .= " " . $processed_binds;
  478. }
  479. }
  480. $success = 1;
  481. $checksum = "";
  482. if ($outcome === FALSE) {
  483. $success = 0;
  484. }
  485. if ($outcome !== FALSE) {
  486. // Should use the $statement rather than the processed
  487. // variables, which includes the binded stuff. If do
  488. // indeed need the binded values, then will need
  489. // to include this as a separate array.
  490. //error_log("STATEMENT: ".$statement,0);
  491. //error_log("BINDS: ".$processed_binds,0);
  492. $checksum = sql_checksum_of_modified_row($statement);
  493. //error_log("CHECKSUM: ".$checksum,0);
  494. }
  495. /* Determine the query type (select, update, insert, delete) */
  496. $querytype = "select";
  497. $querytypes = array("select", "update", "insert", "delete","replace");
  498. foreach ($querytypes as $qtype) {
  499. if (stripos($statement, $qtype) === 0) {
  500. $querytype = $qtype;
  501. }
  502. }
  503. /* Determine the audit event based on the database tables */
  504. $event = "other";
  505. $tables = array("billing" => "patient-record",
  506. "claims" => "patient-record",
  507. "employer_data" => "patient-record",
  508. "forms" => "patient-record",
  509. "form_encounter" => "patient-record",
  510. "form_dictation" => "patient-record",
  511. "form_misc_billing_options" => "patient-record",
  512. "form_reviewofs" => "patient-record",
  513. "form_ros" => "patient-record",
  514. "form_soap" => "patient-record",
  515. "form_vitals" => "patient-record",
  516. "history_data" => "patient-record",
  517. "immunizations" => "patient-record",
  518. "insurance_data" => "patient-record",
  519. "issue_encounter" => "patient-record",
  520. "lists" => "patient-record",
  521. "patient_data" => "patient-record",
  522. "payments" => "patient-record",
  523. "pnotes" => "patient-record",
  524. "onotes" => "patient-record",
  525. "prescriptions" => "order",
  526. "transactions" => "patient-record",
  527. "facility" => "security-administration",
  528. "pharmacies" => "security-administration",
  529. "addresses" => "security-administration",
  530. "phone_numbers" => "security-administration",
  531. "x12_partners" => "security-administration",
  532. "insurance_companies" => "security-administration",
  533. "codes" => "security-administration",
  534. "registry" => "security-administration",
  535. "users" => "security-administration",
  536. "groups" => "security-administration",
  537. "openemr_postcalendar_events" => "scheduling",
  538. "openemr_postcalendar_categories" => "security-administration",
  539. "openemr_postcalendar_limits" => "security-administration",
  540. "openemr_postcalendar_topics" => "security-administration",
  541. "gacl_acl" => "security-administration",
  542. "gacl_acl_sections" => "security-administration",
  543. "gacl_acl_seq" => "security-administration",
  544. "gacl_aco" => "security-administration",
  545. "gacl_aco_map" => "security-administration",
  546. "gacl_aco_sections" => "security-administration",
  547. "gacl_aco_sections_seq" => "security-administration",
  548. "gacl_aco_seq" => "security-administration",
  549. "gacl_aro" => "security-administration",
  550. "gacl_aro_groups" => "security-administration",
  551. "gacl_aro_groups_id_seq" => "security-administration",
  552. "gacl_aro_groups_map" => "security-administration",
  553. "gacl_aro_map" => "security-administration",
  554. "gacl_aro_sections" => "security-administration",
  555. "gacl_aro_sections_seq" => "security-administration",
  556. "gacl_aro_seq" => "security-administration",
  557. "gacl_axo" => "security-administration",
  558. "gacl_axo_groups" => "security-administration",
  559. "gacl_axo_groups_map" => "security-administration",
  560. "gacl_axo_map" => "security-administration",
  561. "gacl_axo_sections" => "security-administration",
  562. "gacl_groups_aro_map" => "security-administration",
  563. "gacl_groups_axo_map" => "security-administration",
  564. "gacl_phpgacl" => "security-administration"
  565. );
  566. /* When searching for table names, truncate the SQL statement,
  567. * removing any WHERE, SET, or VALUE clauses.
  568. */
  569. $truncated_sql = $statement;
  570. $truncated_sql = str_replace("\n", " ", $truncated_sql);
  571. if ($querytype == "select") {
  572. $startwhere = stripos($truncated_sql, " where ");
  573. if ($startwhere > 0) {
  574. $truncated_sql = substr($truncated_sql, 0, $startwhere);
  575. }
  576. }
  577. else {
  578. $startparen = stripos($truncated_sql, "(" );
  579. $startset = stripos($truncated_sql, " set ");
  580. $startvalues = stripos($truncated_sql, " values ");
  581. if ($startparen > 0) {
  582. $truncated_sql = substr($truncated_sql, 0, $startparen);
  583. }
  584. if ($startvalues > 0) {
  585. $truncated_sql = substr($truncated_sql, 0, $startvalues);
  586. }
  587. if ($startset > 0) {
  588. $truncated_sql = substr($truncated_sql, 0, $startset);
  589. }
  590. }
  591. foreach ($tables as $table => $value) {
  592. if (strpos($truncated_sql, $table) !== FALSE) {
  593. $event = $value;
  594. break;
  595. }
  596. else if (strpos($truncated_sql, "form_") !== FALSE) {
  597. $event = "patient-record";
  598. break;
  599. }
  600. }
  601. /* Avoid filling the audit log with trivial SELECT statements.
  602. * Skip SELECTs from unknown tables.
  603. * Skip SELECT count() statements.
  604. * Skip the SELECT made by the authCheckSession() function.
  605. */
  606. if ($querytype == "select") {
  607. if ($event == "other")
  608. return;
  609. if (stripos($statement, "SELECT count(" ) === 0)
  610. return;
  611. if (stripos($statement, "select username, password from users") === 0)
  612. return;
  613. }
  614. /* If the event is a patient-record, then note the patient id */
  615. $pid = 0;
  616. if ($event == "patient-record") {
  617. if (array_key_exists('pid', $_SESSION) && $_SESSION['pid'] != '') {
  618. $pid = $_SESSION['pid'];
  619. }
  620. }
  621. /* If query events are not enabled, don't log them */
  622. if (($querytype == "select") && !($GLOBALS['audit_events_query']))
  623. {
  624. if ((soundex($user) != soundex("emergency")) && (soundex($user) != soundex("breakglass")))
  625. return;
  626. }
  627. if (!($GLOBALS["audit_events_${event}"]))
  628. {
  629. if ((soundex($user) != soundex("emergency")) && (soundex($user) != soundex("breakglass")))
  630. return;
  631. }
  632. $event = $event . "-" . $querytype;
  633. $adodb = $GLOBALS['adodb']['db'];
  634. // ViSolve : Don't log sequences - to avoid the affect due to GenID calls
  635. if (strpos($comments, "sequences") !== FALSE) return;
  636. $sql = "insert into log (date, event, user, groupname, comments, patient_id, success, checksum,crt_user) " .
  637. "values ( NOW(), " .
  638. $adodb->qstr($event) . ", " .
  639. $adodb->qstr($user) . "," .
  640. $adodb->qstr($group) . "," .
  641. $adodb->qstr($comments) . "," .
  642. $adodb->qstr($pid) . "," .
  643. $adodb->qstr($success) . "," .
  644. $adodb->qstr($checksum) . "," .
  645. $adodb->qstr($_SERVER['SSL_CLIENT_S_DN_CN']) .")";
  646. sqlInsertClean_audit($sql);
  647. send_atna_audit_msg($user, $group, $event, $pid, $success, $comments);
  648. //return $ret;
  649. }
  650. /**
  651. * Record the patient disclosures.
  652. * @param $dates - The date when the disclosures are sent to the thrid party.
  653. * @param $event - The type of the disclosure.
  654. * @param $pid - The id of the patient for whom the disclosures are recorded.
  655. * @param $comment - The recipient name and description of the disclosure.
  656. * @uname - The username who is recording the disclosure.
  657. */
  658. function recordDisclosure($dates,$event,$pid,$recipient,$description,$user)
  659. {
  660. $adodb = $GLOBALS['adodb']['db'];
  661. $crt_user= $_SERVER['SSL_CLIENT_S_DN_CN'];
  662. $groupname=$_SESSION['authProvider'];
  663. $success=1;
  664. $sql = "insert into extended_log ( date, event, user, recipient, patient_id, description) " .
  665. "values (" . $adodb->qstr($dates) . "," . $adodb->qstr($event) . "," . $adodb->qstr($user) .
  666. "," . $adodb->qstr($recipient) . ",".
  667. $adodb->qstr($pid) ."," .
  668. $adodb->qstr($description) .")";
  669. $ret = sqlInsertClean_audit($sql);
  670. }
  671. /**
  672. * Edit the disclosures that is recorded.
  673. * @param $dates - The date when the disclosures are sent to the thrid party.
  674. * @param $event - The type of the disclosure.
  675. * param $comment - The recipient and the description of the disclosure are appended.
  676. * $logeventid - The id of the record which is to be edited.
  677. */
  678. function updateRecordedDisclosure($dates,$event,$recipient,$description,$disclosure_id)
  679. {
  680. $adodb = $GLOBALS['adodb']['db'];
  681. $sql="update extended_log set
  682. event=" . $adodb->qstr($event) . ",
  683. date=" . $adodb->qstr($dates) . ",
  684. recipient=" . $adodb->qstr($recipient) . ",
  685. description=" . $adodb->qstr($description) . "
  686. where id=" . $adodb->qstr($disclosure_id) . "";
  687. $ret = sqlInsertClean_audit($sql);
  688. }
  689. /**
  690. * Delete the disclosures that is recorded.
  691. * $deleteid - The id of the record which is to be deleted.
  692. */
  693. function deleteDisclosure($deletelid)
  694. {
  695. $sql="delete from extended_log where id='$deletelid'";
  696. $ret = sqlInsertClean_audit($sql);
  697. }
  698. ?>