PageRenderTime 43ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/library/core/class.cookieidentity.php

https://github.com/Emaratilicious/Garden
PHP | 299 lines | 179 code | 62 blank | 58 comment | 36 complexity | f8728e579c7f175db80cd3408d576b2f MD5 | raw file
  1. <?php if (!defined('APPLICATION')) exit();
  2. /*
  3. Copyright 2008, 2009 Vanilla Forums Inc.
  4. This file is part of Garden.
  5. Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
  6. Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  7. You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
  8. Contact Vanilla Forums Inc. at support [at] vanillaforums [dot] com
  9. */
  10. /**
  11. * Validating, Setting, and Retrieving session data in cookies.
  12. * @author Mark O'Sullivan, Todd Burry
  13. * @copyright 2009 Mark O'Sullivan
  14. * @license http://www.opensource.org/licenses/gpl-2.0.php GPL
  15. * @package Garden
  16. * @version @@GARDEN-VERSION@@
  17. * @namespace Garden.Core
  18. */
  19. class Gdn_CookieIdentity {
  20. public $UserID = NULL;
  21. public $CookieName;
  22. public $CookiePath;
  23. public $CookieDomain;
  24. public $VolatileMarker;
  25. public $CookieHashMethod;
  26. public $CookieSalt;
  27. public function __construct($Config = NULL) {
  28. $this->Init($Config);
  29. }
  30. public function Init($Config = NULL) {
  31. if (is_null($Config))
  32. $Config = Gdn::Config('Garden.Cookie');
  33. elseif(is_string($Config))
  34. $Config = Gdn::Config($Config);
  35. $DefaultConfig = Gdn::Config('Garden.Cookie');
  36. $this->CookieName = ArrayValue('Name', $Config, $DefaultConfig['Name']);
  37. $this->CookiePath = ArrayValue('Path', $Config, $DefaultConfig['Path']);
  38. $this->CookieDomain = ArrayValue('Domain', $Config, $DefaultConfig['Domain']);
  39. $this->CookieHashMethod = ArrayValue('HashMethod', $Config, $DefaultConfig['HashMethod']);
  40. $this->CookieSalt = ArrayValue('Salt', $Config, $DefaultConfig['Salt']);
  41. $this->VolatileMarker = $this->CookieName.'-Volatile';
  42. }
  43. /**
  44. * Destroys the user's session cookie - essentially de-authenticating them.
  45. */
  46. protected function _ClearIdentity() {
  47. // Destroy the cookie.
  48. $this->UserID = 0;
  49. $this->_DeleteCookie($this->CookieName);
  50. }
  51. /**
  52. * Returns the unique id assigned to the user in the database (retrieved
  53. * from the session cookie if the cookie authenticates) or FALSE if not
  54. * found or authentication fails.
  55. *
  56. * @return int
  57. */
  58. public function GetIdentity() {
  59. if (!is_null($this->UserID))
  60. return $this->UserID;
  61. if (!$this->_CheckCookie($this->CookieName)) {
  62. $this->_ClearIdentity();
  63. return 0;
  64. }
  65. list($UserID, $Expiration) = $this->GetCookiePayload($this->CookieName);
  66. if (!is_numeric($UserID) || $UserID < -2) // allow for handshake special id
  67. return 0;
  68. return $this->UserID = $UserID;
  69. }
  70. public function HasVolatileMarker($CheckUserID) {
  71. $HasMarker = $this->CheckVolatileMarker($CheckUserID);
  72. if (!$HasMarker)
  73. $this->SetVolatileMarker($CheckUserID);
  74. return $HasMarker;
  75. }
  76. public function CheckVolatileMarker($CheckUserID) {
  77. if (!$this->_CheckCookie($this->VolatileMarker)) return FALSE;
  78. list($UserID, $Expiration) = $this->GetCookiePayload($this->CookieName);
  79. if ($UserID != $CheckUserID)
  80. return FALSE;
  81. return TRUE;
  82. }
  83. /**
  84. * Returns $this->_HashHMAC with the provided data, the default hashing method
  85. * (md5), and the server's COOKIE.SALT string as the key.
  86. *
  87. * @param string $Data The data to place in the hash.
  88. */
  89. protected static function _Hash($Data, $CookieHashMethod, $CookieSalt) {
  90. return Gdn_CookieIdentity::_HashHMAC($CookieHashMethod, $Data, $CookieSalt);
  91. }
  92. /**
  93. * Returns the provided data hashed with the specified method using the
  94. * specified key.
  95. *
  96. * @param string $HashMethod The hashing method to use on $Data. Options are MD5 or SHA1.
  97. * @param string $Data The data to place in the hash.
  98. * @param string $Key The key to use when hashing the data.
  99. */
  100. protected static function _HashHMAC($HashMethod, $Data, $Key) {
  101. $PackFormats = array('md5' => 'H32', 'sha1' => 'H40');
  102. if (!isset($PackFormats[$HashMethod]))
  103. return false;
  104. $PackFormat = $PackFormats[$HashMethod];
  105. // this is the equivalent of "strlen($Key) > 64":
  106. if (isset($Key[63]))
  107. $Key = pack($PackFormat, $HashMethod($Key));
  108. else
  109. $Key = str_pad($Key, 64, chr(0));
  110. $InnerPad = (substr($Key, 0, 64) ^ str_repeat(chr(0x36), 64));
  111. $OuterPad = (substr($Key, 0, 64) ^ str_repeat(chr(0x5C), 64));
  112. return $HashMethod($OuterPad . pack($PackFormat, $HashMethod($InnerPad . $Data)));
  113. }
  114. /**
  115. * Generates the user's session cookie.
  116. *
  117. * @param int $UserID The unique id assigned to the user in the database.
  118. * @param boolean $Persist Should the user's session remain persistent across visits?
  119. */
  120. public function SetIdentity($UserID, $Persist = FALSE) {
  121. if(is_null($UserID)) {
  122. $this->_ClearIdentity();
  123. return;
  124. }
  125. $this->UserID = $UserID;
  126. if ($Persist !== FALSE) {
  127. // Note: 2592000 is 60*60*24*30 or 30 days
  128. $Expiration = $Expire = time() + 2592000;
  129. } else {
  130. // Note: 172800 is 60*60*24*2 or 2 days
  131. $Expiration = time() + 172800;
  132. // Note: setting $Expire to 0 will cause the cookie to die when the browser closes.
  133. $Expire = 0;
  134. }
  135. // Create the cookie.
  136. $KeyData = $UserID.'-'.$Expiration;
  137. $this->_SetCookie($this->CookieName, $KeyData, array($UserID, $Expiration), $Expire);
  138. $this->SetVolatileMarker($UserID);
  139. }
  140. public function SetVolatileMarker($UserID) {
  141. if (is_null($UserID))
  142. return;
  143. // Note: 172800 is 60*60*24*2 or 2 days
  144. $Expiration = time() + 172800;
  145. // Note: setting $Expire to 0 will cause the cookie to die when the browser closes.
  146. $Expire = 0;
  147. $KeyData = $UserID.'-'.$Expiration;
  148. $this->_SetCookie($this->VolatileMarker, $KeyData, array($UserID, $Expiration), $Expire);
  149. }
  150. protected function _SetCookie($CookieName, $KeyData, $CookieContents, $Expire) {
  151. self::SetCookie($CookieName, $KeyData, $CookieContents, $Expire, $this->CookiePath, $this->CookieDomain, $this->CookieHashMethod, $this->CookieSalt);
  152. }
  153. public static function SetCookie($CookieName, $KeyData, $CookieContents, $Expire, $Path = NULL, $Domain = NULL, $CookieHashMethod = NULL, $CookieSalt = NULL) {
  154. if (is_null($Path))
  155. $Path = Gdn::Config('Garden.Cookie.Path', '/');
  156. if (is_null($Domain))
  157. $Domain = Gdn::Config('Garden.Cookie.Domain', '');
  158. // If the domain being set is completely incompatible with the current domain then make the domain work.
  159. $CurrentHost = Gdn::Request()->Host();
  160. if (!StringEndsWith($CurrentHost, trim($Domain, '.')))
  161. $Domain = '';
  162. if (!$CookieHashMethod)
  163. $CookieHashMethod = Gdn::Config('Garden.Cookie.HashMethod');
  164. if (!$CookieSalt)
  165. $CookieSalt = Gdn::Config('Garden.Cookie.Salt');
  166. // Create the cookie contents
  167. $Key = self::_Hash($KeyData, $CookieHashMethod, $CookieSalt);
  168. $Hash = self::_HashHMAC($CookieHashMethod, $KeyData, $Key);
  169. $Cookie = array($KeyData,$Hash,time());
  170. if (!is_null($CookieContents)) {
  171. if (!is_array($CookieContents)) $CookieContents = array($CookieContents);
  172. $Cookie = array_merge($Cookie, $CookieContents);
  173. }
  174. $CookieContents = implode('|',$Cookie);
  175. // Create the cookie.
  176. setcookie($CookieName, $CookieContents, $Expire, $Path, $Domain);
  177. $_COOKIE[$CookieName] = $CookieContents;
  178. }
  179. protected function _CheckCookie($CookieName) {
  180. $CookieStatus = self::CheckCookie($CookieName, $this->CookieHashMethod, $this->CookieSalt);
  181. if ($CookieStatus === FALSE)
  182. $this->_DeleteCookie($CookieName);
  183. return $CookieStatus;
  184. }
  185. public static function CheckCookie($CookieName, $CookieHashMethod = NULL, $CookieSalt = NULL) {
  186. if (empty($_COOKIE[$CookieName])) {
  187. return FALSE;
  188. }
  189. if (is_null($CookieHashMethod))
  190. $CookieHashMethod = Gdn::Config('Garden.Cookie.HashMethod');
  191. if (is_null($CookieSalt))
  192. $CookieSalt = Gdn::Config('Garden.Cookie.Salt');
  193. $CookieData = explode('|', $_COOKIE[$CookieName]);
  194. if (count($CookieData) < 5) {
  195. self::DeleteCookie($CookieName);
  196. return FALSE;
  197. }
  198. list($HashKey, $CookieHash, $Time, $UserID, $Expiration) = $CookieData;
  199. if ($Expiration < time() && $Expiration != 0) {
  200. self::DeleteCookie($CookieName);
  201. return FALSE;
  202. }
  203. $Key = self::_Hash($HashKey, $CookieHashMethod, $CookieSalt);
  204. $GeneratedHash = self::_HashHMAC($CookieHashMethod, $HashKey, $Key);
  205. if (!CompareHashDigest($CookieHash, $GeneratedHash)) {
  206. self::DeleteCookie($CookieName);
  207. return FALSE;
  208. }
  209. return TRUE;
  210. }
  211. public static function GetCookiePayload($CookieName, $CookieHashMethod = NULL, $CookieSalt = NULL) {
  212. if (!self::CheckCookie($CookieName)) return FALSE;
  213. $Payload = explode('|', $_COOKIE[$CookieName]);
  214. $Key = explode('-', $Payload[0]);
  215. $Expiration = array_pop($Key);
  216. $UserID = implode('-', $Key);
  217. $Payload = array_slice($Payload, 4);
  218. $Payload = array_merge(array($UserID, $Expiration), $Payload);
  219. return $Payload;
  220. }
  221. protected function _DeleteCookie($CookieName) {
  222. unset($_COOKIE[$CookieName]);
  223. self::DeleteCookie($CookieName, $this->CookiePath, $this->CookieDomain);
  224. }
  225. public static function DeleteCookie($CookieName, $Path = NULL, $Domain = NULL) {
  226. if (is_null($Path))
  227. $Path = Gdn::Config('Garden.Cookie.Path');
  228. if (is_null($Domain))
  229. $Domain = Gdn::Config('Garden.Cookie.Domain');
  230. $CurrentHost = Gdn::Request()->Host();
  231. if (!StringEndsWith($CurrentHost, trim($Domain, '.')))
  232. $Domain = '';
  233. $Expiry = time() - 60 * 60;
  234. setcookie($CookieName, "", $Expiry, $Path, $Domain);
  235. $_COOKIE[$CookieName] = NULL;
  236. }
  237. }