EPESI BIM • Business Information Manager • Cloud CRM/ERP http://epe.si/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

281 lines
7.9 KiB

  1. <?php
  2. defined("_VALID_ACCESS") || die('Direct access forbidden');
  3. class Utils_RecordBrowser_Access
  4. {
  5. protected $tab;
  6. protected $action;
  7. protected $record;
  8. protected $ruleCrits;
  9. protected $activeGrantRules;
  10. protected static $ruleCritsCache = [];
  11. protected static $ruleBlockedFieldsCache = [];
  12. public static function create($tab, $action, $record = null)
  13. {
  14. return new self($tab, $action, $record);
  15. }
  16. public function __construct($tab, $action, $record = null)
  17. {
  18. $this->setTab($tab);
  19. $this->setAction($action);
  20. $this->setRecord($record);
  21. }
  22. protected function setTab($tab)
  23. {
  24. $this->tab = $tab;
  25. return $this;
  26. }
  27. public function getTab()
  28. {
  29. return $this->tab;
  30. }
  31. protected function setAction($action)
  32. {
  33. $this->action = $action;
  34. return $this;
  35. }
  36. protected function setRecord($record)
  37. {
  38. $this->record = is_numeric($record)? Utils_RecordBrowserCommon::get_record($this->getTab(), $record): $record;
  39. return $this;
  40. }
  41. public function getUserAccess($adminMode = false)
  42. {
  43. // access inactive records only in admin mode
  44. if (! $this->getRecordInactiveAccess() && ! ($adminMode && Acl::i_am_admin())) return false;
  45. if ($this->isFullDeny()) return false;
  46. if ($this->action === 'browse') return $this->getCritsRaw() !== null && $this->getCritsRaw() !== false ? true: false;
  47. if ($this->getActiveGrantRules() === false) return false;
  48. if ($this->action === 'delete') return true;
  49. return $this->getAccessFields();
  50. }
  51. public function getCrits()
  52. {
  53. if(!$this->getRecordInactiveAccess()) return false;
  54. return $this->getCritsRaw();
  55. }
  56. protected function getCritsRaw()
  57. {
  58. if ($this->isFullDeny()) return false;
  59. if ($this->isFullGrant()) return true;
  60. $ret = null;
  61. $ruleCrits = $this->getRuleCrits();
  62. foreach ( $ruleCrits as $ruleId => $c ) {
  63. if ($ruleId === 'restrict') continue;
  64. if (! $c instanceof Utils_RecordBrowser_CritsInterface) continue;
  65. // if crit is empty, then we have access to all records
  66. if ($c->is_empty()) $ret = $c;
  67. if ($ret instanceof Utils_RecordBrowser_Crits && $ret->is_empty()) continue;
  68. $ret = Utils_RecordBrowserCommon::merge_crits($ret, $c, true);
  69. }
  70. // if there is any access granted - limit it based on restrict crits
  71. if ($ret !== null && $ruleCrits['restrict'] instanceof Utils_RecordBrowser_Crits) $ret = Utils_RecordBrowserCommon::merge_crits($ret, $ruleCrits['restrict']);
  72. return $ret?: false;
  73. }
  74. protected function getRecordInactiveAccess()
  75. {
  76. if(!Utils_RecordBrowserCommon::is_record_active($this->record) && ($this->action=='edit' || $this->action=='delete'))
  77. return false;
  78. return true;
  79. }
  80. public function isFullGrant()
  81. {
  82. $ruleCrits = $this->getRuleCrits();
  83. return ($ruleCrits['restrict']!==true && $ruleCrits['grant']===true);
  84. }
  85. public function isFullDeny()
  86. {
  87. $ruleCrits = $this->getRuleCrits();
  88. return $ruleCrits['restrict']===true;
  89. }
  90. public function getRuleCrits()
  91. {
  92. if (isset($this->ruleCrits)) return $this->ruleCrits;
  93. $cache_key = "{$this->tab}__USER_" . Acl::get_user();
  94. $action = ($this->action == 'browse')? 'view': $this->action;
  95. if (!isset(self::$ruleCritsCache[$cache_key])) {
  96. Utils_RecordBrowserCommon::check_table_name($this->tab);
  97. $user_clearance = Acl::get_clearance();
  98. $r = DB::Execute('SELECT * FROM '.$this->tab.'_access AS acs WHERE NOT EXISTS (SELECT * FROM '.$this->tab.'_access_clearance WHERE rule_id=acs.id AND '.implode(' AND ',array_fill(0, count($user_clearance), 'clearance!=%s')).')', array_values($user_clearance));
  99. $ruleCrits = [
  100. 'view' => [],
  101. 'edit' => [],
  102. 'delete' => [],
  103. 'add' => [],
  104. 'print' => [],
  105. 'export' => [],
  106. 'selection' => []
  107. ];
  108. while ($row = $r->FetchRow())
  109. $ruleCrits[$row['action']][$row['id']] = $this->parseAccessCrits($row['crits']);
  110. $ruleCrits['selection'] = $ruleCrits['selection']?: $ruleCrits['view'];
  111. self::$ruleCritsCache[$cache_key] = $ruleCrits;
  112. }
  113. $ruleCrits = self::$ruleCritsCache[$cache_key];
  114. return $this->ruleCrits = $ruleCrits[$action] + $this->callCustomAccessCallbacks();
  115. }
  116. public function parseAccessCrits($str, $human_readable = false)
  117. {
  118. $ret = Utils_RecordBrowserCommon::unserialize_crits($str);
  119. if (!is_object($ret)) {
  120. $ret = Utils_RecordBrowser_Crits::from_array($ret);
  121. }
  122. return $ret->replace_special_values($human_readable);
  123. }
  124. protected function callCustomAccessCallbacks()
  125. {
  126. $ret = [
  127. 'grant' => null,
  128. 'restrict' => null
  129. ];
  130. foreach ( Utils_RecordBrowserCommon::get_custom_access_callbacks($this->tab) as $callback ) {
  131. if (!is_callable($callback)) continue;
  132. $callbackCrits = call_user_func($callback, $this->action, $this->record, $this->tab);
  133. if (is_bool($callbackCrits)) {
  134. $ret[$callbackCrits ? 'grant': 'restrict'] = true;
  135. break;
  136. }
  137. if ($callbackCrits === null) continue;
  138. // if callback return is crits or crits array use it by default in restrict mode for backward compatibility
  139. $crits = [
  140. 'grant' => null,
  141. 'restrict' => $callbackCrits
  142. ];
  143. if (is_array($callbackCrits) && (isset($callbackCrits['grant']) || isset($callbackCrits['restrict']))) {
  144. // if restrict rules are not set make sure the restrict crits are clean
  145. if (! isset($callbackCrits['restrict'])) $callbackCrits['restrict'] = null;
  146. $crits = array_merge($crits, $callbackCrits);
  147. }
  148. if (! $crits['grant']) $crits['grant'] = null;
  149. foreach ( $crits as $mode => $c ) {
  150. $c = is_array($c) ? Utils_RecordBrowser_Crits::from_array($c): $c;
  151. if ($c instanceof Utils_RecordBrowser_Crits) $ret[$mode] = ($ret[$mode] !== null) ? Utils_RecordBrowserCommon::merge_crits($ret[$mode], $c, $mode === 'grant'): $c;
  152. elseif (is_bool($c)) $ret[$mode] = $c;
  153. }
  154. }
  155. return $ret;
  156. }
  157. protected function getActiveGrantRules()
  158. {
  159. if (isset($this->activeGrantRules)) return $this->activeGrantRules;
  160. if ($this->isFullDeny()) return false;
  161. if ($this->isFullGrant()) return ['grant'];
  162. $ruleCrits = $this->getRuleCrits();
  163. if ($this->record != null && $this->action !== 'add' && $ruleCrits['restrict'] instanceof Utils_RecordBrowser_CritsInterface && ! Utils_RecordBrowserCommon::check_record_against_crits($this->tab, $this->record, $ruleCrits['restrict'])) {
  164. return false;
  165. }
  166. $ret = [];
  167. foreach ( $ruleCrits as $rule_id => $c ) {
  168. if ($rule_id === 'restrict') continue;
  169. if (! $c instanceof Utils_RecordBrowser_CritsInterface) continue;
  170. if ($this->record != null && ! Utils_RecordBrowserCommon::check_record_against_crits($this->tab, $this->record, $c)) continue;
  171. $ret[] = $rule_id;
  172. }
  173. return $this->activeGrantRules = $ret ?: false;
  174. }
  175. protected function getAccessFields()
  176. {
  177. $grant_rule_ids = $this->getActiveGrantRules();
  178. $access_rule_blocked_fields = [];
  179. foreach ( $grant_rule_ids as $rule_id )
  180. $access_rule_blocked_fields[$rule_id] = $this->getRuleBlockedFields($rule_id);
  181. $fields = Utils_RecordBrowserCommon::init($this->tab);
  182. $blocked_fields = count($access_rule_blocked_fields) > 1 ? call_user_func_array('array_intersect', $access_rule_blocked_fields): reset($access_rule_blocked_fields);
  183. $full_field_access = array_fill_keys(array_column($fields, 'id'), true);
  184. $blocked_field_access = array();
  185. if ($blocked_fields) $blocked_field_access = array_fill_keys($blocked_fields, false);
  186. return array_merge($full_field_access, $blocked_field_access);
  187. }
  188. protected function getRuleBlockedFields($ruleId)
  189. {
  190. if (!is_numeric($ruleId)) return [];
  191. if (!isset(self::$ruleBlockedFieldsCache[$this->tab])) {
  192. $r = DB::Execute('SELECT * FROM '.$this->tab.'_access_fields');
  193. $fields = array();
  194. while ($row = $r->FetchRow()) {
  195. $fields[$row['rule_id']][] = $row['block_field'];
  196. }
  197. self::$ruleBlockedFieldsCache[$this->tab] = $fields;
  198. }
  199. return isset(self::$ruleBlockedFieldsCache[$this->tab][$ruleId])? self::$ruleBlockedFieldsCache[$this->tab][$ruleId]: [];
  200. }
  201. }