Validation.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. <?php
  2. namespace Typemill\Models;
  3. use Typemill\Models\User;
  4. use Valitron\Validator;
  5. class Validation
  6. {
  7. /**
  8. * Constructor with custom validation rules
  9. *
  10. * @param obj $db the database connection.
  11. */
  12. public function __construct()
  13. {
  14. $user = new User();
  15. Validator::langDir(__DIR__.'/../vendor/vlucas/valitron/lang'); // always set langDir before lang.
  16. Validator::lang('en');
  17. Validator::addRule('values_allowed', function($field, $value, array $params, array $fields) use ($user)
  18. {
  19. $badvalues = array_diff($value, $params[0]);
  20. if(empty($badvalues)){ return true; }
  21. return false;
  22. }, 'invalid values');
  23. Validator::addRule('image_types', function($field, $value, array $params, array $fields) use ($user)
  24. {
  25. $allowed = ['jpg', 'jpeg', 'png', 'webp'];
  26. $pathinfo = pathinfo($value);
  27. $extension = strtolower($pathinfo['extension']);
  28. if(in_array($extension, $allowed)){ return true; }
  29. return false;
  30. }, 'only jpg, jpeg, png, webp, allowed');
  31. Validator::addRule('userAvailable', function($field, $value, array $params, array $fields) use ($user)
  32. {
  33. $userdata = $user->getUser($value);
  34. if($userdata){ return false; }
  35. return true;
  36. }, 'taken');
  37. Validator::addRule('userExists', function($field, $value, array $params, array $fields) use ($user)
  38. {
  39. $userdata = $user->getUser($value);
  40. if($userdata){ return true; }
  41. return false;
  42. }, 'does not exist');
  43. Validator::addRule('iplist', function($field, $value, array $params, array $fields) use ($user)
  44. {
  45. $iplist = explode(",", $value);
  46. foreach($iplist as $ip)
  47. {
  48. if( filter_var( trim($ip), \FILTER_VALIDATE_IP) === false)
  49. {
  50. return false;
  51. }
  52. }
  53. return true;
  54. }, 'contains one or more invalid ip-adress.');
  55. Validator::addRule('customfields', function($field, $customfields, array $params, array $fields) use ($user)
  56. {
  57. if(empty($customfields))
  58. {
  59. return true;
  60. }
  61. foreach($customfields as $key => $value)
  62. {
  63. if(!isset($key) OR empty($key) OR (preg_match('/^([a-z0-9])+$/i', $key) == false) )
  64. {
  65. return false;
  66. }
  67. if (!isset($value) OR empty($value) OR ( $value != strip_tags($value) ) )
  68. {
  69. return false;
  70. }
  71. }
  72. return true;
  73. }, 'some fields are empty or contain invalid values.');
  74. Validator::addRule('checkPassword', function($field, $value, array $params, array $fields) use ($user)
  75. {
  76. $userdata = $user->getUser($fields['username']);
  77. if($userdata && password_verify($value, $userdata['password'])){ return true; }
  78. return false;
  79. }, 'wrong password');
  80. Validator::addRule('navigation', function($field, $value, array $params, array $fields)
  81. {
  82. $format = '/[@#^*()=\[\]{};:"\\|,.<>\/]/';
  83. if ( preg_match($format, $value))
  84. {
  85. return false;
  86. }
  87. return true;
  88. }, 'contains special characters');
  89. Validator::addRule('noSpecialChars', function($field, $value, array $params, array $fields)
  90. {
  91. $format = '/[!@#$%^&*()_+=\[\]{};\':"\\|,.<>\/?]/';
  92. if ( preg_match($format, $value))
  93. {
  94. return false;
  95. }
  96. return true;
  97. }, 'contains special characters');
  98. Validator::addRule('noHTML', function($field, $value, array $params, array $fields)
  99. {
  100. if ( $value == strip_tags($value) )
  101. {
  102. return true;
  103. }
  104. return false;
  105. }, 'contains html');
  106. Validator::addRule('markdownSecure', function($field, $value, array $params, array $fields)
  107. {
  108. /* strip out code blocks and blockquotes */
  109. $value = preg_replace('/`{4,}[\s\S]+?`{4,}/', '', $value);
  110. $value = preg_replace('/`{3,}[\s\S]+?`{3,}/', '', $value);
  111. $value = preg_replace('/`{2,}[\s\S]+?`{2,}/', '', $value);
  112. $value = preg_replace('/`{1,}[\s\S]+?`{1,}/', '', $value);
  113. $value = preg_replace('/>[\s\S]+?[\n\r]/', '', $value);
  114. if ( $value == strip_tags($value) )
  115. {
  116. return true;
  117. }
  118. return false;
  119. }, 'not secure. For code please use markdown `inline-code` or ````fenced code blocks````.');
  120. }
  121. # return valitron standard object
  122. public function returnValidator(array $params)
  123. {
  124. return new Validator($params);
  125. }
  126. /**
  127. * validation for signup form
  128. *
  129. * @param array $params with form data.
  130. * @return obj $v the validation object passed to a result method.
  131. */
  132. public function signin(array $params)
  133. {
  134. $v = new Validator($params);
  135. $v->rule('required', ['username', 'password'])->message("Required");
  136. $v->rule('alphaNum', 'username')->message("Invalid characters");
  137. $v->rule('lengthBetween', 'password', 5, 20)->message("Length between 5 - 20");
  138. $v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
  139. if($v->validate())
  140. {
  141. return true;
  142. }
  143. else
  144. {
  145. return false;
  146. }
  147. }
  148. /**
  149. * validation for signup form
  150. *
  151. * @param array $params with form data.
  152. * @return obj $v the validation object passed to a result method.
  153. */
  154. public function newUser(array $params, $userroles)
  155. {
  156. $v = new Validator($params);
  157. $v->rule('required', ['username', 'email', 'password'])->message("required");
  158. $v->rule('alphaNum', 'username')->message("invalid characters");
  159. $v->rule('lengthBetween', 'password', 5, 20)->message("Length between 5 - 20");
  160. $v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
  161. $v->rule('userAvailable', 'username')->message("User already exists");
  162. $v->rule('noHTML', 'firstname')->message(" contains HTML");
  163. $v->rule('lengthBetween', 'firstname', 2, 40);
  164. $v->rule('noHTML', 'lastname')->message(" contains HTML");
  165. $v->rule('lengthBetween', 'lastname', 2, 40);
  166. $v->rule('email', 'email')->message("e-mail is invalid");
  167. $v->rule('in', 'userrole', $userroles);
  168. return $this->validationResult($v);
  169. }
  170. public function existingUser(array $params, $userroles)
  171. {
  172. $v = new Validator($params);
  173. $v->rule('required', ['username', 'email', 'userrole'])->message("required");
  174. $v->rule('alphaNum', 'username')->message("invalid");
  175. $v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
  176. $v->rule('userExists', 'username')->message("user does not exist");
  177. $v->rule('noHTML', 'firstname')->message(" contains HTML");
  178. $v->rule('lengthBetween', 'firstname', 2, 40);
  179. $v->rule('noHTML', 'lastname')->message(" contains HTML");
  180. $v->rule('lengthBetween', 'lastname', 2, 40);
  181. $v->rule('email', 'email')->message("e-mail is invalid");
  182. $v->rule('in', 'userrole', $userroles);
  183. return $this->validationResult($v);
  184. }
  185. public function username($username)
  186. {
  187. $v = new Validator($username);
  188. $v->rule('alphaNum', 'username')->message("Only alpha-numeric characters allowed");
  189. $v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20");
  190. return $this->validationResult($v);
  191. }
  192. /**
  193. * validation for changing the password
  194. *
  195. * @param array $params with form data.
  196. * @return obj $v the validation object passed to a result method.
  197. */
  198. public function newPassword(array $params)
  199. {
  200. $v = new Validator($params);
  201. $v->rule('required', ['password', 'newpassword']);
  202. $v->rule('lengthBetween', 'newpassword', 5, 20);
  203. $v->rule('checkPassword', 'password')->message("Password is wrong");
  204. return $this->validationResult($v);
  205. }
  206. /**
  207. * validation for system settings
  208. *
  209. * @param array $params with form data.
  210. * @return obj $v the validation object passed to a result method.
  211. */
  212. public function settings(array $params, array $copyright, array $formats, $name = false)
  213. {
  214. $v = new Validator($params);
  215. $v->rule('required', ['title', 'author', 'copyright', 'year', 'editor']);
  216. $v->rule('lengthBetween', 'title', 2, 20);
  217. $v->rule('lengthBetween', 'author', 2, 40);
  218. $v->rule('regex', 'title', '/^[\pL0-9_ \-]*$/u');
  219. $v->rule('regex', 'author', '/^[\pL_ \-]*$/u');
  220. $v->rule('integer', 'year');
  221. $v->rule('length', 'year', 4);
  222. $v->rule('length', 'langattr', 2);
  223. $v->rule('in', 'editor', ['raw', 'visual']);
  224. $v->rule('values_allowed', 'formats', $formats);
  225. $v->rule('in', 'copyright', $copyright);
  226. $v->rule('iplist', 'trustedproxies');
  227. return $this->validationResult($v, $name);
  228. }
  229. /**
  230. * validation for content editor
  231. *
  232. * @param array $params with form data.
  233. * @return true or $v->errors with array of errors to use in json-response
  234. */
  235. public function editorInput(array $params)
  236. {
  237. $v = new Validator($params);
  238. $v->rule('required', ['title', 'content', 'url']);
  239. $v->rule('lengthBetween', 'title', 2, 100);
  240. $v->rule('noHTML', 'title');
  241. $v->rule('markdownSecure', 'content');
  242. if($v->validate())
  243. {
  244. return true;
  245. }
  246. else
  247. {
  248. return $v->errors();
  249. }
  250. }
  251. public function blockInput(array $params)
  252. {
  253. $v = new Validator($params);
  254. $v->rule('required', ['markdown', 'block_id', 'url']);
  255. $v->rule('markdownSecure', 'markdown');
  256. $v->rule('regex', 'block_id', '/^[0-9.]+$/i');
  257. if($v->validate())
  258. {
  259. return true;
  260. }
  261. else
  262. {
  263. return $v->errors();
  264. }
  265. }
  266. /**
  267. * validation for resort navigation
  268. *
  269. * @param array $params with form data.
  270. * @return true or $v->errors with array of errors to use in json-response
  271. */
  272. public function navigationSort(array $params)
  273. {
  274. $v = new Validator($params);
  275. $v->rule('required', ['item_id', 'parent_id_from', 'parent_id_to']);
  276. $v->rule('regex', 'item_id', '/^[0-9.]+$/i');
  277. $v->rule('regex', 'parent_id_from', '/^[a-zA-Z0-9.]+$/i');
  278. $v->rule('regex', 'parent_id_to', '/^[a-zA-Z0-9.]+$/i');
  279. $v->rule('integer', 'index_new');
  280. if($v->validate())
  281. {
  282. return true;
  283. }
  284. else
  285. {
  286. return $v->errors();
  287. }
  288. }
  289. /**
  290. * validation for new navigation items
  291. *
  292. * @param array $params with form data.
  293. * @return true or $v->errors with array of errors to use in json-response
  294. */
  295. public function navigationItem(array $params)
  296. {
  297. $v = new Validator($params);
  298. $v->rule('required', ['folder_id', 'item_name', 'type', 'url']);
  299. $v->rule('regex', 'folder_id', '/^[0-9.]+$/i');
  300. # $v->rule('noSpecialChars', 'item_name');
  301. $v->rule('navigation', 'item_name');
  302. $v->rule('lengthBetween', 'item_name', 1, 60);
  303. $v->rule('in', 'type', ['file', 'folder']);
  304. if($v->validate())
  305. {
  306. return true;
  307. }
  308. else
  309. {
  310. return $v->errors();
  311. }
  312. }
  313. public function navigationBaseItem(array $params)
  314. {
  315. $v = new Validator($params);
  316. $v->rule('required', ['item_name', 'type', 'url']);
  317. # $v->rule('noSpecialChars', 'item_name');
  318. $v->rule('navigation', 'item_name');
  319. $v->rule('lengthBetween', 'item_name', 1, 40);
  320. $v->rule('in', 'type', ['file', 'folder']);
  321. if($v->validate())
  322. {
  323. return true;
  324. }
  325. else
  326. {
  327. return $v->errors();
  328. }
  329. }
  330. /**
  331. * validation for dynamic fields ( settings for themes and plugins)
  332. *
  333. * @param string $fieldName with the name of the field.
  334. * @param array or string $fieldValue with the values of the field.
  335. * @param string $objectName with the name of the plugin or theme.
  336. * @param array $fieldDefinitions with the field definitions as multi-dimensional array.
  337. * @return obj $v the validation object passed to a result method.
  338. */
  339. public function objectField($fieldName, $fieldValue, $objectName, $fieldDefinitions, $skiprequired = NULL)
  340. {
  341. $v = new Validator(array($fieldName => $fieldValue));
  342. if(isset($fieldDefinitions['required']) && !$skiprequired)
  343. {
  344. $v->rule('required', $fieldName);
  345. }
  346. if(isset($fieldDefinitions['maxlength']))
  347. {
  348. $v->rule('lengthMax', $fieldName, $fieldDefinitions['maxlength']);
  349. }
  350. if(isset($fieldDefinitions['max']))
  351. {
  352. $v->rule('max', $fieldName, $fieldDefinitions['max']);
  353. }
  354. if(isset($fieldDefinitions['min']))
  355. {
  356. $v->rule('min', $fieldName, $fieldDefinitions['min']);
  357. }
  358. if(isset($fieldDefinitions['pattern']))
  359. {
  360. $v->rule('regex', $fieldName, '/^' . $fieldDefinitions['pattern'] . '$/');
  361. }
  362. switch($fieldDefinitions['type'])
  363. {
  364. case "select":
  365. /* create array with option keys as value */
  366. $options = array();
  367. foreach($fieldDefinitions['options'] as $key => $value){ $options[] = $key; }
  368. $v->rule('in', $fieldName, $options);
  369. break;
  370. case "radio":
  371. $v->rule('in', $fieldName, $fieldDefinitions['options']);
  372. break;
  373. case "checkboxlist":
  374. if(isset($fieldValue) && is_array($fieldValue))
  375. {
  376. /* create array with option keys as value */
  377. $options = array();
  378. foreach($fieldDefinitions['options'] as $key => $value){ $options[] = $key; }
  379. /* loop over input values and check, if the options of the field definitions (options for checkboxlist) contains the key (input from user, key is used as value, value is used as label) */
  380. foreach($fieldValue as $key => $value)
  381. {
  382. $v->rule('in', $key, $options);
  383. }
  384. }
  385. break;
  386. case "color":
  387. $v->rule('regex', $fieldName, '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/');
  388. break;
  389. case "email":
  390. $v->rule('email', $fieldName);
  391. break;
  392. case "date":
  393. $v->rule('date', $fieldName);
  394. break;
  395. case "checkbox":
  396. if(isset($fieldDefinitions['required']))
  397. {
  398. $v->rule('accepted', $fieldName);
  399. }
  400. break;
  401. case "url":
  402. $v->rule('url', $fieldName);
  403. $v->rule('lengthMax', $fieldName, 200);
  404. break;
  405. case "text":
  406. $v->rule('noHTML', $fieldName);
  407. $v->rule('lengthMax', $fieldName, 500);
  408. # $v->rule('regex', $fieldName, '/^[\pL0-9_ \-\.\?\!\/\:]*$/u');
  409. break;
  410. case "textarea":
  411. # it understands array, json, yaml
  412. if(is_array($fieldValue))
  413. {
  414. $v = $this->checkArray($fieldValue, $v);
  415. }
  416. else
  417. {
  418. $v->rule('noHTML', $fieldName);
  419. $v->rule('lengthMax', $fieldName, 1000);
  420. }
  421. break;
  422. case "paragraph":
  423. $v->rule('noHTML', $fieldName);
  424. $v->rule('lengthMax', $fieldName, 1000);
  425. break;
  426. case "password":
  427. $v->rule('lengthMax', $fieldName, 100);
  428. break;
  429. case "image":
  430. $v->rule('noHTML', $fieldName);
  431. $v->rule('lengthMax', $fieldName, 1000);
  432. $v->rule('image_types', $fieldName);
  433. break;
  434. case "customfields":
  435. $v->rule('array', $fieldName);
  436. $v->rule('customfields', $fieldName);
  437. break;
  438. default:
  439. $v->rule('lengthMax', $fieldName, 1000);
  440. $v->rule('regex', $fieldName, '/^[\pL0-9_ \-]*$/u');
  441. }
  442. return $this->validationResult($v, $objectName);
  443. }
  444. /**
  445. * result for validation
  446. *
  447. * @param obj $v the validation object.
  448. * @return bool
  449. */
  450. public function checkArray($arrayvalues, $v)
  451. {
  452. foreach($arrayvalues as $key => $value)
  453. {
  454. if(is_array($value))
  455. {
  456. $this->checkArray($value, $v);
  457. }
  458. $v->rule('noHTML', $value);
  459. $v->rule('lengthMax', $value, 1000);
  460. }
  461. return $v;
  462. }
  463. public function validationResult($v, $name = false)
  464. {
  465. if($v->validate())
  466. {
  467. return true;
  468. }
  469. else
  470. {
  471. if($name == 'meta')
  472. {
  473. return $v->errors();
  474. }
  475. elseif($name)
  476. {
  477. if(isset($_SESSION['errors'][$name]))
  478. {
  479. foreach ($v->errors() as $key => $val)
  480. {
  481. $_SESSION['errors'][$name][$key] = $val;
  482. break;
  483. }
  484. }
  485. else
  486. {
  487. $_SESSION['errors'][$name] = $v->errors();
  488. }
  489. }
  490. else
  491. {
  492. $_SESSION['errors'] = $v->errors();
  493. }
  494. return false;
  495. }
  496. }
  497. }