PageRenderTime 23ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/library/core/class.authenticator.php

https://github.com/dkobia/Garden
PHP | 312 lines | 208 code | 61 blank | 43 comment | 33 complexity | 8f99c64ae5b4b88be446e194e21d5a11 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. * An abstract template for authenticator classes.
  12. *
  13. * @author Tim Gunter
  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. /**
  20. * An abstract template for authenticator classes.
  21. *
  22. * @package Garden
  23. */
  24. abstract class Gdn_Authenticator extends Gdn_Pluggable {
  25. const DATA_NONE = 'data none';
  26. const DATA_FORM = 'data form';
  27. const DATA_REQUEST = 'data request';
  28. const DATA_COOKIE = 'data cookie';
  29. const MODE_REPEAT = 'already logged in';
  30. const MODE_GATHER = 'gather';
  31. const MODE_VALIDATE = 'validate';
  32. const MODE_NOAUTH = 'no foreign identity';
  33. const AUTH_DENIED = 0;
  34. const AUTH_PERMISSION = -1;
  35. const AUTH_INSUFFICIENT = -2;
  36. const AUTH_PARTIAL = -3;
  37. const AUTH_SUCCESS = -4;
  38. const AUTH_ABORTED = -5;
  39. const REACT_RENDER = 0;
  40. const REACT_EXIT = 1;
  41. const REACT_REDIRECT = 2;
  42. const REACT_REMOTE = 3;
  43. const URL_REGISTER = 'RegisterUrl';
  44. const URL_SIGNIN = 'SignInUrl';
  45. const URL_SIGNOUT = 'SignOutUrl';
  46. const KEY_TYPE_TOKEN = 'token';
  47. const KEY_TYPE_PROVIDER = 'provider';
  48. /**
  49. * Alias of the authentication scheme to use, e.g. "password" or "openid"
  50. *
  51. */
  52. protected $_AuthenticationSchemeAlias = NULL;
  53. /**
  54. * Contains authenticator configuration information, such as a preshared key or
  55. * discovery URL.
  56. *
  57. */
  58. protected $_AuthenticationProviderModel = NULL;
  59. protected $_AuthenticationProviderData = NULL;
  60. protected $_DataSourceType = self::DATA_FORM;
  61. protected $_DataSource = NULL;
  62. public $_DataHooks = array();
  63. /**
  64. * Returns the unique id assigned to the user in the database, 0 if the
  65. * username/password combination weren't found, or -1 if the user does not
  66. * have permission to sign in.
  67. */
  68. abstract public function Authenticate();
  69. abstract public function CurrentStep();
  70. abstract public function DeAuthenticate();
  71. abstract public function WakeUp();
  72. // What to do if entry/auth/* is called while the user is logged out. Should normally be REACT_RENDER
  73. abstract public function LoginResponse();
  74. // What to do after part 1 of a 2 part authentication process. This is used in conjunction with OAauth/OpenID type authentication schemes
  75. abstract public function PartialResponse();
  76. // What to do after authentication has succeeded.
  77. abstract public function SuccessResponse();
  78. // What to do if the entry/auth/* page is triggered for a user that is already logged in
  79. abstract public function RepeatResponse();
  80. // Get one of the three Forwarding URLs (Registration, SignIn, SignOut)
  81. abstract public function GetURL($URLType);
  82. public function __construct() {
  83. // Figure out what the authenticator alias is
  84. $this->_AuthenticationSchemeAlias = $this->GetAuthenticationSchemeAlias();
  85. // Initialize gdn_pluggable
  86. parent::__construct();
  87. }
  88. public function DataSourceType() {
  89. return $this->_DataSourceType;
  90. }
  91. public function FetchData($DataSource, $DirectSupplied = array()) {
  92. $this->_DataSource = $DataSource;
  93. if ($DataSource == $this) {
  94. foreach ($this->_DataHooks as $DataTarget => $DataHook)
  95. $this->_DataHooks[$DataTarget]['value'] = ArrayValue($DataTarget, $DirectSupplied);
  96. return;
  97. }
  98. if (sizeof($this->_DataHooks)) {
  99. foreach ($this->_DataHooks as $DataTarget => $DataHook) {
  100. switch ($this->_DataSourceType) {
  101. case self::DATA_REQUEST:
  102. case self::DATA_FORM:
  103. $this->_DataHooks[$DataTarget]['value'] = $this->_DataSource->GetValue($DataHook['lookup'], FALSE);
  104. break;
  105. case self::DATA_COOKIE:
  106. $this->_DataHooks[$DataTarget]['value'] = $this->_DataSource->GetValueFrom(Gdn_Authenticator::INPUT_COOKIES, $DataHook['lookup'], FALSE);
  107. break;
  108. }
  109. }
  110. }
  111. }
  112. public function HookDataField($InternalFieldName, $DataFieldName, $DataFieldRequired = TRUE) {
  113. $this->_DataHooks[$InternalFieldName] = array('lookup' => $DataFieldName, 'required' => $DataFieldRequired);
  114. }
  115. public function GetValue($Key, $Default = FALSE) {
  116. if (array_key_exists($Key, $this->_DataHooks) && array_key_exists('value', $this->_DataHooks[$Key]))
  117. return $this->_DataHooks[$Key]['value'];
  118. return $Default;
  119. }
  120. protected function _CheckHookedFields() {
  121. foreach ($this->_DataHooks as $DataKey => $DataValue) {
  122. if ($DataValue['required'] == TRUE && (!array_key_exists('value', $DataValue) || $DataValue['value'] == NULL)) return Gdn_Authenticator::MODE_GATHER;
  123. }
  124. return Gdn_Authenticator::MODE_VALIDATE;
  125. }
  126. public function LoadProvider($AuthenticationProviderLookupKey) {
  127. $this->_AuthenticationProviderModel = new Gdn_AuthenticationProviderModel();
  128. $AuthenticatorData = $this->_AuthenticationProviderModel->GetProviderByKey($AuthenticationProviderLookupKey);
  129. if ($AuthenticatorData) {
  130. $this->_AuthenticationProviderData = $AuthenticatorData;
  131. }
  132. else {
  133. throw new Exception("Tried to load bogus authentication provider via lookup key'{$AuthenticationProviderLookupKey}'. No information stored for this key.");
  134. }
  135. }
  136. public function CreateToken($TokenType, $ProviderKey, $UserKey = NULL, $Authorized = FALSE) {
  137. $TokenKey = implode('.', array('token',$ProviderKey,time(),mt_rand(0,100000)));
  138. $TokenSecret = sha1(md5(implode('.',array($TokenKey,mt_rand(0,100000)))));
  139. $Timestamp = time();
  140. $InsertArray = array(
  141. 'Token' => $TokenKey,
  142. 'TokenSecret' => $TokenSecret,
  143. 'TokenType' => $TokenType,
  144. 'ProviderKey' => $ProviderKey,
  145. 'Lifetime' => Gdn::Config('Garden.Authenticators.handshake.TokenLifetime', 60),
  146. 'Timestamp' => date('Y-m-d H:i:s',$Timestamp),
  147. 'Authorized' => $Authorized
  148. );
  149. if ($UserKey !== NULL)
  150. $InsertArray['ForeignUserKey'] = $UserKey;
  151. try {
  152. Gdn::Database()->SQL()->Insert('UserAuthenticationToken', $InsertArray);
  153. if ($TokenType == 'access' && !is_null($UserKey))
  154. $this->DeleteToken($ProviderKey, $UserKey, 'request');
  155. } catch(Exception $e) {
  156. return FALSE;
  157. }
  158. return $InsertArray;
  159. }
  160. public function LookupToken($ProviderKey, $UserKey, $TokenType = NULL) {
  161. $TokenData = Gdn::Database()->SQL()
  162. ->Select('uat.*')
  163. ->From('UserAuthenticationToken uat')
  164. ->Where('uat.ForeignUserKey', $UserKey)
  165. ->Where('uat.ProviderKey', $ProviderKey)
  166. ->BeginWhereGroup()
  167. ->Where('(uat.Timestamp + uat.Lifetime) >=', 'NOW()')
  168. ->OrWHere('uat.Lifetime', 0)
  169. ->EndWhereGroup()
  170. ->Get()
  171. ->FirstRow(DATASET_TYPE_ARRAY);
  172. if ($TokenData && (is_null($TokenType) || strtolower($TokenType) == strtolower($TokenData['TokenType'])))
  173. return $TokenData;
  174. return FALSE;
  175. }
  176. public function DeleteToken($ProviderKey, $UserKey, $TokenType) {
  177. Gdn::Database()->SQL()
  178. ->From('UserAuthenticationToken')
  179. ->Where('ProviderKey', $ProviderKey)
  180. ->Where('ForeignUserKey', $UserKey)
  181. ->Where('TokenType', $TokenType)
  182. ->Delete();
  183. }
  184. public function SetNonce($TokenKey, $Nonce, $Timestamp = NULL) {
  185. $InsertArray = array(
  186. 'Token' => $TokenKey,
  187. 'Nonce' => $Nonce,
  188. 'Timestamp' => date('Y-m-d H:i:s',(is_null($Timestamp)) ? time() : $Timestamp)
  189. );
  190. try {
  191. $NumAffected = Gdn::Database()->SQL()->Update('UserAuthenticationNonce')
  192. ->Set('Nonce', $Nonce)
  193. ->Set('Timestamp', $InsertArray['Timestamp'])
  194. ->Where('Token', $InsertArray['Token'])
  195. ->Put();
  196. if (!$NumAffected->PDOStatement() || !$NumAffected->PDOStatement()->rowCount())
  197. throw new Exception();
  198. } catch (Exception $e) {
  199. Gdn::Database()->SQL()->Insert('UserAuthenticationNonce', $InsertArray);
  200. }
  201. return TRUE;
  202. }
  203. public function LookupNonce($TokenKey, $Nonce = NULL) {
  204. $NonceData = Gdn::Database()->SQL()->Select('uan.*')
  205. ->From('UserAuthenticationNonce uan')
  206. ->Where('uan.Token', $TokenKey)
  207. ->Get()
  208. ->FirstRow(DATASET_TYPE_ARRAY);
  209. if ($NonceData && (is_null($Nonce) || $NonceData['Nonce'] == $Nonce))
  210. return $NonceData['Nonce'];
  211. return FALSE;
  212. }
  213. public function ClearNonces($TokenKey) {
  214. Gdn::SQL()->Delete('UserAuthenticationNonce', array(
  215. 'Token' => $TokenKey
  216. ));
  217. }
  218. public function RequireLogoutTransientKey() {
  219. return TRUE;
  220. }
  221. public function GetAuthenticationSchemeAlias() {
  222. $StipSuffix = str_replace('Gdn_','',__CLASS__);
  223. $ClassName = str_replace('Gdn_','',get_class($this));
  224. $ClassName = substr($ClassName,-strlen($StipSuffix)) == $StipSuffix ? substr($ClassName,0,-strlen($StipSuffix)) : $ClassName;
  225. return strtolower($ClassName);
  226. }
  227. public function GetProviderValue($Key, $Default = FALSE) {
  228. if (array_key_exists($Key, $this->_AuthenticationProviderData))
  229. return $this->_AuthenticationProviderData[$Key];
  230. return $Default;
  231. }
  232. public function SetIdentity($UserID, $Persist = TRUE) {
  233. $AuthenticationSchemeAlias = $this->GetAuthenticationSchemeAlias();
  234. Gdn::Authenticator()->SetIdentity($UserID, $Persist);
  235. Gdn::Session()->Start();
  236. if ($UserID > 0) {
  237. Gdn::Session()->SetPreference('Authenticator', $AuthenticationSchemeAlias);
  238. } else {
  239. Gdn::Session()->SetPreference('Authenticator', '');
  240. }
  241. }
  242. public function GetProviderKey() {
  243. return $this->GetProviderValue('AuthenticationKey');
  244. }
  245. public function GetProviderSecret() {
  246. return $this->GetProviderValue('AssociationSecret');
  247. }
  248. public function GetProviderUrl() {
  249. return $this->GetProviderValue('URL');
  250. }
  251. }