policy.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. <?php
  2. /*
  3. * phpMeccano v0.2.0. Web-framework written with php programming language. Core module [policy.php].
  4. * Copyright (C) 2015-2019 Alexei Muzarov
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. *
  20. * e-mail: azexmail@gmail.com
  21. * e-mail: azexmail@mail.ru
  22. * https://bitbucket.org/azexmail/phpmeccano
  23. */
  24. namespace core;
  25. require_once MECCANO_CORE_DIR.'/extclass.php';
  26. interface intPolicy {
  27. function __construct(\mysqli $dbLink);
  28. public function delPolicy($plugin);
  29. public function setFuncAccess($plugin, $func, $groupId, $access = true); // old name [funcAccess]
  30. public function installPolicy(\DOMDocument $policy, $validate = true); // old name [install]
  31. public function groupPolicyList($plugin, $groupId, $code = MECCANO_DEF_LANG);
  32. public function getPolicyDescById($id);
  33. }
  34. class Policy extends ServiceMethods implements intPolicy {
  35. protected $dbLink; // database link
  36. public function __construct(\mysqli $dbLink) {
  37. $this->dbLink = $dbLink;
  38. }
  39. public function delPolicy($plugin) {
  40. $this->zeroizeError();
  41. if (!pregPlugin($plugin)) {
  42. $this->setError(ERROR_INCORRECT_DATA, 'delPolicy: incorrect plugin name');
  43. return false;
  44. }
  45. // checking if plugin exists
  46. $qPlugin = $this->dbLink->query("SELECT `id` "
  47. . "FROM `".MECCANO_TPREF."_core_plugins_installed` "
  48. . "WHERE `name`='$plugin' ;");
  49. if ($this->dbLink->errno) {
  50. $this->setError(ERROR_NOT_EXECUTED, 'delPolicy: '.$this->dbLink->error);
  51. return false;
  52. }
  53. if (!$this->dbLink->affected_rows) {
  54. $this->setError(ERROR_NOT_FOUND, 'delPolicy: unable to find plugin');
  55. return false;
  56. }
  57. $queries = [
  58. "DELETE `d` FROM `".MECCANO_TPREF."_core_policy_descriptions` `d` "
  59. . "JOIN `".MECCANO_TPREF."_core_policy_summary_list` `s` "
  60. . "ON `s`.`id`=`d`.`policyid` "
  61. . "WHERE `s`.`name`='$plugin' ;",
  62. "DELETE `a` FROM `".MECCANO_TPREF."_core_policy_access` `a` "
  63. . "JOIN `".MECCANO_TPREF."_core_policy_summary_list` `s` "
  64. . "ON `s`.`id`=`a`.`funcid` "
  65. . "WHERE `s`.`name`='$plugin' ;",
  66. "DELETE `n` FROM `".MECCANO_TPREF."_core_policy_nosession` `n` "
  67. . "JOIN `".MECCANO_TPREF."_core_policy_summary_list` `s` "
  68. . "ON `s`.`id`=`n`.`funcid` "
  69. . "WHERE `s`.`name`='$plugin' ;",
  70. "DELETE FROM `".MECCANO_TPREF."_core_policy_summary_list` "
  71. . "WHERE `name`='$plugin' ;"];
  72. foreach ($queries as $value) {
  73. $this->dbLink->query($value);
  74. if ($this->dbLink->errno) {
  75. $this->setError(ERROR_NOT_EXECUTED, 'delPolicy: something went wrong -> '.$this->dbLink->error);
  76. return false;
  77. }
  78. }
  79. return true;
  80. }
  81. public function setFuncAccess($plugin, $func, $groupId, $access = true) {
  82. $this->zeroizeError();
  83. if ($this->usePolicy && !$this->checkFuncAccess('core', 'policy_func_access')) {
  84. $this->setError(ERROR_RESTRICTED_ACCESS, "setFuncAccess: restricted by the policy");
  85. return false;
  86. }
  87. if (!is_integer($groupId) || !pregPlugin($plugin) || !pregPlugin($func)) {
  88. $this->setError(ERROR_NOT_EXECUTED, 'setFuncAccess: incorect type of incoming parameters');
  89. return false;
  90. }
  91. if (!$groupId) {
  92. if ($access) {
  93. $access = 1;
  94. }
  95. else {
  96. $access = 0;
  97. }
  98. $this->dbLink->query("UPDATE `".MECCANO_TPREF."_core_policy_nosession` `n` "
  99. . "JOIN `".MECCANO_TPREF."_core_policy_summary_list` `s` "
  100. . "ON `n`.`funcid`=`s`.`id` "
  101. . "SET `n`.`access`=$access "
  102. . "WHERE `s`.`func`='$func' "
  103. . "AND `s`.`name`='$plugin' ;");
  104. }
  105. elseif ($access) {
  106. $this->dbLink->query("UPDATE `".MECCANO_TPREF."_core_policy_access` `a` "
  107. . "JOIN `".MECCANO_TPREF."_core_policy_summary_list` `s` "
  108. . "ON `a`.`funcid`=`s`.`id` "
  109. . "SET `a`.`access`=1 "
  110. . "WHERE `s`.`func`='$func' "
  111. . "AND `s`.`name`='$plugin' "
  112. . "AND `a`.`groupid`=$groupId ;");
  113. }
  114. elseif (!$access && $groupId!=1) {
  115. $this->dbLink->query("UPDATE `".MECCANO_TPREF."_core_policy_access` `a` "
  116. . "JOIN `".MECCANO_TPREF."_core_policy_summary_list` `s` "
  117. . "ON `a`.`funcid`=`s`.`id` "
  118. . "SET `a`.`access`=0 "
  119. . "WHERE `s`.`func`='$func' "
  120. . "AND `s`.`name`='$plugin' "
  121. . "AND `a`.`groupid`=$groupId ;");
  122. }
  123. else {
  124. $this->setError(ERROR_SYSTEM_INTERVENTION, 'setFuncAccess: impossible to disable access for system group');
  125. return false;
  126. }
  127. if ($this->dbLink->errno) {
  128. $this->setError(ERROR_NOT_EXECUTED, 'setFuncAccess: unable to change access -> '.$this->dbLink->error);
  129. return false;
  130. }
  131. if (!$this->dbLink->affected_rows) {
  132. $this->setError(ERROR_NOT_FOUND, 'setFuncAccess: plugin name, function or group does not exist or access flag was not changed');
  133. return false;
  134. }
  135. return true;
  136. }
  137. public function installPolicy(\DOMDocument $policy, $validate = true) {
  138. $this->zeroizeError();
  139. if ($validate && !@$policy->relaxNGValidate(MECCANO_CORE_DIR.'/validation-schemas/policy-v01.rng')) {
  140. $this->setError(ERROR_INCORRECT_DATA, 'installPolicy: incorrect structure of incoming data');
  141. return false;
  142. }
  143. $pluginName = $policy->getElementsByTagName('policy')->item(0)->getAttribute('plugin');
  144. // check whether plugin is installed
  145. $qPlugin = $this->dbLink->query("SELECT `id` "
  146. . "FROM `".MECCANO_TPREF."_core_plugins_installed` "
  147. . "WHERE `name`='$pluginName' ;");
  148. if ($this->dbLink->errno) {
  149. $this->setError(ERROR_NOT_EXECUTED, "installPolicy: unable to check whether the plugin [$pluginName] is installed -> ".$this->dbLink->errno);
  150. return false;
  151. }
  152. if (!$this->dbLink->affected_rows) {
  153. $this->setError(ERROR_NOT_FOUND, "installPolicy: plugin [$pluginName] is not installed");
  154. return false;
  155. }
  156. // get list of available languages
  157. $qAvaiLang = $this->dbLink->query("SELECT `code`, `id` "
  158. . "FROM `".MECCANO_TPREF."_core_langman_languages` ;");
  159. if ($this->dbLink->errno) {
  160. $this->setError(ERROR_NOT_EXECUTED, 'installPolicy: unable to get list of available languages: '.$this->dbLink->error);
  161. return false;
  162. }
  163. // avaiable languages
  164. $avLangIds = [];
  165. $avLangCodes = [];
  166. while ($row = $qAvaiLang->fetch_row()) {
  167. $avLangIds[$row[0]] = $row[1];
  168. $avLangCodes[] = $row[0];
  169. }
  170. $incomingPolicy = [];
  171. $defaultRules = [];
  172. $funcNodes = $policy->getElementsByTagName('function');
  173. foreach ($funcNodes as $funcNode) {
  174. $funcName = $funcNode->getAttribute('name');
  175. $nonAuthRule = $funcNode->getAttribute('nonauth');
  176. $authRule = $funcNode->getAttribute('auth');
  177. $defaultRules[$funcName] = [(int) $nonAuthRule, (int) $authRule];
  178. $incomingPolicy[$funcName] = [];
  179. $langNodes = $funcNode->getElementsByTagName('description');
  180. foreach ($langNodes as $langNode){
  181. $code = $langNode->getAttribute('code');
  182. if (isset($avLangIds[$code])) {
  183. $incomingPolicy[$funcName][$code]['short'] = $langNode->getElementsByTagName('short')->item(0)->nodeValue;
  184. $incomingPolicy[$funcName][$code]['detailed'] = $langNode->getElementsByTagName('detailed')->item(0)->nodeValue;
  185. }
  186. }
  187. }
  188. // get installed policies of the plugin
  189. $qPolicy = $this->dbLink->query("SELECT `func`, `id` "
  190. . "FROM `".MECCANO_TPREF."_core_policy_summary_list` "
  191. . "WHERE `name`='$pluginName' ;");
  192. if ($this->dbLink->errno) {
  193. $this->setError(ERROR_NOT_EXECUTED, 'installEvents: unable to get installed events -> '.$this->dbLink->error);
  194. return false;
  195. }
  196. $installedPolicy = [];
  197. while ($row = $qPolicy->fetch_row()) {
  198. $installedPolicy[$row[0]] = $row[1];
  199. }
  200. // delete outdated policies
  201. $outdatedPolicy = array_diff(array_keys($installedPolicy), array_keys($incomingPolicy));
  202. foreach ($outdatedPolicy as $func) {
  203. $funcId = $installedPolicy[$func];
  204. $sql = [
  205. "DELETE FROM `".MECCANO_TPREF."_core_policy_descriptions` "
  206. . "WHERE `policyid`=$funcId ;",
  207. "DELETE FROM `".MECCANO_TPREF."_core_policy_access` "
  208. . "WHERE `funcid`=$funcId ;",
  209. "DELETE FROM `".MECCANO_TPREF."_core_policy_nosession` "
  210. . "WHERE `funcid`=$funcId ;",
  211. "DELETE FROM `".MECCANO_TPREF."_core_policy_summary_list` "
  212. . "WHERE `id`=$funcId ;"
  213. ];
  214. foreach ($sql as $dQuery) {
  215. $this->dbLink->query($dQuery);
  216. if ($this->dbLink->errno) {
  217. $this->setError(ERROR_NOT_EXECUTED, "installPolicy: unable to delete outdated policy -> ".$this->dbLink->error);
  218. return false;
  219. }
  220. }
  221. }
  222. // getting of group identifiers
  223. $qGroupIds = $this->dbLink->query("SELECT `id` "
  224. . "FROM `".MECCANO_TPREF."_core_userman_groups` ;");
  225. if ($this->dbLink->errno) {
  226. $this->setError(ERROR_NOT_EXECUTED, 'installPolicy: unable to get group identifiers -> '.$this->dbLink->error);
  227. return false;
  228. }
  229. $groupIds = [];
  230. while ($row = $qGroupIds->fetch_row()) {
  231. $groupIds[] = $row[0];
  232. }
  233. // install/update policies
  234. foreach ($incomingPolicy as $funcName => $descriptions) {
  235. $missingCodes = array_diff($avLangCodes, array_keys($descriptions));
  236. if ($missingCodes) {
  237. foreach ($missingCodes as $code) {
  238. $descriptions[$code]['short'] = "$funcName";
  239. $descriptions[$code]['detailed'] = "$funcName";
  240. }
  241. }
  242. // update policy
  243. if (isset($installedPolicy[$funcName])) {
  244. $funcId = $installedPolicy[$funcName];
  245. foreach ($descriptions as $inCode => $desc) {
  246. $codeId = $avLangIds[$inCode];
  247. $updateShort = $this->dbLink->real_escape_string($desc['short']);
  248. $updateDetailed = $this->dbLink->real_escape_string($desc['detailed']);
  249. // update policy description
  250. $this->dbLink->query("UPDATE `".MECCANO_TPREF."_core_policy_descriptions` "
  251. . "SET `short`='$updateShort', `detailed`='$updateDetailed' "
  252. . "WHERE `policyid`=$funcId "
  253. . "AND `codeid`=$codeId ;");
  254. if ($this->dbLink->errno) {
  255. $this->setError(ERROR_NOT_EXECUTED, 'installPolicy: unable to update policy description -> '.$this->dbLink->error);
  256. return false;
  257. }
  258. }
  259. }
  260. // install policy
  261. else {
  262. // create record in the summary list
  263. $this->dbLink->query("INSERT INTO `".MECCANO_TPREF."_core_policy_summary_list` (`name`, `func`) "
  264. . "VALUES ('$pluginName', '$funcName') ;");
  265. if ($this->dbLink->errno) {
  266. $this->setError(ERROR_NOT_EXECUTED, 'installPolicy: unable to add policy into the summary list -> '.$this->dbLink->error);
  267. return false;
  268. }
  269. $insertId = $this->dbLink->insert_id;
  270. // get default rules
  271. list($nonAuthRule, $authRule) = $defaultRules[$funcName];
  272. // policy for the inactive session (non-authorized user)
  273. $this->dbLink->query("INSERT INTO `".MECCANO_TPREF."_core_policy_nosession` (`funcid`, `access`) "
  274. . "VALUES ($insertId, $nonAuthRule) ;");
  275. if ($this->dbLink->errno) {
  276. $this->setError(ERROR_NOT_EXECUTED, 'installPolicy: unable to create policy for the inactive session -> '.$this->dbLink->error);
  277. return false;
  278. }
  279. // policy for the groups
  280. foreach ($groupIds as $groupId) {
  281. if ($groupId == 1) {
  282. $access = 1;
  283. }
  284. else {
  285. $access = $authRule;
  286. }
  287. $this->dbLink->query("INSERT INTO `".MECCANO_TPREF."_core_policy_access` (`groupid`, `funcid`, `access`) "
  288. . "VALUES ($groupId, $insertId, $access) ;");
  289. if ($this->dbLink->errno) {
  290. $this->setError(ERROR_NOT_EXECUTED, 'installPolicy: unable to install group policy -> '.$this->dbLink->error);
  291. return false;
  292. }
  293. }
  294. // create policy description
  295. foreach ($descriptions as $inCode => $desc) {
  296. $codeId = $avLangIds[$inCode];
  297. $insertShort = $this->dbLink->real_escape_string($desc['short']);
  298. $insertDetailed = $this->dbLink->real_escape_string($desc['detailed']);
  299. $this->dbLink->query("INSERT INTO `".MECCANO_TPREF."_core_policy_descriptions` "
  300. . "(`codeid`, `policyid`, `short`, `detailed`) "
  301. . "VALUES ($codeId, $insertId, '$insertShort', '$insertDetailed') ;");
  302. if ($this->dbLink->errno) {
  303. $this->setError(ERROR_NOT_EXECUTED, 'installPolicy: unable to install policy description -> '.$this->dbLink->error);
  304. return false;
  305. }
  306. }
  307. }
  308. }
  309. return true;
  310. }
  311. public function groupPolicyList($plugin, $groupId, $code = MECCANO_DEF_LANG) {
  312. $this->zeroizeError();
  313. if ($this->usePolicy && !$this->checkFuncAccess('core', 'policy_list_about')) {
  314. $this->setError(ERROR_RESTRICTED_ACCESS, "groupPolicyList: restricted by the policy");
  315. return false;
  316. }
  317. if (!pregPlugin($plugin) || !(is_integer($groupId) || is_bool($groupId)) || !pregLang($code)) {
  318. $this->setError(ERROR_INCORRECT_DATA, 'groupPolicyList: incorect incoming parameters');
  319. return false;
  320. }
  321. if (!$groupId) {
  322. $qList = $this->dbLink->query("SELECT `d`.`id`, `d`.`short`, `s`.`func`, `n`.`access` "
  323. . "FROM `".MECCANO_TPREF."_core_policy_summary_list` `s` "
  324. . "JOIN `".MECCANO_TPREF."_core_policy_nosession` `n` "
  325. . "ON `s`.`id`=`n`.`funcid` "
  326. . "JOIN `".MECCANO_TPREF."_core_policy_descriptions` `d` "
  327. . "ON `d`.`policyid`=`s`.`id` "
  328. . "JOIN `".MECCANO_TPREF."_core_langman_languages` `l` "
  329. . "ON `d`.`codeid`=`l`.`id` "
  330. . "WHERE `s`.`name`='$plugin' "
  331. . "AND `l`.`code`='$code' ;");
  332. }
  333. else {
  334. $qList = $this->dbLink->query("SELECT `d`.`id`, `d`.`short`, `s`.`func`, `a`.`access` "
  335. . "FROM `".MECCANO_TPREF."_core_policy_summary_list` `s` "
  336. . "JOIN `".MECCANO_TPREF."_core_policy_access` `a` "
  337. . "ON `s`.`id`=`a`.`funcid` "
  338. . "JOIN `".MECCANO_TPREF."_core_policy_descriptions` `d` "
  339. . "ON `d`.`policyid`=`s`.`id` "
  340. . "JOIN `".MECCANO_TPREF."_core_langman_languages` `l` "
  341. . "ON `d`.`codeid`=`l`.`id` "
  342. . "WHERE `s`.`name`='$plugin' "
  343. . "AND `a`.`groupid`=$groupId "
  344. . "AND `l`.`code`='$code' ;");
  345. }
  346. if ($this->dbLink->errno) {
  347. $this->setError(ERROR_NOT_EXECUTED, 'groupPolicyList: something went wrong -> '.$this->dbLink->error);
  348. return false;
  349. }
  350. if (!$this->dbLink->affected_rows) {
  351. $this->setError(ERROR_NOT_FOUND, 'groupPolicyList: not found');
  352. return false;
  353. }
  354. if ($this->outputType == 'xml') {
  355. $xml = new \DOMDocument('1.0', 'utf-8');
  356. $policyNode = $xml->createElement('policy');
  357. $xml->appendChild($policyNode);
  358. $attr_plugin = $xml->createAttribute('plugin');
  359. $attr_plugin->value = $plugin;
  360. $policyNode->appendChild($attr_plugin);
  361. $attr_group = $xml->createAttribute('group');
  362. $attr_group->value = $groupId;
  363. $policyNode->appendChild($attr_group);
  364. while ($row = $qList->fetch_row()) {
  365. $funcNode = $xml->createElement('function');
  366. $policyNode->appendChild($funcNode);
  367. $funcNode->appendChild($xml->createElement('id', $row[0]));
  368. $funcNode->appendChild($xml->createElement('short', $row[1]));
  369. $funcNode->appendChild($xml->createElement('name', $row[2]));
  370. $funcNode->appendChild($xml->createElement('access', $row[3]));
  371. }
  372. return $xml;
  373. }
  374. else {
  375. $policyNode = [];
  376. $policyNode['plugin'] = $plugin;
  377. $policyNode['group'] = $groupId;
  378. $policyNode['functions'] = [];
  379. while ($row = $qList->fetch_row()) {
  380. $policyNode['functions'][] = [
  381. 'id' => (int) $row[0],
  382. 'short' => $row[1],
  383. 'name' => $row[2],
  384. 'access' => (int) $row[3]
  385. ];
  386. }
  387. if ($this->outputType == 'array') {
  388. return $policyNode;
  389. }
  390. else {
  391. return json_encode($policyNode);
  392. }
  393. }
  394. }
  395. public function getPolicyDescById($id) {
  396. $this->zeroizeError();
  397. if ($this->usePolicy && !$this->checkFuncAccess('core', 'policy_list_about')) {
  398. $this->setError(ERROR_RESTRICTED_ACCESS, "getPolicyDescById: restricted by the policy");
  399. return false;
  400. }
  401. if (!is_integer($id)) {
  402. $this->setError(ERROR_INCORRECT_DATA, 'getPolicyDescById: identifier must be integer');
  403. return false;
  404. }
  405. $qDesc = $this->dbLink->query("SELECT `short`, `detailed` "
  406. . "FROM `".MECCANO_TPREF."_core_policy_descriptions` "
  407. . "WHERE `id`=$id ;");
  408. if ($this->dbLink->errno) {
  409. $this->setError(ERROR_NOT_EXECUTED, 'getPolicyDescById: unable to get description -> '.$this->dbLink->error);
  410. return false;
  411. }
  412. if (!$this->dbLink->affected_rows) {
  413. $this->setError(ERROR_NOT_FOUND, 'getPolicyDescById: description was not found');
  414. return false;
  415. }
  416. list($short, $detailed) = $qDesc->fetch_row();
  417. if ($this->outputType == 'xml') {
  418. $xml = new \DOMDocument('1.0', 'utf-8');
  419. $polycyNode = $xml->createElement('policy');
  420. $xml->appendChild($polycyNode);
  421. $idNode = $xml->createElement('id', $id);
  422. $shortNode = $xml->createElement('short', $short);
  423. $detailedNode = $xml->createElement('detailed', $detailed);
  424. $polycyNode->appendChild($idNode);
  425. $polycyNode->appendChild($shortNode);
  426. $polycyNode->appendChild($detailedNode);
  427. return $xml;
  428. }
  429. else {
  430. if ($this->outputType == 'json') {
  431. return json_encode(['id' => $id, 'short' => $short, 'detailed' => $detailed]);
  432. }
  433. else {
  434. return ['id' => $id, 'short' => $short, 'detailed' => $detailed];
  435. }
  436. }
  437. }
  438. }