Token.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <?php
  2. App::uses('ToolsAppModel', 'Tools.Model');
  3. App::uses('CommonComponent', 'Tools.Controller/Component');
  4. /**
  5. * A generic model to hold tokens
  6. *
  7. * @author Mark Scherer
  8. * @cakephp 2.x
  9. * @license MIT
  10. * 2011-11-17 ms
  11. */
  12. class Token extends ToolsAppModel {
  13. public $displayField = 'key';
  14. public $order = array('Token.created' => 'DESC');
  15. protected $defaultLength = 22;
  16. protected $validity = MONTH;
  17. public $validate = array(
  18. 'type' => array(
  19. 'notEmpty' => array(
  20. 'rule' => array('notEmpty'),
  21. 'message' => 'valErrMandatoryField',
  22. ),
  23. ),
  24. 'key' => array(
  25. 'notEmpty' => array(
  26. 'rule' => array('notEmpty'),
  27. 'message' => 'valErrMandatoryField',
  28. 'last' => true,
  29. ),
  30. 'isUnique' => array(
  31. 'rule' => array('isUnique'),
  32. 'message' => 'valErrTokenExists',
  33. ),
  34. ),
  35. 'content' => array(
  36. 'maxLength' => array(
  37. 'rule' => array('maxLength', 255),
  38. 'message' => array('valErrMaxCharacters %s', 255),
  39. 'allowEmpty' => true
  40. ),
  41. ),
  42. 'used' => array('numeric')
  43. );
  44. /**
  45. * stores new key in DB
  46. *
  47. * @param string type: necessary
  48. * @param string key: optional key, otherwise a key will be generated
  49. * @param mixed user_id: optional (if used, only this user can use this key)
  50. * @param string content: up to 255 characters of content may be added (optional)
  51. * NOW: checks if this key is already used (should be unique in table)
  52. * @return string key on SUCCESS, boolean false otherwise
  53. * 2009-05-13 ms
  54. */
  55. public function newKey($type, $key = null, $uid = null, $content = null) {
  56. if (empty($type)) {
  57. return false;
  58. }
  59. if (empty($key)) {
  60. $key = $this->generateKey($this->defaultLength);
  61. $keyLength = $this->defaultLength;
  62. } else {
  63. $keyLength = mb_strlen($key);
  64. }
  65. $data = array(
  66. 'type' => $type,
  67. 'user_id' => (string)$uid,
  68. 'content' => (string)$content,
  69. 'key' => $key,
  70. );
  71. $this->set($data);
  72. $max = 99;
  73. while (!$this->validates()) {
  74. $data['key'] = $this->generateKey($keyLength);
  75. $this->set($data);
  76. $max--;
  77. if ($max === 0) {
  78. return false;
  79. }
  80. }
  81. $this->create();
  82. if ($this->save($data)) {
  83. return $key;
  84. }
  85. return false;
  86. }
  87. /**
  88. * usesKey (only once!) - by KEY
  89. *
  90. * @param string type: necessary
  91. * @param string key: necessary
  92. * @param mixed user_id: needs to be provided if this key has a user_id stored
  93. * @return array Content - if successfully used or if already used (used=1), FALSE else
  94. * 2009-05-13 ms
  95. */
  96. public function useKey($type, $key, $uid = null, $treatUsedAsInvalid = false) {
  97. if (empty($type) || empty($key)) {
  98. return false;
  99. }
  100. $options = array('conditions' => array($this->alias.'.key' => $key, $this->alias.'.type' => $type));
  101. if (!empty($uid)) {
  102. $options['conditions'][$this->alias.'.user_id'] = $uid;
  103. }
  104. $res = $this->find('first', $options);
  105. if (empty($res)) {
  106. return false;
  107. }
  108. if (!empty($uid) && !empty($res[$this->alias]['user_id']) && $res[$this->alias]['user_id'] != $uid) {
  109. // return $res; # more secure to fail here if user_id is not provided, but was submitted prev.
  110. return false;
  111. }
  112. # already used?
  113. if (!empty($res[$this->alias]['used'])) {
  114. if ($treatUsedAsInvalid) {
  115. return false;
  116. }
  117. # return true and let the application check what to do then
  118. return $res;
  119. }
  120. # actually spend key (set to used)
  121. if ($this->spendKey($res[$this->alias]['id'])) {
  122. return $res;
  123. }
  124. # no limit? we dont spend key then
  125. if (!empty($res[$this->alias]['unlimited'])) {
  126. return $res;
  127. }
  128. $this->log('VIOLATION in '.$this->alias.' Model (method useKey)');
  129. return false;
  130. }
  131. /**
  132. * sets Key to "used" (only once!) - directly by ID
  133. *
  134. * @param id of key to spend: necessary
  135. * @return boolean true on success, false otherwise
  136. * 2009-05-13 ms
  137. */
  138. public function spendKey($id = null) {
  139. if (empty($id)) {
  140. return false;
  141. }
  142. //$this->id = $id;
  143. if ($this->updateAll(array($this->alias.'.used' => $this->alias.'.used + 1', $this->alias.'.modified'=>'"'.date(FORMAT_DB_DATETIME).'"'), array($this->alias.'.id'=>$id))) {
  144. return true;
  145. }
  146. return false;
  147. }
  148. /**
  149. * remove old/invalid keys
  150. * does not remove recently used ones (for proper feedback)!
  151. *
  152. * @return boolean success
  153. * 2010-06-17 ms
  154. */
  155. public function garbigeCollector() {
  156. $conditions = array(
  157. $this->alias.'.created <'=>date(FORMAT_DB_DATETIME, time()-$this->validity),
  158. );
  159. return $this->deleteAll($conditions, false);
  160. }
  161. /**
  162. * get admin stats
  163. * 2010-10-22 ms
  164. */
  165. public function stats() {
  166. $keys = array();
  167. $keys['unused_valid'] = $this->find('count', array('conditions'=>array($this->alias.'.used'=>0, $this->alias.'.created >='=>date(FORMAT_DB_DATETIME, time()-$this->validity))));
  168. $keys['used_valid'] = $this->find('count', array('conditions'=>array($this->alias.'.used'=>1, $this->alias.'.created >='=>date(FORMAT_DB_DATETIME, time()-$this->validity))));
  169. $keys['unused_invalid'] = $this->find('count', array('conditions'=>array($this->alias.'.used'=>0, $this->alias.'.created <'=>date(FORMAT_DB_DATETIME, time()-$this->validity))));
  170. $keys['used_invalid'] = $this->find('count', array('conditions'=>array($this->alias.'.used'=>1, $this->alias.'.created <'=>date(FORMAT_DB_DATETIME, time()-$this->validity))));
  171. $types = $this->find('all', array('conditions'=>array(), 'fields'=>array('DISTINCT type')));
  172. $keys['types'] = !empty($types) ? Set::extract('/'.$this->alias.'/type', $types) : array();
  173. return $keys;
  174. }
  175. /**
  176. * Generator
  177. *
  178. * @param length (defaults to defaultLength)
  179. * @return string Key
  180. * 2009-05-13 ms
  181. */
  182. public function generateKey($length = null) {
  183. if (empty($length)) {
  184. $length = $this->defaultLength;
  185. }
  186. App::uses('RandomLib', 'Tools.Lib');
  187. return RandomLib::generatePassword($length);
  188. }
  189. }